106 lines
4.2 KiB
Bash
Executable file
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
|