GNU Octave Meets JupyterLite: Compute Anywhere, Anytime!

Octave-lite logo

We are thrilled to announce the newest member of our JupyterLite kernel ecosystem: Xeus-Octave. Xeus-Octave allows you to run GNU Octave code directly on your browser. GNU Octave is a free and open-source Scientific Programming Language that can be used to run Matlab scripts. In this article, we present the challenges encountered when targeting WebAssembly, the current state of the Xeus-Octave kernel, and the future plans for expanding the GNU Octave ecosystem.


Earlier this year, we introduced the JupyterLite kernel for R, Xeus-R-Lite. Much like R, cross-compiling GNU Octave to WebAssembly required the same custom toolchain to enable the compilation of Fortran code, combining LLVM Flang and Emscripten.

Similar to many other mathematically oriented language packages, GNU Octave requires a BLAS/LAPACK implementation. Fortunately, OpenBLAS and the Netlib implementations of BLAS/LAPACK had already been added to the emscripten-forge WebAssembly distribution. Initially, OpenBLAS was the preferred implementation, but for the successful compilation of Octave, Netlib LAPACK was selected as it presented fewer hurdles during the build process.


Cross-Compilation of GNU Octave

One of the complications of cross-compiling Octave to WebAssembly, which had not been encountered with the R source code, was the extensive use of Fortran common symbols blocks in the internal libraries of Octave such as odepack.

C Source: liboctave/external/odepack/slsode.f
C-----------------------------------------------------------------------
C The following internal Common block contains
C (a) variables which are local to any subroutine but whose values must
C     be preserved between calls to the routine ("own" variables), and
C (b) variables which are communicated between subroutines.
C The block SLS001 is declared in subroutines SLSODE, SINTDY, SSTODE,
C SPREPJ, and SSOLSY.
C Groups of variables are replaced by dummy arrays in the Common
C declarations in routines where those variables are not used.
C-----------------------------------------------------------------------
     COMMON /SLS001/ CONIT, CRATE, EL(13), ELCO(13,12),
    1   HOLD, RMAX, TESCO(3,12),
    1   CCMAX, EL0, H, HMIN, HMXI, HU, RC, TN, UROUND,
    2   INIT, MXSTEP, MXHNIL, NHNIL, NSLAST, NYH,
    3   IALTH, IPUP, LMAX, MEO, NQNYH, NSLP,
    3   ICF, IERPJ, IERSL, JCUR, JSTART, KFLAG, L,
    4   LYH, LEWT, LACOR, LSAVF, LWM, LIWM, METH, MITER,
    5   MAXORD, MAXCOR, MSBP, MXNCF, N, NQ, NST, NFE, NJE, NQU

Initially, it was not possible to cross-compile these common blocks to WebAssembly because the latest version of LLVM (v20 at the time of testing) did not support common symbol linkage.

// Source: llvm/lib/MC/MCWasmStreamer.cpp
void MCWasmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size,
                                      Align ByteAlignment) {
  llvm_unreachable("Common symbols are not yet implemented for Wasm");
}

As a temporary solution, LLVM was patched with the help of Serge Guelton to simulate common symbols as weak symbols.

void MCWasmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size,
                                      Align ByteAlignment) {
-  llvm_unreachable("Common symbols are not yet implemented for Wasm");
+  auto *Symbol = cast<mcsymbolwasm>(S);
+  getAssembler().registerSymbol(*Symbol);
+  Symbol->setWeak(true);
+  Symbol->setExternal(true);
}

A proper solution to enable support of common symbols is currently in progress and will likely be included in the next release of LLVM v22 (see llvm-project/pull/151478). For curious readers, the patched version of LLVM can be found here (linux only).

In addition to the patches for LLVM, GNU Octave required a few minor modifications to target WebAssembly; mainly this entailed disabling the GUI functionalities and consolidating the Fortran function signatures and calling conventions. A full list of patches can be found in the recipe directory on emscripten-forge.


Xeus-Octave

Once GNU Octave had been successfully packaged for WebAssembly, bringing Xeus-Octave to JupyterLite was a simple matter of adding a recipe to emscripten-forge!

A JupyterLite notebook demonstrating plots with Xeus-Octave
Try Xeus-Octave in JupyterLite!

Future Work

For our next steps, the team is planning on expanding the Octave ecosystem by adding Octave packages to both conda-forge and emscripten-forge. The packaging work will require defining a process where Octave packages can be installed in predetermined conda environments, perhaps with some minor modifications to the existing pkg utility.


About the Author

Isabel Paredes, who led the charge on bringing GNU Octave to emscripten-forge, is a senior scientific software developer at QuantStack. Prior to working on this project, she focused on porting the R programming language and the Robot Operating System (ROS) framework to WebAssembly.

Acknowledgments

This project synthesizes work from many open-source contributors.