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