#!/bin/sh
# crmvec-run: run a program with crmvec's libmvec.so.1 (and, on aarch64,
# libsleefgnuabi.so.3) found before the system's, so that the vector math it
# was compiled to call is correctly rounded. Nothing else changes.
# With --libm (x86-64 and aarch64), libcrpreload.so is preloaded too, so the program's
# scalar libm calls (exp, sin, pow, ...) are correctly rounded as well.
# With --fast (x86-64), the vector math comes from fast/libmvec.so.1 instead:
# not correctly rounded, but within OpenCL's bound for each function, at about
# glibc's speed, and the same bits on every CPU with AVX2 and FMA.
#   crmvec-run ./program args...
#   crmvec-run --libm ./program args...
#   crmvec-run --fast ./program args...
#   crmvec-run --libm --stamp OUTDIR ./program args...   (then records in OUTDIR what math was used: crmvec-stamp)
#   crmvec-run --help | --version
libm=; fast=; stamp=
while :; do
  case "${1-}" in
  --help | -h)
    echo "usage: crmvec-run [--libm] [--fast] [--stamp DIR] PROGRAM [ARGS...]"
    echo "Runs PROGRAM with crmvec's correctly rounded libmvec.so.1 found first"
    echo "(from /usr/lib/x86_64-linux-gnu/crmvec:/usr/lib/aarch64-linux-gnu/crmvec); nothing else changes. With --libm, also preloads"
    echo "libcrpreload.so: CORE-MATH's correctly rounded functions under the C"
    echo "library's names, for the program's scalar math calls (x86-64)."
    echo "With --fast, the vector math comes from crmvec's fast mode instead"
    echo "(x86-64): not correctly rounded, within OpenCL's bound for each"
    echo "function, at about glibc's speed, and the same bits on every CPU with"
    echo "AVX2 and FMA (others get the correctly rounded results)."
    echo "With --stamp DIR, after PROGRAM ends, records in DIR which math it ran"
    echo "with (DIR/crmvec-libm.json, and a BIDS dataset_description.json's"
    echo "GeneratedBy), and exits with PROGRAM's status (crmvec-stamp --help)."
    exit 0 ;;
  --version) echo "crmvec-run 0.12.2"; exit 0 ;;
  --libm) libm=1; shift ;;
  --fast) fast=1; shift ;;
  --stamp) [ $# -ge 2 ] || { echo "crmvec-run: --stamp needs a directory" >&2; exit 2; }; stamp=$2; shift 2 ;;
  *) break ;;
  esac
done
[ $# -gt 0 ] || { echo "usage: crmvec-run [--libm] [--fast] [--stamp DIR] PROGRAM [ARGS...] (see crmvec-run --help)" >&2; exit 2; }
if [ -n "$libm" ]; then
  # /usr/lib/x86_64-linux-gnu/crmvec:/usr/lib/aarch64-linux-gnu/crmvec may list several directories (Debian's has both architectures'): the first holding the library
  # (split from a variable: IFS splits expansions, not a literal word)
  pre=; dirs="/usr/lib/x86_64-linux-gnu/crmvec:/usr/lib/aarch64-linux-gnu/crmvec"; old_ifs=$IFS; IFS=:
  for d in $dirs; do [ -z "$pre" ] && [ -f "$d/libcrpreload.so" ] && pre="$d/libcrpreload.so"; done
  IFS=$old_ifs
  [ -n "$pre" ] || { echo "crmvec-run: --libm: no libcrpreload.so in /usr/lib/x86_64-linux-gnu/crmvec:/usr/lib/aarch64-linux-gnu/crmvec (it is built on x86-64 and aarch64)" >&2; exit 2; }
  LD_PRELOAD="$pre${LD_PRELOAD:+ $LD_PRELOAD}"; export LD_PRELOAD
fi
path="/usr/lib/x86_64-linux-gnu/crmvec:/usr/lib/aarch64-linux-gnu/crmvec"
if [ -n "$fast" ]; then
  fd=; dirs="/usr/lib/x86_64-linux-gnu/crmvec:/usr/lib/aarch64-linux-gnu/crmvec"; old_ifs=$IFS; IFS=:
  for d in $dirs; do [ -f "$d/fast/libmvec.so.1" ] && fd="${fd:+$fd:}$d/fast"; done
  IFS=$old_ifs
  [ -n "$fd" ] || { echo "crmvec-run: --fast: no fast/libmvec.so.1 in /usr/lib/x86_64-linux-gnu/crmvec:/usr/lib/aarch64-linux-gnu/crmvec (the fast mode is built on x86-64 only)" >&2; exit 2; }
  path="$fd:$path"
fi
LD_LIBRARY_PATH="$path${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"; export LD_LIBRARY_PATH
[ -n "$stamp" ] || exec "$@"
"$@"; rc=$?
mkdir -p "$stamp" && "$(dirname "$0")/crmvec-stamp" "$stamp" || echo "crmvec-run: --stamp: could not record in $stamp" >&2
exit $rc
