validate.sh raw

   1  #!/bin/bash
   2  # Validate the wasm target against native.
   3  #
   4  # Every program with a main() is compiled twice - once natively and once for
   5  # -target wasm - then both are run and their stdout compared. Native is the
   6  # reference: the wasm module must produce identical output in a real browser.
   7  # This is the evidence that the wasm target is correct, not merely that it
   8  # links.
   9  set -uo pipefail
  10  
  11  ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
  12  MOXIE="${MOXIE:-$ROOT/moxie}"
  13  WORK="${WORK:-$ROOT/tests/wasm/tmp}"
  14  PASS=0
  15  FAIL=0
  16  
  17  # Programs to validate: each is a directory or .mx file containing a main().
  18  TARGETS=()
  19  if [ $# -gt 0 ]; then
  20      TARGETS=("$@")
  21  else
  22      for f in "$ROOT"/tests/regressions/should_compile/*.mx; do
  23          [ -f "$f" ] || continue
  24          grep -q 'func main' "$f" && TARGETS+=("$f")
  25      done
  26      # Directory cases: main.mx plus a lib/ subpackage. A cross-package call is
  27      # not inlined and crosses the return codec, so whole classes of layout bug
  28      # are only reachable this way.
  29      for d in "$ROOT"/tests/regressions/should_compile/*/; do
  30          [ -f "$d/main.mx" ] || continue
  31          TARGETS+=("${d%/}")
  32      done
  33  fi
  34  
  35  rm -rf "$WORK"; mkdir -p "$WORK"
  36  
  37  # Start from an empty package cache: each run measures a cold compile, not
  38  # whatever an earlier run happened to cache. The cross-target corruption this
  39  # used to also guard against ("Cannot select: intrinsic %llvm.wasm.memory.size"
  40  # from a native build linking a wasm object) is fixed at the source: a compiler
  41  # process now owns a private scratch directory (buildScratchDir), so concurrent
  42  # builds cannot overwrite each other's IR between clang writing it and the read
  43  # that caches it, and each target has its own cache root.
  44  rm -rf "$HOME/moxie/cache" 2>/dev/null
  45  if [ -n "${MOXIEPATH:-}" ]; then
  46      rm -rf "$MOXIEPATH/cache" 2>/dev/null
  47  fi
  48  
  49  for t in "${TARGETS[@]}"; do
  50      name="$(basename "$t")"; name="${name%.mx}"
  51      d="$WORK/$name"
  52      mkdir -p "$d/pkg"
  53      if [ -f "$t" ]; then
  54          cp "$t" "$d/pkg/main.mx"
  55      else
  56          cp "$t"/*.mx "$d/pkg/" 2>/dev/null
  57      fi
  58      src="$d/pkg"
  59  
  60      # native reference
  61      if ! (cd "$ROOT" && MOXIEROOT="$ROOT" "$MOXIE" build -o "$d/native" "$src") >"$d/native.log" 2>&1; then
  62          printf '%-32s SKIP  (native build failed)\n' "$name"
  63          continue
  64      fi
  65      "$d/native" >"$d/native.out" 2>&1
  66      nrc=$?
  67  
  68      # wasm module
  69      if ! (cd "$ROOT" && MOXIEROOT="$ROOT" "$MOXIE" build -target wasm -o "$d/app.wasm" "$src") >"$d/wasm.log" 2>&1; then
  70          printf '%-32s FAIL  (wasm build failed)\n' "$name"
  71          tail -3 "$d/wasm.log" | sed 's/^/      /'
  72          FAIL=$((FAIL+1))
  73          continue
  74      fi
  75  
  76      # Run in a browser. A module that spins never yields, so the driver cannot
  77      # deliver run.py's own wait_for_function timeout and the call blocks
  78      # forever: bound each run here, or one bad module stalls the whole suite
  79      # and hides every later result. RUN_TIMEOUT is seconds.
  80      timeout "${RUN_TIMEOUT:-60}" python3 "$ROOT/tests/wasm/run.py" -q "$d/app.wasm" >"$d/wasm.out" 2>&1
  81      rrc=$?
  82      if [ "$rrc" -ne 0 ]; then
  83          if [ "$rrc" -eq 124 ]; then
  84              printf '%-32s FAIL  (browser run timed out after %ss)\n' "$name" "${RUN_TIMEOUT:-60}"
  85          else
  86              printf '%-32s FAIL  (browser run failed)\n' "$name"
  87          fi
  88          tail -3 "$d/wasm.out" | sed 's/^/      /'
  89          FAIL=$((FAIL+1))
  90          continue
  91      fi
  92      cp "$d/wasm.out" "$d/wasm.clean"
  93  
  94      if [ "$nrc" != "0" ]; then
  95          printf '%-32s SKIP  (native exited %s)\n' "$name" "$nrc"
  96          continue
  97      fi
  98      if diff -q "$d/native.out" "$d/wasm.clean" >/dev/null; then
  99          printf '%-32s PASS\n' "$name"
 100          PASS=$((PASS+1))
 101      else
 102          printf '%-32s FAIL  (output differs)\n' "$name"
 103          diff "$d/native.out" "$d/wasm.clean" | head -6 | sed 's/^/      /'
 104          FAIL=$((FAIL+1))
 105      fi
 106  done
 107  
 108  echo
 109  echo "wasm validation: $PASS passed, $FAIL failed"
 110  [ "$FAIL" -eq 0 ]
 111