#!/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 ${W65816_CC_EXTRA:-} -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
