Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[hannk] Add support for building/running for wasm #6361

Merged
merged 5 commits into from
Nov 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16...3.20)
project(Halide
VERSION 13.0.0
VERSION 14.0.0
DESCRIPTION "Halide compiler and libraries"
HOMEPAGE_URL "https://halide-lang.org")

Expand Down
1 change: 1 addition & 0 deletions apps/hannk/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
build*/
bin/
emsdk
70 changes: 57 additions & 13 deletions apps/hannk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)

enable_testing()

# ----------------------------

option(HANNK_AOT_HOST_ONLY "Only build AOT host tools for cross-compiling" OFF)

option(HANNK_BUILD_TFLITE "Build TFLite+Delegate for HANNK" ON)
if (HANNK_BUILD_TFLITE AND (Halide_TARGET MATCHES "wasm"))
message(FATAL_ERROR "HANNK_BUILD_TFLITE must be OFF when targeting wasm")
endif ()
message(STATUS "HANNK_BUILD_TFLITE is ${HANNK_BUILD_TFLITE}")

# -fPIC is necessary for .so builds (at least on Linux); not necessary for the non-delegate
Expand All @@ -18,35 +25,43 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)

# Find Halide
find_package(Halide REQUIRED)

# Set up the version of TFLite we expect
# (We need to do this even if HANNK_BUILD_TFLITE is off,
# so that the .tflite file parser can get the right schema)
set(TFLITE_VERSION_MAJOR "2" CACHE STRING "Major version of TFLite to assume")
set(TFLITE_VERSION_MINOR "6" CACHE STRING "Minor version of TFLite to assume")
set(TFLITE_VERSION_PATCH "0" CACHE STRING "Patch version of TFLite to assume")
set(TFLITE_VERSION "${TFLITE_VERSION_MAJOR}.${TFLITE_VERSION_MINOR}.${TFLITE_VERSION_PATCH}")

# ----------------------------

add_compile_definitions(TFLITE_VERSION_MAJOR=${TFLITE_VERSION_MAJOR})
add_compile_definitions(TFLITE_VERSION_MINOR=${TFLITE_VERSION_MINOR})
add_compile_definitions(TFLITE_VERSION_PATCH=${TFLITE_VERSION_PATCH})
if (HANNK_BUILD_TFLITE)
add_compile_definitions(HANNK_BUILD_TFLITE=1)
else ()
add_compile_definitions(HANNK_BUILD_TFLITE=0)
endif ()
add_compile_definitions(HANNK_BUILD_TFLITE=$<BOOL:${HANNK_BUILD_TFLITE}>)

set(TFLITE_VERSION "${TFLITE_VERSION_MAJOR}.${TFLITE_VERSION_MINOR}.${TFLITE_VERSION_PATCH}")
# ----------------------------

# Find HalideHelpers -- this is just the Runtime headers and CMake functions, but no libraries
find_package(HalideHelpers REQUIRED)

# ----------------------------

add_subdirectory(halide)
if (HANNK_AOT_HOST_ONLY)
# Don't add anything else to the build... everything for AOT is in the halide subdirectory
return()
endif ()

add_subdirectory(interpreter)
add_subdirectory(tflite)
add_subdirectory(util)
if (HANNK_BUILD_TFLITE)
add_subdirectory(delegate)
endif ()

# ----------------------------

