#!/bin/bash # Build the stdlib archive for moxie-iskra from a moxie source tree. # Requires: moxie (with -internal-printir), opt, llc, clang, ar, ld # # Usage: MOXIEROOT=../moxie ./tools/build-stdlib-archive.sh [output-dir] # # The target-dir is the moxie project to compile (e.g., ~/s/smesh). # Output goes to output-dir (default: /tmp/iskra-corpus). set -e TARGETDIR="${1:?usage: $0 [output-dir]}" OUTDIR="${2:-/tmp/iskra-corpus}" MOXIEROOT="${MOXIEROOT:?set MOXIEROOT to the moxie compiler source root}" mkdir -p "$OUTDIR" echo "=== Step 1: Generate post-interface-lowering IR ===" >&2 cd "$TARGETDIR" moxie build -internal-printir -o /dev/null . > "$OUTDIR/lowered.ll" 2>/dev/null || true LINES=$(wc -l < "$OUTDIR/lowered.ll") echo " generated $LINES lines of IR" >&2 echo "=== Step 2: Fix visibility ===" >&2 sed -E \ 's/^define internal /define /; s/^define hidden /define /; s/^define dso_local /define /; s/ = internal unnamed_addr / = unnamed_addr /g; s/ = internal constant/ = constant/g; s/ = internal global/ = global/g; s/ = hidden unnamed_addr / = unnamed_addr /g; s/ = hidden constant/ = constant/g; s/ = hidden global/ = global/g' \ "$OUTDIR/lowered.ll" > "$OUTDIR/lowered-fixed.ll" echo "=== Step 2b: Strip app-level (main.*) definitions ===" >&2 # Phase 1: strip app-level (main.*) definitions from the IR. # Keep: init functions, closures ($), string constants (main$string). # Strip: user-declared functions, method receiver functions, globals. awk ' # Strip function defs: @main.func, @(*main.Type).method, @"(*main.Type).method" # But keep closures ($) and init /^define / && /@main\./ && !/@main\.[^(]*\$/ && !/@main\.init[^A-Za-z]/ { skip=1; next } /^define / && /@"main\./ && !/@"main\.[^"]*\$/ && !/@"main\.init[^A-Za-z]/ { skip=1; next } /^define / && /@\(\*main\./ { skip=1; next } /^define / && /@"\(\*main\./ { skip=1; next } /^define / && /@\(main\./ { skip=1; next } skip && /^}/ { skip=0; next } skip { next } # Strip main.* globals and alloc tables /^@main\./ { next } /^@"main\./ { next } /^@"main\$alloc/ { next } { print } ' "$OUTDIR/lowered-fixed.ll" > "$OUTDIR/lowered-stripped.ll" # Phase 2: find references to @main.* and @main$* that are now undefined, emit stubs { grep -oP '@"?main\.[^("{\s,)]+' "$OUTDIR/lowered-stripped.ll" grep -oP '@"main\$[^"]+' "$OUTDIR/lowered-stripped.ll" } | sort -u > "$OUTDIR/_main_refs.txt" { grep -P '^(define|declare) ' "$OUTDIR/lowered-stripped.ll" | grep -oP '@"?main\.[^("{\s,)]+' grep -P '^@"?main[\.\$]' "$OUTDIR/lowered-stripped.ll" | grep -oP '@"?main[\.\$][^("{\s,= ]+' } | sort -u > "$OUTDIR/_main_defined.txt" comm -23 "$OUTDIR/_main_refs.txt" "$OUTDIR/_main_defined.txt" > "$OUTDIR/_main_missing.txt" { cat "$OUTDIR/lowered-stripped.ll" while IFS= read -r sym; do case "$sym" in *'$'*) echo "$sym\" = external global i8" ;; *) echo "declare void $sym(...)" ;; esac done < "$OUTDIR/_main_missing.txt" } > "$OUTDIR/lowered-stdlib.ll" BEFORE=$(grep -c '^define ' "$OUTDIR/lowered-fixed.ll") AFTER=$(grep -c '^define ' "$OUTDIR/lowered-stdlib.ll") STUBS=$(wc -l < "$OUTDIR/_main_missing.txt") echo " functions: $BEFORE -> $AFTER (stripped $((BEFORE - AFTER)) main.* defs, added $STUBS stubs)" >&2 rm -f "$OUTDIR/_main_refs.txt" "$OUTDIR/_main_defined.txt" "$OUTDIR/_main_missing.txt" "$OUTDIR/lowered-stripped.ll" echo "=== Step 3: Compile to object ===" >&2 opt -O2 "$OUTDIR/lowered-stdlib.ll" -o "$OUTDIR/lowered.bc" llc -filetype=obj -O2 --function-sections --data-sections "$OUTDIR/lowered.bc" -o "$OUTDIR/stdlib.o" echo " stdlib.o: $(du -h "$OUTDIR/stdlib.o" | cut -f1)" >&2 echo "=== Step 4: Build vendored bdw-gc ===" >&2 BDWGC="$MOXIEROOT/lib/bdwgc" TMPDIR=$(mktemp -d) GCFLAGS="-DUSE_MMAP -DUSE_MUNMAP -DGC_BUILTIN_ATOMIC -DNO_EXECUTE_PERMISSION" GCFLAGS="$GCFLAGS -DALL_INTERIOR_POINTERS -DIGNORE_DYNAMIC_LOADING -DNO_GETCONTEXT" GCFLAGS="$GCFLAGS -DGC_DISABLE_INCREMENTAL -DNO_MSGBOX_ON_ERROR -DDONT_USE_ATEXIT" GCFLAGS="$GCFLAGS -DNO_GETENV -DNO_CLOCK -DNO_DEBUGGING -DGC_NO_FINALIZATION" GCFLAGS="$GCFLAGS -DGC_DONT_REGISTER_MAIN_STATIC_DATA -DSTACK_NOT_SCANNED" GCFLAGS="$GCFLAGS -DNO_PROC_STAT -DSTACKBOTTOM=0 -I$BDWGC/include -O2 -ffunction-sections -fdata-sections" GCOBJS=() for src in allchblk.c alloc.c blacklst.c dbg_mlc.c dyn_load.c headers.c \ mach_dep.c malloc.c mallocx.c mark.c mark_rts.c misc.c \ new_hblk.c os_dep.c ptr_chck.c reclaim.c; do out="$TMPDIR/$src.o" clang -c $GCFLAGS -o "$out" "$BDWGC/$src" 2>/dev/null GCOBJS+=("$out") done ld -r -o "$OUTDIR/bdwgc.o" "${GCOBJS[@]}" echo " bdwgc.o: $(du -h "$OUTDIR/bdwgc.o" | cut -f1)" >&2 echo "=== Step 5: Build assembly stubs ===" >&2 cat > "$TMPDIR/asm_stubs.c" << 'STUBEOF' #include #include void cpuid_stub(uint32_t eax, uint32_t ecx, uint32_t* result) __asm__("internal/cpu.cpuid"); void cpuid_stub(uint32_t eax, uint32_t ecx, uint32_t* result) { __asm__ __volatile__("cpuid" : "=a"(result[0]), "=b"(result[1]), "=c"(result[2]), "=d"(result[3]) : "a"(eax), "c"(ecx)); } uint64_t xgetbv_stub(uint32_t xcr) __asm__("internal/cpu.xgetbv"); uint64_t xgetbv_stub(uint32_t xcr) { uint32_t lo, hi; __asm__ __volatile__("xgetbv" : "=a"(lo), "=d"(hi) : "c"(xcr)); return ((uint64_t)hi << 32) | lo; } int32_t getgoamd64_stub(void) __asm__("internal/cpu.getGOAMD64level"); int32_t getgoamd64_stub(void) { uint32_t eax, ebx, ecx, edx; __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(7), "c"(0)); if (ebx & (1 << 5)) return 3; __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(1), "c"(0)); if (ecx & (1 << 28)) return 2; return 1; } struct syscall6_result { long r1; long r2; long err; }; struct syscall6_result syscall6_stub(long num, long a1, long a2, long a3, long a4, long a5, long a6) __asm__("internal/runtime/syscall.Syscall6"); struct syscall6_result syscall6_stub(long num, long a1, long a2, long a3, long a4, long a5, long a6) { struct syscall6_result res = {0, 0, 0}; long r; register long r10 __asm__("r10") = a4; register long r8 __asm__("r8") = a5; register long r9 __asm__("r9") = a6; __asm__ __volatile__("syscall" : "=a"(r) : "a"(num), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8), "r"(r9) : "rcx", "r11", "memory"); if (r < 0 && r > -4096) { res.r1 = -1; res.err = -r; } else { res.r1 = r; } return res; } void entersyscall_stub(void) __asm__("runtime.entersyscall"); void entersyscall_stub(void) {} void exitsyscall_stub(void) __asm__("runtime.exitsyscall"); void exitsyscall_stub(void) {} void beforefork_stub(void) __asm__("syscall.runtime_BeforeFork"); void beforefork_stub(void) {} void afterfork_stub(void) __asm__("syscall.runtime_AfterFork"); void afterfork_stub(void) {} void afterforkinchild_stub(void) __asm__("syscall.runtime_AfterForkInChild"); void afterforkinchild_stub(void) {} void doallthreadssyscall_stub(void) __asm__("syscall.runtime_doAllThreadsSyscall"); void doallthreadssyscall_stub(void) {} int32_t haswaitingreaders_stub(void) __asm__("syscall.hasWaitingReaders"); int32_t haswaitingreaders_stub(void) { return 0; } void cgocaller_stub(void) __asm__("syscall.cgocaller"); void cgocaller_stub(void) {} void gettimeofday_stub(struct timeval *tv) __asm__("syscall.gettimeofday"); void gettimeofday_stub(struct timeval *tv) { gettimeofday(tv, 0); } long rawvforksyscall_stub(long trap, long a1, long a2, long a3) __asm__("syscall.rawVforkSyscall"); long rawvforksyscall_stub(long trap, long a1, long a2, long a3) { long r; __asm__ __volatile__("syscall" : "=a"(r) : "a"(trap), "D"(a1), "S"(a2), "d"(a3) : "rcx", "r11", "memory"); if (r < 0 && r > -4096) return -r; return r; } void* makestrongfromweak_stub(void* p) __asm__("weak.runtime_makeStrongFromWeak"); void* makestrongfromweak_stub(void* p) { return p; } STUBEOF clang -c -O2 -ffunction-sections -fdata-sections -o "$OUTDIR/asm_stubs.o" "$TMPDIR/asm_stubs.c" echo " asm_stubs.o: $(du -h "$OUTDIR/asm_stubs.o" | cut -f1)" >&2 echo "=== Step 6: Build C runtime ===" >&2 RTOBJS=() for f in "$MOXIEROOT"/src/runtime/*.c; do case "$(basename "$f")" in *_windows*|*_darwin*|*_arm64*|*_other*|*_baremetal*|runtime_wasm*) continue ;; esac out="$TMPDIR/$(basename "$f").o" clang -c -O2 -ffunction-sections -fdata-sections -o "$out" "$f" 2>/dev/null && RTOBJS+=("$out") done for f in "$MOXIEROOT"/src/runtime/*.S; do case "$(basename "$f")" in *_arm64*|*_windows*|*_darwin*) continue ;; esac out="$TMPDIR/$(basename "$f").o" clang -c -o "$out" "$f" 2>/dev/null && RTOBJS+=("$out") done for dir in "$MOXIEROOT"/src/internal/internal/futex "$MOXIEROOT"/src/internal/cpu \ "$MOXIEROOT"/src/internal/runtime/syscall "$MOXIEROOT"/src/syscall; do [ -d "$dir" ] || continue for f in "$dir"/*.c; do [ -f "$f" ] || continue case "$(basename "$f")" in *_windows*|*_darwin*|*_arm64*|*_other*|*_baremetal*) continue ;; esac out="$TMPDIR/$(basename "$f").o" clang -c -O2 -ffunction-sections -fdata-sections -o "$out" "$f" 2>/dev/null && RTOBJS+=("$out") done done ld -r -o "$OUTDIR/runtime.o" "${RTOBJS[@]}" echo " runtime.o: $(du -h "$OUTDIR/runtime.o" | cut -f1)" >&2 echo "=== Step 7: Create archive ===" >&2 rm -f "$OUTDIR/libstdlib.a" ar rcs "$OUTDIR/libstdlib.a" "$OUTDIR/stdlib.o" "$OUTDIR/bdwgc.o" "$OUTDIR/asm_stubs.o" echo " libstdlib.a: $(du -h "$OUTDIR/libstdlib.a" | cut -f1)" >&2 echo "=== Step 8: Install ===" >&2 ISKRA_DIR="$HOME/.local/share/moxie-iskra" mkdir -p "$ISKRA_DIR" cp "$OUTDIR/runtime.o" "$ISKRA_DIR/runtime.o" cp "$OUTDIR/lowered.bc" "$ISKRA_DIR/stdlib.bc" cp "$OUTDIR/bdwgc.o" "$ISKRA_DIR/bdwgc.o" cp "$OUTDIR/asm_stubs.o" "$ISKRA_DIR/asm_stubs.o" echo " installed: $ISKRA_DIR/runtime.o" >&2 echo " installed: $ISKRA_DIR/stdlib.bc ($(du -h "$ISKRA_DIR/stdlib.bc" | cut -f1))" >&2 echo " installed: $ISKRA_DIR/bdwgc.o" >&2 echo " installed: $ISKRA_DIR/asm_stubs.o" >&2 echo "=== Done ===" >&2 echo "Archive: $OUTDIR/libstdlib.a" >&2 echo "Bitcode: $ISKRA_DIR/stdlib.bc" >&2 echo "Runtime: $ISKRA_DIR/runtime.o" >&2 rm -rf "$TMPDIR"