#!/bin/bash

# For now, only an installed libc is supported.

CC=${CC-avr-gcc}
MCUS=${MCUS-at90s8515 attiny88 atmega8 atmega2560}

ccode=code.out
elf=x.elf
aout=atest.out
out=out.dox

AVRTEST_HOME=$(dirname $(which avrtest))

avrtest="${AVRTEST_HOME}/avrtest_log -q -no-log"

echo "avrtest=$avrtest"

# cycles := Execution time.
# stack  := Stack usage.
fun_run ()
{
    echo "code=$(cat $ccode)"

    local aopt="-I${AVRTEST_HOME} ${AVRTEST_HOME}/exit-$mcu.o"
    local copt="-mmcu=$mcu -Os -std=gnu99 -Wall -Werror"

    $CC bench.c -o $elf $copt $aopt $lopt || exit 1
    $avrtest "-mmcu=$(Mlib $mcu)" $elf > $aout || exit 2

    cycles=$(grep "Total" $aout | awk '{ print $3 }')
    stack=$(grep "Stack (rel)" $aout | awk -F ',|]' '{ print $2 }' | xargs)
    echo "$mcu: cycles=$cycles stack=$stack"
}

# 1 = Symbol
# size1 := size with prologue functions.
# size2 := size w/o prologue functions.
fun_link ()
{
    local sym=$1
    local sopt="-Wl,-u,$sym -nostartfiles"
    local copt="-mmcu=$mcu -Os -std=gnu99 -Wall -Werror"
    local d1="-Wl,--defsym,errno=0"
    local d2="-Wl,--defsym,__prologue_saves__=0,--defsym,__epilogue_restores__=0"
    echo "" | $CC -xc - -o $elf $copt $sopt $d1 $lopt || exit 3
    size1=$(avr-size $elf | grep $elf | awk '{ print $1 }')

    echo "" | $CC -xc - -o $elf $copt $sopt $d1 $d2 $lopt || exit 4
    size2=$(avr-size $elf | grep $elf | awk '{ print $1 }')
    echo "$mcu: size1=$size1 size2=$size2"
}

# 1 = file.txt
# 2... = MCUs
# snip := Code snip as shown in table.
do_func_txt ()
{
    local txt="$1"
    shift

    # Line starts:
    # # -> Ignore (comment)
    # None of these -> Decl @ C-Code @ Symbol
    while read -u 11 p; do
	case "$p" in
	    "#"* | "" ) ;;
	    *)
		echo "line=$p"
		snip=$(cut -d@ -f2 <<< "$p" | sed -e 's:^ *\(.*)\) *$:\1:g')
		echo -n "$(cut -d@ -f1 <<< "$p") " > $ccode
		echo "$snip ;" >> $ccode
		echo "snip=${snip}"
		hash="#"
		case $(awk '{ print $1 }' <<< "$snip") in
		    *_min | *_flt ) hash="" ;;
		esac

		echo "  <tr><td><tt>$hash$snip</tt> $what" >> $out

		for mcu in $*; do
		    # Code sizes.
		    lopt="$(cut -d@ -f4 <<< "$p")"

		    fun_link $(cut -d@ -f3 <<< "$p")

		    echo -n "    <td align=\"right\">" >> $out
		    if [ "$size1" = "$size2" ]; then
			echo -n "$size1" >> $out
		    else
			echo -n "$size1 ($size2)" >> $out
		    fi

		    # Runtime, Stack.
		    fun_run
		    echo "<br>$stack<br>$cycles" >> $out
		done
		;;
	esac
    done 11<$txt
}

# mcu -> arch (kind of).
Mlib ()
{
    md=$($CC -mmcu=$1 --print-multi-directory)
    [ "$md" != "." ] || md="avr2"
    echo $md
}

cc_version=$($CC --version | head -1 | awk '{ print $3 }')

c="" && [ $CC != avr-gcc ] && c="CC=$CC "

cat <<EOF > $out
/* Auto-generated file.  DO NOT EDIT, OR YOUR CHANGES WILL BE LOST!
   This file has been auto-generated by:
   $ ${c}$0 $*
*/

/** \page benchmarks

\section bench_libc A few of libc Functions

avr-gcc version is $cc_version

The size of a function is given in view of all picked up functions. By default
AVR-LibC is compiled with \c -mcall-prologues. In parenthesis the size
without taking into account the code for the prologue and epilogue routines
is shown. Both sizes can coincide when no prologue and epilogue routines
are present, and then only one code size is shown.

- \c #qsort sorts an array of \c char with 100 elements.
- \c double has a size of 4, which plays a role in the float type promotions
   of arguments of varargs functions like \c #sprintf.
- For an overview of the different AVR architectures,
  see <a href="https://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html"
    >avr-gcc: Command Line Options</a>.
<small>
<table>
  <tr><th>Function <th>Units
EOF

for m in $MCUS; do
    echo "    <th>$(Mlib $m)" >> $out
done

what="<td>Flash bytes<br>Stack bytes<br>Cycles"

for f in $*; do
    [ -f "$f" ] || Err "$f: not found"

    do_func_txt $f $MCUS
done

cat <<EOF >> $out
</table>
</small>
*/
EOF

echo "== $out =="
cat "$out"

rm -f -- *.out
