build-stdlib-archive.sh raw

   1  #!/bin/bash
   2  # Build the stdlib archive for moxie-iskra from a moxie source tree.
   3  # Requires: moxie (with -internal-printir), opt, llc, clang, ar, ld
   4  #
   5  # Usage: MOXIEROOT=../moxie ./tools/build-stdlib-archive.sh <target-dir> [output-dir]
   6  #
   7  # The target-dir is the moxie project to compile (e.g., ~/s/smesh).
   8  # Output goes to output-dir (default: /tmp/iskra-corpus).
   9  set -e
  10  
  11  TARGETDIR="${1:?usage: $0 <target-dir> [output-dir]}"
  12  OUTDIR="${2:-/tmp/iskra-corpus}"
  13  MOXIEROOT="${MOXIEROOT:?set MOXIEROOT to the moxie compiler source root}"
  14  
  15  mkdir -p "$OUTDIR"
  16  
  17  echo "=== Step 1: Generate post-interface-lowering IR ===" >&2
  18  cd "$TARGETDIR"
  19  moxie build -internal-printir -o /dev/null . > "$OUTDIR/lowered.ll" 2>/dev/null || true
  20  LINES=$(wc -l < "$OUTDIR/lowered.ll")
  21  echo "  generated $LINES lines of IR" >&2
  22  
  23  echo "=== Step 2: Fix visibility ===" >&2
  24  sed -E \
  25      's/^define internal /define /;
  26       s/^define hidden /define /;
  27       s/^define dso_local /define /;
  28       s/ = internal unnamed_addr / = unnamed_addr /g;
  29       s/ = internal constant/ = constant/g;
  30       s/ = internal global/ = global/g;
  31       s/ = hidden unnamed_addr / = unnamed_addr /g;
  32       s/ = hidden constant/ = constant/g;
  33       s/ = hidden global/ = global/g' \
  34      "$OUTDIR/lowered.ll" > "$OUTDIR/lowered-fixed.ll"
  35  
  36  echo "=== Step 2b: Strip app-level (main.*) definitions ===" >&2
  37  # Phase 1: strip app-level (main.*) definitions from the IR.
  38  # Keep: init functions, closures ($), string constants (main$string).
  39  # Strip: user-declared functions, method receiver functions, globals.
  40  awk '
  41  # Strip function defs: @main.func, @(*main.Type).method, @"(*main.Type).method"
  42  # But keep closures ($) and init
  43  /^define / && /@main\./ && !/@main\.[^(]*\$/ && !/@main\.init[^A-Za-z]/ { skip=1; next }
  44  /^define / && /@"main\./ && !/@"main\.[^"]*\$/ && !/@"main\.init[^A-Za-z]/ { skip=1; next }
  45  /^define / && /@\(\*main\./ { skip=1; next }
  46  /^define / && /@"\(\*main\./ { skip=1; next }
  47  /^define / && /@\(main\./ { skip=1; next }
  48  skip && /^}/ { skip=0; next }
  49  skip { next }
  50  # Strip main.* globals and alloc tables
  51  /^@main\./ { next }
  52  /^@"main\./ { next }
  53  /^@"main\$alloc/ { next }
  54  { print }
  55  ' "$OUTDIR/lowered-fixed.ll" > "$OUTDIR/lowered-stripped.ll"
  56  
  57  # Phase 2: find references to @main.* and @main$* that are now undefined, emit stubs
  58  { grep -oP '@"?main\.[^("{\s,)]+' "$OUTDIR/lowered-stripped.ll"
  59    grep -oP '@"main\$[^"]+' "$OUTDIR/lowered-stripped.ll"
  60  } | sort -u > "$OUTDIR/_main_refs.txt"
  61  { grep -P '^(define|declare) ' "$OUTDIR/lowered-stripped.ll" | grep -oP '@"?main\.[^("{\s,)]+'
  62    grep -P '^@"?main[\.\$]' "$OUTDIR/lowered-stripped.ll" | grep -oP '@"?main[\.\$][^("{\s,= ]+'
  63  } | sort -u > "$OUTDIR/_main_defined.txt"
  64  comm -23 "$OUTDIR/_main_refs.txt" "$OUTDIR/_main_defined.txt" > "$OUTDIR/_main_missing.txt"
  65  
  66  {
  67      cat "$OUTDIR/lowered-stripped.ll"
  68      while IFS= read -r sym; do
  69          case "$sym" in
  70              *'$'*) echo "$sym\" = external global i8" ;;
  71              *)     echo "declare void $sym(...)" ;;
  72          esac
  73      done < "$OUTDIR/_main_missing.txt"
  74  } > "$OUTDIR/lowered-stdlib.ll"
  75  
  76  BEFORE=$(grep -c '^define ' "$OUTDIR/lowered-fixed.ll")
  77  AFTER=$(grep -c '^define ' "$OUTDIR/lowered-stdlib.ll")
  78  STUBS=$(wc -l < "$OUTDIR/_main_missing.txt")
  79  echo "  functions: $BEFORE -> $AFTER (stripped $((BEFORE - AFTER)) main.* defs, added $STUBS stubs)" >&2
  80  rm -f "$OUTDIR/_main_refs.txt" "$OUTDIR/_main_defined.txt" "$OUTDIR/_main_missing.txt" "$OUTDIR/lowered-stripped.ll"
  81  
  82  echo "=== Step 3: Compile to object ===" >&2
  83  opt -O2 "$OUTDIR/lowered-stdlib.ll" -o "$OUTDIR/lowered.bc"
  84  llc -filetype=obj -O2 --function-sections --data-sections "$OUTDIR/lowered.bc" -o "$OUTDIR/stdlib.o"
  85  echo "  stdlib.o: $(du -h "$OUTDIR/stdlib.o" | cut -f1)" >&2
  86  
  87  echo "=== Step 4: Build vendored bdw-gc ===" >&2
  88  BDWGC="$MOXIEROOT/lib/bdwgc"
  89  TMPDIR=$(mktemp -d)
  90  GCFLAGS="-DUSE_MMAP -DUSE_MUNMAP -DGC_BUILTIN_ATOMIC -DNO_EXECUTE_PERMISSION"
  91  GCFLAGS="$GCFLAGS -DALL_INTERIOR_POINTERS -DIGNORE_DYNAMIC_LOADING -DNO_GETCONTEXT"
  92  GCFLAGS="$GCFLAGS -DGC_DISABLE_INCREMENTAL -DNO_MSGBOX_ON_ERROR -DDONT_USE_ATEXIT"
  93  GCFLAGS="$GCFLAGS -DNO_GETENV -DNO_CLOCK -DNO_DEBUGGING -DGC_NO_FINALIZATION"
  94  GCFLAGS="$GCFLAGS -DGC_DONT_REGISTER_MAIN_STATIC_DATA -DSTACK_NOT_SCANNED"
  95  GCFLAGS="$GCFLAGS -DNO_PROC_STAT -DSTACKBOTTOM=0 -I$BDWGC/include -O2 -ffunction-sections -fdata-sections"
  96  GCOBJS=()
  97  for src in allchblk.c alloc.c blacklst.c dbg_mlc.c dyn_load.c headers.c \
  98             mach_dep.c malloc.c mallocx.c mark.c mark_rts.c misc.c \
  99             new_hblk.c os_dep.c ptr_chck.c reclaim.c; do
 100      out="$TMPDIR/$src.o"
 101      clang -c $GCFLAGS -o "$out" "$BDWGC/$src" 2>/dev/null
 102      GCOBJS+=("$out")
 103  done
 104  ld -r -o "$OUTDIR/bdwgc.o" "${GCOBJS[@]}"
 105  echo "  bdwgc.o: $(du -h "$OUTDIR/bdwgc.o" | cut -f1)" >&2
 106  
 107  echo "=== Step 5: Build assembly stubs ===" >&2
 108  cat > "$TMPDIR/asm_stubs.c" << 'STUBEOF'
 109  #include <stdint.h>
 110  #include <sys/time.h>
 111  
 112  void cpuid_stub(uint32_t eax, uint32_t ecx, uint32_t* result)
 113      __asm__("internal/cpu.cpuid");
 114  void cpuid_stub(uint32_t eax, uint32_t ecx, uint32_t* result) {
 115      __asm__ __volatile__("cpuid"
 116          : "=a"(result[0]), "=b"(result[1]), "=c"(result[2]), "=d"(result[3])
 117          : "a"(eax), "c"(ecx));
 118  }
 119  
 120  uint64_t xgetbv_stub(uint32_t xcr) __asm__("internal/cpu.xgetbv");
 121  uint64_t xgetbv_stub(uint32_t xcr) {
 122      uint32_t lo, hi;
 123      __asm__ __volatile__("xgetbv" : "=a"(lo), "=d"(hi) : "c"(xcr));
 124      return ((uint64_t)hi << 32) | lo;
 125  }
 126  
 127  int32_t getgoamd64_stub(void) __asm__("internal/cpu.getGOAMD64level");
 128  int32_t getgoamd64_stub(void) {
 129      uint32_t eax, ebx, ecx, edx;
 130      __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(7), "c"(0));
 131      if (ebx & (1 << 5)) return 3;
 132      __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1), "c"(0));
 133      if (ecx & (1 << 28)) return 2;
 134      return 1;
 135  }
 136  
 137  struct syscall6_result { long r1; long r2; long err; };
 138  struct syscall6_result syscall6_stub(long num, long a1, long a2, long a3, long a4, long a5, long a6)
 139      __asm__("internal/runtime/syscall.Syscall6");
 140  struct syscall6_result syscall6_stub(long num, long a1, long a2, long a3, long a4, long a5, long a6) {
 141      struct syscall6_result res = {0, 0, 0};
 142      long r;
 143      register long r10 __asm__("r10") = a4;
 144      register long r8 __asm__("r8") = a5;
 145      register long r9 __asm__("r9") = a6;
 146      __asm__ __volatile__("syscall" : "=a"(r)
 147          : "a"(num), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9)
 148          : "rcx", "r11", "memory");
 149      if (r < 0 && r > -4096) { res.r1 = -1; res.err = -r; } else { res.r1 = r; }
 150      return res;
 151  }
 152  
 153  void entersyscall_stub(void) __asm__("runtime.entersyscall");
 154  void entersyscall_stub(void) {}
 155  void exitsyscall_stub(void) __asm__("runtime.exitsyscall");
 156  void exitsyscall_stub(void) {}
 157  void beforefork_stub(void) __asm__("syscall.runtime_BeforeFork");
 158  void beforefork_stub(void) {}
 159  void afterfork_stub(void) __asm__("syscall.runtime_AfterFork");
 160  void afterfork_stub(void) {}
 161  void afterforkinchild_stub(void) __asm__("syscall.runtime_AfterForkInChild");
 162  void afterforkinchild_stub(void) {}
 163  void doallthreadssyscall_stub(void) __asm__("syscall.runtime_doAllThreadsSyscall");
 164  void doallthreadssyscall_stub(void) {}
 165  int32_t haswaitingreaders_stub(void) __asm__("syscall.hasWaitingReaders");
 166  int32_t haswaitingreaders_stub(void) { return 0; }
 167  void cgocaller_stub(void) __asm__("syscall.cgocaller");
 168  void cgocaller_stub(void) {}
 169  void gettimeofday_stub(struct timeval *tv) __asm__("syscall.gettimeofday");
 170  void gettimeofday_stub(struct timeval *tv) { gettimeofday(tv, 0); }
 171  long rawvforksyscall_stub(long trap, long a1, long a2, long a3)
 172      __asm__("syscall.rawVforkSyscall");
 173  long rawvforksyscall_stub(long trap, long a1, long a2, long a3) {
 174      long r;
 175      __asm__ __volatile__("syscall" : "=a"(r) : "a"(trap), "D"(a1), "S"(a2), "d"(a3) : "rcx", "r11", "memory");
 176      if (r < 0 && r > -4096) return -r;
 177      return r;
 178  }
 179  void* makestrongfromweak_stub(void* p) __asm__("weak.runtime_makeStrongFromWeak");
 180  void* makestrongfromweak_stub(void* p) { return p; }
 181  STUBEOF
 182  clang -c -O2 -ffunction-sections -fdata-sections -o "$OUTDIR/asm_stubs.o" "$TMPDIR/asm_stubs.c"
 183  echo "  asm_stubs.o: $(du -h "$OUTDIR/asm_stubs.o" | cut -f1)" >&2
 184  
 185  echo "=== Step 6: Build C runtime ===" >&2
 186  RTOBJS=()
 187  for f in "$MOXIEROOT"/src/runtime/*.c; do
 188      case "$(basename "$f")" in *_windows*|*_darwin*|*_arm64*|*_other*|*_baremetal*|runtime_wasm*) continue ;; esac
 189      out="$TMPDIR/$(basename "$f").o"
 190      clang -c -O2 -ffunction-sections -fdata-sections -o "$out" "$f" 2>/dev/null && RTOBJS+=("$out")
 191  done
 192  for f in "$MOXIEROOT"/src/runtime/*.S; do
 193      case "$(basename "$f")" in *_arm64*|*_windows*|*_darwin*) continue ;; esac
 194      out="$TMPDIR/$(basename "$f").o"
 195      clang -c -o "$out" "$f" 2>/dev/null && RTOBJS+=("$out")
 196  done
 197  for dir in "$MOXIEROOT"/src/internal/internal/futex "$MOXIEROOT"/src/internal/cpu \
 198             "$MOXIEROOT"/src/internal/runtime/syscall "$MOXIEROOT"/src/syscall; do
 199      [ -d "$dir" ] || continue
 200      for f in "$dir"/*.c; do
 201          [ -f "$f" ] || continue
 202          case "$(basename "$f")" in *_windows*|*_darwin*|*_arm64*|*_other*|*_baremetal*) continue ;; esac
 203          out="$TMPDIR/$(basename "$f").o"
 204          clang -c -O2 -ffunction-sections -fdata-sections -o "$out" "$f" 2>/dev/null && RTOBJS+=("$out")
 205      done
 206  done
 207  ld -r -o "$OUTDIR/runtime.o" "${RTOBJS[@]}"
 208  echo "  runtime.o: $(du -h "$OUTDIR/runtime.o" | cut -f1)" >&2
 209  
 210  echo "=== Step 7: Create archive ===" >&2
 211  rm -f "$OUTDIR/libstdlib.a"
 212  ar rcs "$OUTDIR/libstdlib.a" "$OUTDIR/stdlib.o" "$OUTDIR/bdwgc.o" "$OUTDIR/asm_stubs.o"
 213  echo "  libstdlib.a: $(du -h "$OUTDIR/libstdlib.a" | cut -f1)" >&2
 214  
 215  echo "=== Step 8: Install ===" >&2
 216  ISKRA_DIR="$HOME/.local/share/moxie-iskra"
 217  mkdir -p "$ISKRA_DIR"
 218  cp "$OUTDIR/runtime.o" "$ISKRA_DIR/runtime.o"
 219  cp "$OUTDIR/lowered.bc" "$ISKRA_DIR/stdlib.bc"
 220  cp "$OUTDIR/bdwgc.o" "$ISKRA_DIR/bdwgc.o"
 221  cp "$OUTDIR/asm_stubs.o" "$ISKRA_DIR/asm_stubs.o"
 222  echo "  installed: $ISKRA_DIR/runtime.o" >&2
 223  echo "  installed: $ISKRA_DIR/stdlib.bc ($(du -h "$ISKRA_DIR/stdlib.bc" | cut -f1))" >&2
 224  echo "  installed: $ISKRA_DIR/bdwgc.o" >&2
 225  echo "  installed: $ISKRA_DIR/asm_stubs.o" >&2
 226  
 227  echo "=== Done ===" >&2
 228  echo "Archive: $OUTDIR/libstdlib.a" >&2
 229  echo "Bitcode: $ISKRA_DIR/stdlib.bc" >&2
 230  echo "Runtime: $ISKRA_DIR/runtime.o" >&2
 231  rm -rf "$TMPDIR"
 232