o1_quality.sh (5351B)
1 #!/usr/bin/env bash 2 # o1_quality.sh -- A/B harness for the -O1 code-quality work (doc/plan/O1.md). 3 # 4 # Compiles a fixed ecosystem corpus at -O1 -c with a kit binary and reports, per 5 # file, the __TEXT byte size and the emitted instruction count. Optionally diffs 6 # against a baseline kit binary and/or against clang -O1. 7 # 8 # Usage: 9 # scripts/o1_quality.sh [--kit PATH] [--baseline PATH] [--clang] [--arch A] 10 # [--insn-mix SYM] [name ...] 11 # 12 # --kit PATH kit binary to measure (default: build/release/kit) 13 # --baseline PATH second kit binary; report fixed-vs-baseline deltas 14 # --clang also compile with clang and report kit/clang ratios 15 # --arch A aa64|x64|rv64 (default aa64 -> aarch64 native on Apple Si) 16 # --insn-mix SYM for the given symbol, dump the per-mnemonic count delta 17 # name ... restrict to these corpus entries (default: all) 18 # 19 # Pitfalls (see O1.md sec.1): a kit binary copied outside build/ fails with 20 # "support dir not found", so keep A/B binaries inside build/release/. 21 set -euo pipefail 22 23 ROOT="$(cd "$(dirname "$0")/.." && pwd)" 24 KIT="$ROOT/build/release/kit" 25 BASELINE="" 26 DO_CLANG=0 27 ARCH="aa64" 28 INSN_MIX="" 29 WANT=() 30 31 while [ $# -gt 0 ]; do 32 case "$1" in 33 --kit) KIT="$2"; shift 2 ;; 34 --baseline) BASELINE="$2"; shift 2 ;; 35 --clang) DO_CLANG=1; shift ;; 36 --arch) ARCH="$2"; shift 2 ;; 37 --insn-mix) INSN_MIX="$2"; shift 2 ;; 38 -h|--help) sed -n '2,22p' "$0"; exit 0 ;; 39 *) WANT+=("$1"); shift ;; 40 esac 41 done 42 43 case "$ARCH" in 44 aa64) KTARGET="aarch64-macos"; CTARGET="arm64-apple-macos" ;; 45 x64) KTARGET="x86_64-macos"; CTARGET="x86_64-apple-macos" ;; 46 rv64) KTARGET="riscv64-linux-gnu"; CTARGET="" ;; 47 *) echo "unknown --arch $ARCH" >&2; exit 1 ;; 48 esac 49 50 SDK="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null || true)" 51 WORK="$ROOT/build/o1_quality" 52 mkdir -p "$WORK" 53 54 # corpus: name | relpath-under-srcdir | extra-cflags (use @SRC@ for -I srcdir) 55 corpus() { 56 cat <<EOF 57 lapi|lua|lapi.c|-I@SRC@ 58 lparser|lua|lparser.c|-I@SRC@ 59 lvm|lua|lvm.c|-I@SRC@ 60 sqlite|sqlite|sqlite3.c| 61 cjson|cjson|cJSON.c|-I@SRC@ 62 lz4|lz4|lz4.c|-I@SRC@ 63 miniz|miniz|miniz.c| 64 yyjson|yyjson|yyjson.c|-I@SRC@ 65 tinyexpr|tinyexpr|tinyexpr.c|-I@SRC@ 66 EOF 67 } 68 69 text_bytes() { # $1=objfile -> __text byte count (Mach-O), 0 if absent 70 size -m "$1" 2>/dev/null | awk '/__text/{print $NF; found=1} END{if(!found)print 0}' 71 } 72 insn_count() { # $1=objfile -> emitted instruction count 73 "$KIT" objdump -d "$1" 2>/dev/null | awk '/^[[:space:]]*[0-9a-f]+:\t/{n++} END{print n+0}' 74 } 75 76 compile_kit() { # $1=kitbin $2=src $3=out $4=extra 77 "$1" cc -O1 -std=c11 -target "$KTARGET" --sysroot "$SDK" $4 -c "$2" -o "$3" 2>>"$WORK/err.log" 78 } 79 compile_clang() { # $1=src $2=out $3=extra 80 clang -O1 -std=c11 -target "$CTARGET" -isysroot "$SDK" $3 -c "$1" -o "$2" 2>>"$WORK/err.log" 81 } 82 83 printf '%-10s %12s %10s' "file" "kit_text" "kit_insn" 84 [ -n "$BASELINE" ] && printf ' %12s %8s' "base_text" "dTEXT%" 85 [ "$DO_CLANG" = 1 ] && printf ' %11s %8s' "clang_text" "k/clang" 86 printf '\n' 87 88 : > "$WORK/err.log" 89 tot_k=0; tot_b=0; tot_c=0 90 while IFS='|' read -r name eco rel extra; do 91 [ -n "$name" ] || continue 92 if [ "${#WANT[@]}" -gt 0 ]; then 93 skip=1; for w in "${WANT[@]}"; do [ "$w" = "$name" ] && skip=0; done 94 [ "$skip" = 1 ] && continue 95 fi 96 src_root="$("$ROOT/scripts/ecosystem.sh" srcdir "$eco" 2>/dev/null || true)" 97 [ -n "$src_root" ] || { printf '%-10s (not provisioned)\n' "$name"; continue; } 98 src="$src_root/$rel" 99 [ -f "$src" ] || { printf '%-10s (missing %s)\n' "$name" "$rel"; continue; } 100 ex="${extra//@SRC@/$src_root}" 101 102 ko="$WORK/$name.k.o" 103 compile_kit "$KIT" "$src" "$ko" "$ex" || { printf '%-10s (kit FAILED)\n' "$name"; continue; } 104 kt="$(text_bytes "$ko")"; ki="$(insn_count "$ko")" 105 tot_k=$((tot_k + kt)) 106 printf '%-10s %12s %10s' "$name" "$kt" "$ki" 107 108 if [ -n "$BASELINE" ]; then 109 bo="$WORK/$name.b.o" 110 if compile_kit "$BASELINE" "$src" "$bo" "$ex"; then 111 bt="$(text_bytes "$bo")"; tot_b=$((tot_b + bt)) 112 d="n/a"; [ "$bt" -gt 0 ] && d="$(awk -v k="$kt" -v b="$bt" 'BEGIN{printf "%+.2f", (k-b)*100.0/b}')" 113 printf ' %12s %8s' "$bt" "$d" 114 else 115 printf ' %12s %8s' "FAIL" "-" 116 fi 117 fi 118 if [ "$DO_CLANG" = 1 ] && [ -n "$CTARGET" ]; then 119 co="$WORK/$name.c.o" 120 if compile_clang "$src" "$co" "$ex"; then 121 ct="$(text_bytes "$co")"; tot_c=$((tot_c + ct)) 122 r="n/a"; [ "$ct" -gt 0 ] && r="$(awk -v k="$kt" -v c="$ct" 'BEGIN{printf "%.2f", k*1.0/c}')" 123 printf ' %11s %8s' "$ct" "$r" 124 else 125 printf ' %11s %8s' "FAIL" "-" 126 fi 127 fi 128 printf '\n' 129 130 if [ -n "$INSN_MIX" ]; then 131 "$KIT" objdump -d "$ko" 2>/dev/null | awk -v want="$INSN_MIX" ' 132 $0 ~ ("<" want ">:") {inf=1; next} /^[0-9a-f]+ </{inf=0} 133 inf && /^[[:space:]]*[0-9a-f]+:\t/ {n=split($0,a,"\t"); split(a[3],m," "); c[m[1]]++} 134 END{for(k in c) printf " %-10s %d\n", k, c[k]}' | sort -k2 -rn | head -25 135 fi 136 done < <(corpus) 137 138 printf '%-10s %12s\n' "TOTAL" "$tot_k" 139 [ -n "$BASELINE" ] && [ "$tot_b" -gt 0 ] && \ 140 awk -v k="$tot_k" -v b="$tot_b" 'BEGIN{printf " fixed vs baseline __TEXT: %+.2f%%\n", (k-b)*100.0/b}' 141 [ "$DO_CLANG" = 1 ] && [ "$tot_c" -gt 0 ] && \ 142 awk -v k="$tot_k" -v c="$tot_c" 'BEGIN{printf " kit/clang aggregate __TEXT: %.3fx\n", k*1.0/c}' 143 echo " (errors, if any, in $WORK/err.log)"