#!/bin/bash # Build BLIS (generic config) as a WebAssembly static library to serve as a # fast BLAS backend for the WASM libflame build. # # Link order matters: put libblis.a BEFORE libflame.a so the linker resolves # BLAS symbols from BLIS; libflame's built-in f2c BLAS members are then never # pulled in (they remain only as fallback for routines BLIS doesn't provide, # e.g. banded/packed level-2). # # Usage: ./build-blis-wasm.sh (single-threaded) # THREADING=pthreads ./build-blis-wasm.sh (wasm-threads build; # link apps with -pthread -sPTHREAD_POOL_SIZE=N, and all other # linked objects must also be compiled with -pthread) set -euo pipefail THREADING=${THREADING:-single} EXTRA_CFLAGS="" if [ "$THREADING" = "pthreads" ]; then EXTRA_CFLAGS="-pthread" fi cd "$(dirname "$0")" if ! command -v emcc >/dev/null 2>&1; then source ~/emsdk/emsdk_env.sh fi if [ ! -d blis ]; then git clone --depth 1 https://github.com/flame/blis fi cd blis # --- Patches to make BLIS's BLAS interface WASM-compatible with libflame --- # # WASM requires call sites and definitions to agree on exact signatures. # libflame's f2c-derived code declares BLAS subroutines returning int and # calls lsame_/xerbla_ with 2 args (no hidden Fortran string lengths). # Stock BLIS uses void returns and 4-arg lsame_ / 3-arg xerbla_. # 1. Drop BLIS's f2c-derived compat sources (banded/packed level-2 routines, # lsame_, xerbla_). libflame.a already provides all of them in the # 2-arg/int f2c convention. Keep cabs1 (no conflicts) and all headers. ls frame/compat/f2c/*.c | grep -v cabs1 | xargs rm -f rm -f frame/compat/f2c/bla_xerbla_array.h sed -i '/#include "bla_xerbla_array.h"/d' frame/compat/bli_blas_defs.h # 2. Prototype lsame_/xerbla_ with 2 args to match libflame's definitions. sed -i 's/, long ca_len, long cb_len//; s/, int ca_len, int cb_len//' frame/compat/f2c/bla_lsame.h sed -i 's/, ftnlen srname_len//' frame/compat/f2c/bla_xerbla.h # 3. Strip hidden string-length args from BLIS's internal lsame_/xerbla_ calls. sed -i 's/, *(ftnlen)[0-9]*//g' frame/compat/check/*.h # 4. Give every BLAS interface function an int return (f2c convention), so # signatures match libflame's declarations. Bare `return;` must become # `return 0;` (a hard error in C23 otherwise). sed -i 's/void PASTEF77/int PASTEF77/g' \ frame/compat/*.c frame/compat/*.h frame/compat/f2c/*.h \ frame/compat/extra/*.c frame/compat/extra/*.h frame/compat/amd/*.c sed -i 's/\breturn; \\/return 0; \\/g; s/^\( *\)return;$/\1return 0;/' \ frame/compat/*.c frame/compat/check/*.h frame/compat/extra/*.c frame/compat/amd/*.c # 5. BLIS tests the obsolete EMSCRIPTEN macro; modern emcc defines __EMSCRIPTEN__. sed -i 's/defined(EMSCRIPTEN)/defined(__EMSCRIPTEN__)/' frame/include/bli_system.h # --- Configure and build --- # -msimd128 lets the generic C kernels autovectorize to wasm SIMD. CC=emcc AR=emar RANLIB=emranlib CFLAGS="-msimd128 $EXTRA_CFLAGS" ./configure \ --disable-shared --enable-static \ --enable-blas --disable-cblas \ --int-size=32 --blas-int-size=32 \ --complex-return=gnu \ --enable-threading="$THREADING" \ generic # The generic make_defs.mk rejects the "emcc" vendor string; emcc is clang. make -j"$(nproc)" CC_VENDOR=clang echo "Done. Library: $(pwd)/lib/generic/libblis.a"