The first six probes each proved a piece. merged.sh puts them together: the program's @main is renamed out of the way, a C main takes the main thread and runs it there, caml_startup happens on a thread beside it, and clang links the lot -- the emitted program object, flan_rt.c, flan_dev.c, flan_agent.c and the whole compiler as one -output-complete-obj. It runs, and the compiler inside it compiles the very source the program was built from. Nothing is wired up. The two halves share an address space and do not speak. That is the point: the question was whether they can, not what they would say. sig.sh and symbols.sh answer the two questions the first pass got wrong or skipped. The SIGSEGV reading in harness5.c was taken at the wrong moment -- OCaml 5 starts domains after caml_startup returns, so the disposition had to be read from inside the runtime, and against a plain ocamlopt executable as a control. symbols.sh is the hazard nobody looks for until the link fails: four .c files that are compiled into two different processes today, and the OCaml runtime, all landing in one link.
67 lines
2.6 KiB
Bash
67 lines
2.6 KiB
Bash
#!/usr/bin/env bash
|
|
# Step 8: the thing the whole spike is really asking about -- ONE binary that
|
|
# is both a compiled Flan program and the OCaml compiler, with clang doing the
|
|
# final link.
|
|
#
|
|
# Everything before this proved a piece. This proves the shape: the Flan
|
|
# program's own main() is renamed out of the way, a C main() takes the main
|
|
# thread and runs the program there, and caml_startup happens on a side thread
|
|
# beside it. That is exactly item 11's inversion, built for real.
|
|
#
|
|
# It is NOT the merged architecture -- nothing is wired up, the compiler and the
|
|
# program do not talk. It is a link and a size and a startup number.
|
|
set -u
|
|
here=$(cd "$(dirname "$0")" && pwd)
|
|
root=$(cd "$here/../.." && pwd)
|
|
src=${1:-test/programs/edn.flan}
|
|
cd "$root" || exit 1
|
|
out=$(mktemp -d); trap 'rm -rf "$out"' EXIT
|
|
|
|
FLAN=_build/default/bin/main.exe
|
|
OCAMLLIB=$(ocamlopt -where)
|
|
SYSLIBS="-lm -lpthread -ldl -lzstd"
|
|
|
|
echo "program: $src"
|
|
|
|
# 1. The Flan program, as it is built today, for the baseline sizes.
|
|
"$FLAN" build "$src" -o "$out/rel" || exit 1
|
|
"$FLAN" build "$src" --dev -o "$out/dev" || exit 1
|
|
|
|
# 2. The same program as an object, with its main renamed so a C main can own
|
|
# the process. Emit writes @main literally; sed is enough to move it.
|
|
"$FLAN" emit "$src" --dev > "$out/prog.ll" || exit 1
|
|
sed -i 's/define i32 @main(/define i32 @flan_program_main(/' "$out/prog.ll"
|
|
grep -q 'define i32 @flan_program_main(' "$out/prog.ll" || {
|
|
echo "could not find @main in the emitted IR -- adjust the rename"; exit 1; }
|
|
clang -c -x ir "$out/prog.ll" -o "$out/prog.o" || exit 1
|
|
|
|
# 3. The runtime the program needs, and the agent beside it.
|
|
clang -c -O2 runtime/flan_rt.c -o "$out/rt.o" || exit 1
|
|
clang -c -O2 runtime/flan_dev.c -o "$out/dev.o" || exit 1
|
|
clang -c -O2 vendor/agent/flan_agent.c -o "$out/ag.o" || exit 1
|
|
|
|
# 4. The whole OCaml compiler as one object.
|
|
ocamlfind ocamlopt -thread -package unix,threads.posix -linkpkg \
|
|
-output-complete-obj \
|
|
-I "$root/_build/default/lib/.flan.objs/byte" \
|
|
-I "$root/_build/default/lib/.flan.objs/native" \
|
|
-o "$out/compiler.o" "$root/_build/default/lib/flan.cmxa" \
|
|
"$here/thread_ml.ml" || exit 1
|
|
|
|
# 5. One link. clang, as the project already does it.
|
|
clang -I"$OCAMLLIB" "$here/merged_main.c" "$out/prog.o" "$out/rt.o" "$out/dev.o" \
|
|
"$out/ag.o" "$out/compiler.o" -o "$out/merged" $SYSLIBS || exit 1
|
|
|
|
echo
|
|
echo "sizes:"
|
|
for f in rel dev merged; do
|
|
printf ' %-30s %9d bytes\n' "$f" "$(stat -c%s "$out/$f")"
|
|
done
|
|
printf ' %-30s %9d bytes\n' "what the compiler adds to a dev build" \
|
|
"$(( $(stat -c%s "$out/merged") - $(stat -c%s "$out/dev") ))"
|
|
|
|
echo
|
|
echo "running the merged binary:"
|
|
"$out/merged" "$src"
|
|
echo "exit: $?"
|