# Benchmarking executable
add_executable(benchmark benchmark.cpp)
target_link_libraries(benchmark PRIVATE
Expand All @@ -66,12 +81,41 @@ target_link_libraries(compare_vs_tflite PRIVATE
target_include_directories(compare_vs_tflite
PUBLIC $<BUILD_INTERFACE:${hannk_SOURCE_DIR}>)

# TODO: Surely there's a better way to set Emscripten flags.
if (Halide_TARGET MATCHES "wasm")
foreach (t IN ITEMS benchmark compare_vs_tflite)
# Note: "SHELL:" prevents de-duplication of the -s flag.
target_link_options(
${t} PRIVATE
"SHELL:-s ALLOW_MEMORY_GROWTH=1"
"SHELL:-s ENVIRONMENT=node"
"SHELL:-s NODERAWFS"
"SHELL:$<$<CONFIG:Debug>:-s ASSERTIONS=1>"
)
endforeach ()
endif ()

if (Halide_TARGET MATCHES "wasm" AND NODE_JS_EXECUTABLE)
execute_process(COMMAND "${NODE_JS_EXECUTABLE}" --version
OUTPUT_VARIABLE NODE_JS_VERSION_RAW
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REPLACE "v" "" NODE_JS_VERSION ${NODE_JS_VERSION_RAW})

if (NODE_JS_VERSION VERSION_LESS "16.13")
message(FATAL_ERROR "Halide requires Node v16.13 or later, but found ${NODE_JS_VERSION_RAW} at ${NODE_JS_EXECUTABLE}. Please set NODE_JS_EXECUTABLE on the CMake command line.")
endif ()
endif ()

# Tests
file(GLOB TEST_FILES CONFIGURE_DEPENDS "test/*/*")
file(GLOB TEST_FILES CONFIGURE_DEPENDS "test/*/*.tflite")
foreach (t IN LISTS TEST_FILES)
file(RELATIVE_PATH test_name ${hannk_SOURCE_DIR} ${t})
add_test(NAME ${test_name} COMMAND compare_vs_tflite ${t} --benchmark 0)

# Emscripten sets CMAKE_CROSSCOMPILING_TOOLCHAIN to NODE_JS_EXECUTABLE,
# which ensures these tests will run in Node.
add_test(NAME ${test_name}
COMMAND compare_vs_tflite ${t} --benchmark 0)

set_tests_properties(${test_name} PROPERTIES
LABELS hannk_tests)
endforeach()

endforeach ()
6 changes: 3 additions & 3 deletions apps/hannk/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ build: \
$(BIN)/$(HL_TARGET)/compare_vs_tflite

test: compare_vs_tflite
$(foreach test_model, $(shell ls -1 test/*/*), $(BIN)/$(HL_TARGET)/compare_vs_tflite $(test_model) --benchmark 0;)
$(foreach test_model, $(shell ls -1 test/*/*.tflite), $(BIN)/$(HL_TARGET)/compare_vs_tflite $(test_model) --benchmark 0;)

test-hexagon-sim: $(BIN)/$(HL_TARGET)/$(BENCHMARK_OUT)
@mkdir -p $@
echo "Benchmarking tests..."
$(foreach test_model, $(shell ls -1 test/*/*), BIN=$(BIN) ./run_benchmark_on_hexagon_sim.sh $(test_model);)
$(foreach test_model, $(shell ls -1 test/*/*.tflite), BIN=$(BIN) ./run_benchmark_on_hexagon_sim.sh $(test_model);)

test-hexagon-device: $(BIN)/$(HL_TARGET)/$(BENCHMARK_OUT)
echo "Benchmarking tests..."
Expand All @@ -51,7 +51,7 @@ test-hexagon-device: $(BIN)/$(HL_TARGET)/$(BENCHMARK_OUT)
adb push test/. /vendor/bin/hannk-test
adb push $(HEXAGON_SDK_ROOT)/libs/run_main_on_hexagon/ship/android_aarch64/run_main_on_hexagon /vendor/bin/run_main_on_hexagon
adb push $(HEXAGON_SDK_ROOT)/libs/run_main_on_hexagon/ship/hexagon_toolv84_v65/librun_main_on_hexagon_skel.so /vendor/lib/rfsa/adsp/
$(foreach test_model, $(shell ls -1 test/*/*), adb shell 'cd /vendor/bin; touch /vendor/lib/rfsa/adsp/run_main_on_hexagon.farf; ./run_main_on_hexagon 3 /vendor/bin/$(BENCHMARK_OUT) --verbose hannk-$(test_model)';)
$(foreach test_model, $(shell ls -1 test/*/*.tflite), adb shell 'cd /vendor/bin; touch /vendor/lib/rfsa/adsp/run_main_on_hexagon.farf; ./run_main_on_hexagon 3 /vendor/bin/$(BENCHMARK_OUT) --verbose hannk-$(test_model)';)

clean:
rm -rf $(BIN)
Expand Down
43 changes: 43 additions & 0 deletions apps/hannk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,46 @@ Usage:

compare_vs_tflite a.tflite [b.tflite ...]

### WebAssembly

There is limited support for building and running hannk under WebAssembly.

#### Requirements:

- You must use CMake to build (Make isn't supported).
- You must have Emscripten v2.0.32 (or later) installed and activated.
- You must have Node.js v16.13 (or later) installed for testing.
Note that (as of this writing), EMSDK includes an older version of Node that *will not* work.

#### Building:

The simplest way is:

```
$ HL_TARGET=wasm-32-wasmrt-wasm_simd128 NODE_JS_EXECUTABLE=/path/to/good/version/of/node ./configure_cmake.sh
...output...
$ ninja
```

Note that `wasm_simd128` is optional, but highly recommended.


#### Running:

If you've built as described above, you can just run `ctest` to run the basic self-tests.

If you want to run `benchmark` or `compare_vs_tflite` manually, you'll need to launch it under `node`
manually; as noted above, when EMSDK is activated, `node` will likely refer to a version of Node.js
that won't work, so you will need to provide a path to a suitable version:

```
$ cd build
$ /path/to/good/version/of/node benchmark ../test/*/*.tflite
$ /path/to/good/version/of/node compare_vs_tflite ../test/*/*.tflite
```

Note that compare_vs_tflite doesn't actually build or use tflite when compiling under WebAssembly!
The only mode it supports is directly parsing the .tflite files, which is pretty close to the same as
the `benchmark` tool.

115 changes: 86 additions & 29 deletions apps/hannk/configure_cmake.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,114 @@

set -e

HANNK_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
BUILD_DIR="${HANNK_DIR}/build"
HANNK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"

mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}"
if [ -z "${BUILD_DIR}" ]; then
BUILD_DIR="${HANNK_DIR}/build"
fi

if [ -z ${HALIDE_INSTALL_PATH} ]; then
HALIDE_INSTALL_PATH=${HOME}/halide-14-install/
if [ -z "${HALIDE_INSTALL_PATH}" ]; then
HALIDE_INSTALL_PATH="${HOME}/halide-14-install/"
fi
echo Using HalideInstall=${HALIDE_INSTALL_PATH}

if [ -z ${HL_TARGET} ]; then
HL_TARGET=host
if [ -z "${HL_TARGET}" ]; then
HL_TARGET=host
fi
echo Using HL_TARGET=${HL_TARGET}

if [ -z "${CMAKE_GENERATOR}" ]; then
CMAKE_GENERATOR=Ninja
CMAKE_GENERATOR=Ninja
fi
echo Using build tool=${CMAKE_GENERATOR}

if [ -z "${CMAKE_BUILD_TYPE}" ]; then
CMAKE_BUILD_TYPE=Release
CMAKE_BUILD_TYPE=Release
fi

if [ -z "${ANDROID_PLATFORM}" ]; then
ANDROID_PLATFORM=21
fi
echo Using CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}

if [ -z "${HANNK_BUILD_TFLITE}" ]; then
HANNK_BUILD_TFLITE=ON
HANNK_BUILD_TFLITE=ON
else
HANNK_BUILD_TFLITE=OFF
HANNK_BUILD_TFLITE=OFF
fi
echo Using HANNK_BUILD_TFLITE=${HANNK_BUILD_TFLITE}

EXTRAS=
# TODO: this doesn't work (yet); crosscompiling in CMake is painful.
## In a cross-compiling scenario, use a separate host and build dir
# TODO: figure out if there's a way to generalize "is this crosscompiling or not", and just make this a single if-else

if [[ "${HL_TARGET}" =~ ^arm-64-android.* ]]; then
echo Configuring for Android arm64-v8a build...
echo Using ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}
EXTRAS="-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a"
HOST_BUILD_DIR="${BUILD_DIR}/_host"
HOST_BUILD_TARGET=(--target hannk-halide_generators)
HOST_HL_TARGET=host
HOST_CMAKE_DEFS=(-DHANNK_AOT_HOST_ONLY=ON)
elif [[ "${HL_TARGET}" =~ ^wasm-32-wasmrt.* ]]; then
HOST_BUILD_DIR="${BUILD_DIR}/_host"
HOST_BUILD_TARGET=(--target hannk-halide_generators)
HOST_HL_TARGET=host
HOST_CMAKE_DEFS=(-DHANNK_AOT_HOST_ONLY=ON)
else
echo Assuming host build...
HOST_BUILD_DIR="${BUILD_DIR}"
HOST_BUILD_TARGET=()
HOST_HL_TARGET="${HL_TARGET}"
HOST_CMAKE_DEFS=()
fi

