65816-llvm-mos/scripts/bench.sh
Scott Duensing f338d93bae Checkpoint
2026-05-02 18:30:15 -05:00

106 lines
4.2 KiB
Bash
Executable file

#!/usr/bin/env bash
# bench.sh — compile a benchmark suite with both clang (this toolchain)
# and Calypsi cc65816, compare emitted code size.
#
# Each benchmark is a self-contained .c file under benchmarks/. We
# compile each with both toolchains (-O2 / --speed), then count
# bytes in the .text + .data sections of the resulting object.
# Output is a markdown table on stdout.
#
# Cycle-time comparison would require running each benchmark in MAME
# under both toolchains' produced code, with a wrapper function that
# instruments the cycle counter. That's a separate, more involved
# tool — left for future work.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
BENCH_DIR="$PROJECT_ROOT/benchmarks"
CLANG="$PROJECT_ROOT/tools/llvm-mos-build/bin/clang"
CALYPSI="$PROJECT_ROOT/tools/calypsi/usr/local/lib/calypsi-65816-5.16/bin/cc65816"
[ -x "$CLANG" ] || { echo "ERROR: clang not built" >&2; exit 1; }
[ -x "$CALYPSI" ] || { echo "ERROR: Calypsi not installed" >&2; exit 1; }
[ -d "$BENCH_DIR" ] || { echo "ERROR: $BENCH_DIR not found" >&2; exit 1; }
# Object-size measurement. Different object formats — for clang it's
# ELF (use llvm-readobj), for Calypsi it's its own format (use the
# binary file size as a proxy, minus header overhead). ELF .text +
# .rodata + .data covers code + constants; we report code-only as the
# primary metric.
clangSize() {
local o="$1"
"$PROJECT_ROOT/tools/llvm-mos-build/bin/llvm-readobj" --section-headers "$o" \
2>/dev/null | awk '
/Name: .text/ { intext=1; inrodata=0; indata=0; next }
/Name: .rodata/ { intext=0; inrodata=1; indata=0; next }
/Name: .data/ { intext=0; inrodata=0; indata=1; next }
/Name: / { intext=0; inrodata=0; indata=0; next }
/Size:/ {
if (intext) text += strtonum($2)
if (inrodata) rodata += strtonum($2)
if (indata) data += strtonum($2)
}
END { print text " " rodata " " data }
'
}
# Calypsi text size: extract the highest farcode offset from the
# assembler listing. cc65816 -> .s, then as65816 --list-file
# emits "OFFSET hexbytes" columns; we pick the max offset and add
# the byte width of the final instruction (1-3 bytes typically).
# Approximation but within a byte or two of true text size.
calypsiTextSize() {
local src="$1"
local s lst tmp
s=$(mktemp --suffix=.s)
lst=$(mktemp --suffix=.lst)
tmp=$(mktemp --suffix=.o)
"$CALYPSI" -O 2 --speed --assembly-source "$s" -c "$src" -o "$tmp" 2>/dev/null \
|| { echo 0; rm -f "$s" "$lst" "$tmp"; return; }
"$CALYPSI" -O 2 --speed -c "$src" -o "$tmp" 2>/dev/null
"$PROJECT_ROOT/tools/calypsi/usr/local/lib/calypsi-65816-5.16/bin/as65816" \
--list-file "$lst" -o "$tmp" "$s" 2>/dev/null
# Highest farcode offset. We skip the +instruction-bytes detail
# (rough estimate is fine for relative comparison).
local maxOff
maxOff=$(grep -oE "^[0-9]+ [0-9a-f]{6}" "$lst" 2>/dev/null \
| awk '{print strtonum("0x"$2)}' | sort -n | tail -1)
echo "${maxOff:-0}"
rm -f "$s" "$lst" "$tmp"
}
# Print markdown header.
printf '| Benchmark | clang (B) | Calypsi (B) | clang vs Calypsi |\n'
printf '|-----------|----------:|------------:|-----------------:|\n'
totalClang=0
totalCalypsi=0
for src in "$BENCH_DIR"/*.c; do
name=$(basename "$src" .c)
cObj=$(mktemp --suffix=.clang.o)
"$CLANG" --target=w65816 -O2 -ffunction-sections \
-c "$src" -o "$cObj" 2>/dev/null || { echo "clang failed on $name" >&2; rm -f "$cObj"; continue; }
read clangText _ _ < <(clangSize "$cObj")
clangText=${clangText:-0}
calText=$(calypsiTextSize "$src")
if [ "$calText" -gt 0 ]; then
ratio=$(awk -v a="$clangText" -v b="$calText" 'BEGIN{printf "%.2fx", a/b}')
else
ratio="—"
fi
printf '| %s | %d | %d | %s |\n' "$name" "$clangText" "$calText" "$ratio"
totalClang=$((totalClang + clangText))
totalCalypsi=$((totalCalypsi + calText))
rm -f "$cObj"
done
if [ "$totalCalypsi" -gt 0 ]; then
totalRatio=$(awk -v a="$totalClang" -v b="$totalCalypsi" 'BEGIN{printf "%.2fx", a/b}')
printf '| **total** | **%d** | **%d** | **%s** |\n' "$totalClang" "$totalCalypsi" "$totalRatio"
fi