Skip to content

WebAssembly bindings #44

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

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9522533
Import bergamot wasm compile
jerinphilip Dec 20, 2023
c8adc0c
Add a bindings file
jerinphilip Dec 20, 2023
066f014
Skeleton imagination: Need to get to compile -> work, now
jerinphilip Dec 20, 2023
3d37331
Add emsdk-version
jerinphilip Dec 20, 2023
e1742e4
Turn on ruy, turn off blas, gemmology
jerinphilip Dec 20, 2023
97a3e31
WITH_BLAS, not USE_BLAS
jerinphilip Dec 20, 2023
14d2c4b
Compile PCRE2, we're almost close to android
jerinphilip Dec 20, 2023
2be8a3c
Fix narrowing casts: uint64_t -> size_t via static_cast
jerinphilip Dec 20, 2023
3165122
Add wiring: BUILD_{CLI,PYTHON,WASM}
jerinphilip Dec 20, 2023
eb17e83
Add CMakeLists.txt, include bindings/wasm/slimt.cpp in compile
jerinphilip Dec 20, 2023
8d65292
Disable importing GEMM
jerinphilip Dec 20, 2023
60b52b1
Remove repeated link_libraries
jerinphilip Dec 20, 2023
2ae5d5d
Fix some signatures
jerinphilip Dec 20, 2023
2210bdc
Fix cmake-format
jerinphilip Dec 20, 2023
7a0cd90
Fix some compiler errors
jerinphilip Dec 20, 2023
7f0c375
Comment out non working parts to incrementally build
jerinphilip Dec 21, 2023
c181046
Disable tail test runs until successful compile
jerinphilip Dec 21, 2023
b94ed66
Add a shell-script documenting WASM local build commands
jerinphilip Dec 21, 2023
3f90cc0
Fix cmake formatting
jerinphilip Dec 21, 2023
a1684d2
Merge branch 'main' into wasm
jerinphilip Oct 24, 2024
ee0f709
GitHub CI: actions/upload-artifact@{v2->v4}
jerinphilip Oct 24, 2024
9a2ff79
Bump cibuildwheel: v2.11.1 -> v2.21.3
jerinphilip Oct 24, 2024
4000bf8
MacOS: Install `python-setuptools` from brew
jerinphilip Oct 24, 2024
40d8de9
pybind11: Clone v2.10.3 specifically
jerinphilip Oct 24, 2024
535a61e
Add accelerate specific compile definitions for cblas_sgemm
jerinphilip Oct 24, 2024
657546f
Format CMakeLists.txt
jerinphilip Oct 24, 2024
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
136 changes: 131 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ env:
ccache_maxsize: 200M
ccache_cmake: -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache
ccache_version: '4.5'
emsdk_version: 3.1.8 # For use in emscripten build

jobs:
formatting:
Expand Down Expand Up @@ -45,7 +46,7 @@ jobs:
run:
bash scripts/ci/format-check.sh

- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
if: always()
with:
name: format
Expand Down Expand Up @@ -88,7 +89,7 @@ jobs:
run:
bash scripts/ci/${{ matrix.name }}/04-package.sh

- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: slimt
Expand Down Expand Up @@ -122,7 +123,7 @@ jobs:
submodules: recursive
- name: "Fetch pybind11"
run: |
git clone https://github.com/pybind/pybind11 3rd-party/pybind11
git clone --branch v2.10.3 https://github.com/pybind/pybind11 3rd-party/pybind11

- name: Generate ccache_vars for ccache based on machine
shell: bash
Expand Down Expand Up @@ -194,7 +195,7 @@ jobs:


- name: Build wheels
uses: pypa/cibuildwheel@v2.11.1
uses: pypa/cibuildwheel@v2.21.3
# to supply options, put them in 'env', like:
env:
CIBW_BUILD_VERBOSITY: 3
Expand Down Expand Up @@ -285,7 +286,7 @@ jobs:
# gdb -return-child-result -batch -ex 'set follow-fork-mode child' -ex 'run' -ex 'bt' -ex 'quit' --args python -m pytest --pyargs slimt -s


- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: wheels
path: ./wheelhouse/*.whl
Expand Down Expand Up @@ -367,3 +368,128 @@ jobs:
run: |
python3 -m pip install twine
twine upload *.whl

build-wasm:
name: "emscripten"
runs-on: ubuntu-latest
steps:

- name: Checkout
uses: actions/checkout@v2
with:
submodules: recursive

- name: Set ccache environment for emcc
run: |
# We are hardcoding this to mtime instead of env pickup. Rest use content.
echo "CCACHE_COMPILER_CHECK=mtime" >> $GITHUB_ENV

echo "CCACHE_BASEDIR=${{ env.ccache_basedir }}" >> $GITHUB_ENV
echo "CCACHE_COMPRESS=${{ env.ccache_compress }}" >> $GITHUB_ENV
echo "CCACHE_COMPRESSLEVEL=${{ env.ccache_compresslevel }}" >> $GITHUB_ENV
echo "CCACHE_DIR=${{ env.ccache_dir }}" >> $GITHUB_ENV
echo "CCACHE_MAXSIZE=${{ env.ccache_maxsize }}" >> $GITHUB_ENV
# https://emscripten.org/docs/compiling/Building-Projects.html#using-a-compiler-wrapper
echo "EM_COMPILER_WRAPPER=ccache" >> $GITHUB_ENV

# This need to be run before setup, so ccache build caching doesn't complain.
- name: Obtain emsdk sources
run: |
git clone --depth 1 https://github.com/emscripten-core/emsdk.git

- name: Cache-op for build-cache through ccache
uses: actions/cache@v2
with:
path: |
${{ env.ccache_dir }}
${{ github.workspace }}/emsdk/ccache/git-emscripten_64bit/
key: ccache-${{ github.job }}-${{ env.emsdk_version }}-${{ steps.ccache_vars.outputs.hash }}-${{ github.ref }}-${{ steps.ccache_vars.outputs.timestamp }}
restore-keys: |-
ccache-${{ github.job }}-${{ env.emsdk_version }}-${{ steps.ccache_vars.outputs.hash }}-${{ github.ref }}
ccache-${{ github.job }}-${{ env.emsdk_version }}-${{ steps.ccache_vars.outputs.hash }}
ccache-${{ github.job }}-${{ env.emsdk_version }}

- name: Setup Emscripten toolchain
run: |
(cd emsdk && ./emsdk install ${{ env.emsdk_version }} ccache-git-emscripten-64bit)
(cd emsdk && ./emsdk activate ${{ env.emsdk_version }} ccache-git-emscripten-64bit)
# mtime of this file is checked by ccache, we set it to avoid cache misses.
touch -m -d '1 Jan 2021 12:00' emsdk/.emscripten

# These needs to be done in the activated shell.
eval $(./emsdk/emsdk construct_env \
| sed 's/export PATH=\(.*\);/echo \1 >> $GITHUB_PATH;/' \
| sed 's/export \(.*\);/echo \1 >> $GITHUB_ENV;/' );

# This looks more permanent than version pinned, so keeping temporarily to avoid failures.
echo "${{ github.workspace }}/emsdk/ccache/git-emscripten_64bit/bin" >> $GITHUB_PATH

- name: Generate ccache_vars for ccache based on machine
shell: bash
id: ccache_vars
run: |-
echo "::set-output name=hash::$(echo ${{ env.ccache_compilercheck }})"
echo "::set-output name=timestamp::$(date '+%Y-%m-%dT%H.%M.%S')"

- name: Verify Emscripten setup
run: |
emcc --version
emcmake cmake --version
emmake make --version

- name: ccache prolog
run: |-
ccache -s # Print current cache stats
ccache -z # Zero cache entry

- name: "Configure builds"
run: |
mkdir -p build-wasm
cd build-wasm
emcmake cmake -DWITH_BLAS=OFF -DWITH_RUY=ON -DWITH_GEMMOLOGY=OFF -DSLIMT_USE_INTERNAL_PCRE2=ON -DBUILD_WASM=ON ..


- name: "Compile"
working-directory: build-wasm
run: |
emmake make -j2

- name: ccache epilog
run: |
ccache -s # Print current cache stats

# The following is useless as it works within Firefox, that too only in a
# privileged context. Might choose to enable should Mozilla be
# interested.
#
# - name: Import GEMM library from a separate wasm module
# working-directory: build-wasm
# run: bash ../wasm/patch-artifacts-import-gemm-module.sh

# Setup nodejs-18, as nodejs-14 provided by emsdk fails when running
# and newer version of node allows us to use fetch().
- name: Setup nodejs
uses: actions/setup-node@v3
with:
node-version: 18

# - name: Test run
# working-directory: wasm
# run: |
# cp ../build-wasm/bergamot-translator-worker.{js,wasm} ./
# npm install jsdom

# # --unhandled-rejections make the script exit with a non-zero code (at least on node-14).
# # So leaving this here.
# node --unhandled-rejections=strict node-test.js

# # Upload both together.
# - name: Upload wasm artifact
# uses: actions/upload-artifact@v4
# with:
# name: wasm-artefacts
# if-no-files-found: error
# path: |
# ${{github.workspace}}/build-wasm/bergamot-translator-worker.js
# ${{github.workspace}}/build-wasm/bergamot-translator-worker.wasm
# ${{github.workspace}}/build-wasm/bergamot-translator-worker.js.bak
19 changes: 18 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ option(SLIMT_PYTHON_LINK_STATIC
"link-method to produce python package (static/shared)" ON)
option(SLIMT_GENERATED_UNIT_TESTS "Generate unit tests to run using Python" OFF)

option(BUILD_CLI "Build command-line applications" ON)
option(BUILD_PYTHON "Build Python bindings library" OFF)
option(BUILD_WASM "Build WebAssemby bindings library" OFF)

if(BUILD_WASM)
set(BUILD_CLI OFF)
endif(BUILD_WASM)

include(MacroEnsureOutOfSourceBuild)
macro_ensure_out_of_source_build(
"${PROJECT_NAME} requires an out of source build. Please create a separate build directory and run 'cmake /path/to/${PROJECT_NAME} [options]' there."
Expand Down Expand Up @@ -119,6 +127,12 @@ if(WITH_BLAS)
)
add_library(AppleLibs::accelerate ALIAS apple_accelerate)
list(APPEND SLIMT_PRIVATE_LIBS AppleLibs::accelerate)
# 'cblas_sgemm' is deprecated: first deprecated in macOS 13.3 - An updated
# CBLAS interface supporting ILP64 is available. Please compile with
# -DACCELERATE_NEW_LAPACK to access the new headers and
# -DACCELERATE_LAPACK_ILP64 for ILP64 support.
list(APPEND SLIMT_COMPILE_DEFINITIONS ACCELERATE_NEW_LAPACK
ACCELERATE_LAPACK_ILP64)
endif(APPLE)
endif(WITH_BLAS)

Expand Down Expand Up @@ -219,10 +233,13 @@ if(BUILD_PYTHON)
else(USE_PYBIND11_SOURCE)
find_package(pybind11 REQUIRED)
endif(USE_PYBIND11_SOURCE)

add_subdirectory(bindings/python)
endif(BUILD_PYTHON)

if(BUILD_WASM)
add_subdirectory(bindings/wasm)
endif(BUILD_WASM)

if(BUILD_JNI)
add_subdirectory(bindings/java)
endif(BUILD_JNI)
16 changes: 9 additions & 7 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
add_executable(slimt_cli main.cc)
set_target_properties(slimt_cli PROPERTIES OUTPUT_NAME "slimt-cli")
target_link_libraries(slimt_cli PUBLIC slimt)
if(BUILD_CLI)
add_executable(slimt_cli main.cc)
set_target_properties(slimt_cli PROPERTIES OUTPUT_NAME "slimt-cli")
target_link_libraries(slimt_cli PUBLIC slimt-static)

set(SLIMT_BINARIES slimt_cli)
set(SLIMT_BINARIES slimt_cli)

if(UNIX)
install(TARGETS ${SLIMT_BINARIES} DESTINATION ${CMAKE_INSTALL_BINDIR})
endif(UNIX)
if(UNIX)
install(TARGETS ${SLIMT_BINARIES} DESTINATION ${CMAKE_INSTALL_BINDIR})
endif(UNIX)
endif(BUILD_CLI)
84 changes: 84 additions & 0 deletions bindings/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# See https://github.com/emscripten-core/emscripten/blob/main/src/settings.js
list(
APPEND
WASM_COMPILE_FLAGS
-O3
# Preserve whitespaces in JS even for release builds; this doesn't increase
# wasm binary size
$<$<CONFIG:Release>:-g1>
# Relevant Debug info only for release with debug builds as this increases
# wasm binary size
$<$<CONFIG:RelWithDebInfo>:-g2>
-fPIC
-mssse3
-msimd128
# -fno-exceptions # Can't do that because spdlog uses exceptions
-sDISABLE_EXCEPTION_CATCHING=1
-sSTRICT=1)
list(
APPEND
WASM_LINK_FLAGS
-O3
# Preserve whitespaces in JS even for release builds; this doesn't increase
# wasm binary size
$<$<CONFIG:Release>:-g1>
# Relevant Debug info only for release with debug builds as this increases
# wasm binary size
$<$<CONFIG:RelWithDebInfo>:-g2>
-lembind
# Save some code, and some speed
-sASSERTIONS=0
-sDISABLE_EXCEPTION_CATCHING=1
# the intgemm functions we call will be undefined since these are linked at
# runtime by our own javascript.
#
# jerin: Disabling this particular flag, as it errors. em++: error: legacy
# setting used in strict mode: LLD_REPORT_UNDEFINED -sLLD_REPORT_UNDEFINED
-sERROR_ON_UNDEFINED_SYMBOLS=0
# Cause we can!
-sSTRICT=1
# You know we need it
-sALLOW_MEMORY_GROWTH=1
-sENVIRONMENT=web,worker
# No need to call main(), there's nothing there.
-sINVOKE_RUN=0
# No need for filesystem code in the generated Javascript
-sFILESYSTEM=0
# If you turn this on, it will mangle names which makes the dynamic linking
# hard.
-sDECLARE_ASM_MODULE_EXPORTS=0
# Export all of the intgemm functions in case we need to fall back to using
# the embedded intgemm
# -sEXPORTED_FUNCTIONS=[_int8PrepareAFallback,_int8PrepareBFallback,_int8PrepareBFromTransposedFallback,_int8PrepareBFromQuantizedTransposedFallback,_int8PrepareBiasFallback,_int8MultiplyAndAddBiasFallback,_int8SelectColumnsOfBFallback]
# Necessary for mozintgemm linking. This prepares the `wasmMemory` variable
# ahead of time as opposed to delegating that task to the wasm binary itself.
# This way we can link MozIntGEMM module to the same memory as the main
# bergamot-translator module.
-sIMPORTED_MEMORY=1
# Dynamic execution is either frowned upon or blocked inside browser
# extensions
-sDYNAMIC_EXECUTION=0
# no main, pass --no-entry to suppress, said linker.
--no-entry)

add_executable(wasm-slimt slimt.cpp)

# Generate version file that can be included in the wasm artifacts
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/project_version.js.in
${CMAKE_CURRENT_BINARY_DIR}/project_version.js @ONLY)

target_link_libraries(wasm-slimt PRIVATE slimt-static)

# This compile definition is required for generating binding code properly
target_compile_definitions(wasm-slimt PRIVATE WASM_BINDINGS)
target_compile_options(wasm-slimt PRIVATE ${WASM_COMPILE_FLAGS})
target_link_options(wasm-slimt PRIVATE ${WASM_LINK_FLAGS})
target_link_options(
wasm-slimt PRIVATE
--extern-pre-js=${CMAKE_CURRENT_BINARY_DIR}/project_version.js)

set_target_properties(
wasm-slimt
PROPERTIES OUTPUT_NAME "slimt"
SUFFIX ".js"
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
1 change: 1 addition & 0 deletions bindings/wasm/project_version.js.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
var SLIMT_VERSION_FULL = "@PROJECT_VERSION_STRING_FULL@";
Loading
Loading