## Build HANNK for the host no matter what

echo "Configuring HANNK for ${HOST_HL_TARGET}"
cmake \
${EXTRAS} \
-G "${CMAKE_GENERATOR}" \
-S "${HANNK_DIR}" \
-B "${HOST_BUILD_DIR}" \
"${HOST_CMAKE_DEFS[@]}" \
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \
-DHalide_DIR="${HALIDE_INSTALL_PATH}" \
-DCMAKE_PREFIX_PATH="${HALIDE_INSTALL_PATH}" \
-DHalide_TARGET=${HL_TARGET} \
-DHANNK_BUILD_TFLITE=${HANNK_BUILD_TFLITE} \
-DHalide_DIR="${HALIDE_INSTALL_PATH}/lib/cmake/Halide" \
-DHalideHelpers_DIR="${HALIDE_INSTALL_PATH}/lib/cmake/HalideHelpers" \
-DHalide_TARGET="${HOST_HL_TARGET}" \
-DHANNK_BUILD_TFLITE=${HANNK_BUILD_TFLITE}

if [ -z "${HOST_BUILD_TARGET[*]}" ]; then
echo "Building HANNK for ${HOST_HL_TARGET}"
else
echo "Building HANNK host generator executables"
fi
cmake --build "${HOST_BUILD_DIR}" "${HOST_BUILD_TARGET[@]}"

## Now if we're cross-compiling for Android or WASM, set up the build
## for that, using the platform-provided CMake toolchain files.

if [[ "${HL_TARGET}" =~ ^arm-64-android.* ]]; then
echo "Using ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}"
echo "Using CMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake"
CROSS_CMAKE_DEFS=(
-DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake"
-DANDROID_ABI=arm64-v8a
"-DANDROID_PLATFORM=${ANDROID_PLATFORM}"
# Required because TFLite's internal Eigen tries to compile an unnecessary BLAS with the system Fortran compiler.
"-DCMAKE_Fortran_COMPILER=NO"
)
elif [[ "${HL_TARGET}" =~ ^wasm-32-wasmrt.* ]]; then
echo "Using NODE_JS_EXECUTABLE=${NODE_JS_EXECUTABLE}"
CROSS_CMAKE_DEFS=(
-DCMAKE_TOOLCHAIN_FILE="${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
-DNODE_JS_EXECUTABLE="${NODE_JS_EXECUTABLE}"
)
else
# Not cross-compiling, so we're done.
exit
fi

echo "Configuring cross-build HANNK for ${HL_TARGET}"
cmake \
-G "${CMAKE_GENERATOR}" \
-S "${HANNK_DIR}" \
-B "${BUILD_DIR}"
-B "${BUILD_DIR}" \
"${CROSS_CMAKE_DEFS[@]}" \
-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" \
-DHANNK_BUILD_TFLITE=OFF \
-DHalide_TARGET="${HL_TARGET}" \
-DHalideHelpers_DIR="${HALIDE_INSTALL_PATH}/lib/cmake/HalideHelpers" \
-Dhannk-halide_generators_ROOT="${HOST_BUILD_DIR}"

echo "Building cross-build HANNK for ${HL_TARGET}"
cmake --build "${BUILD_DIR}"
Loading