#!/usr/bin/env bash # Does annotation change a single byte of what the backend emits? # # `flan emit --x86` annotates: a comment per Flan form, a frame map per # function, and a name for each piece of bookkeeping the compiler adds. All of # that is comments, plus the splitting of one long `.byte` directive into # several. Both are supposed to be invisible to the assembler -- and "supposed # to be" is exactly the kind of claim this repo measures rather than asserts, # because the whole licence for annotating at all is that the bytes are the # bytes. # # So: emit every program in the corpus both ways, assemble both, and compare # each section of the two objects byte for byte. No linking and no running -- # survey.sh is what says the programs still behave, and this says nothing they # are built from moved. # # Three settings, because they are three different emitters. The default; --dev, # which adds the indirection cells and the ABI marker; and --debug, which adds # the line table, the labels its rows hang off, and the CFI directives. The # debug case is the sharp one: a `.debug_line` row is an address expressed as a # label, and annotation emits no labels precisely so that those cannot move. # # Usage: spike/x86/annot.sh [name-substring ...] set -u orig=$(pwd) here=$(cd "$(dirname "$0")" && pwd) root=$(cd "$here/../.." && pwd) cd "$root" || exit 1 if [ -n "${FLAN:-}" ]; then case $FLAN in /*) flan=$FLAN;; *) flan=$orig/$FLAN;; esac else dune build --root . bin/main.exe 2>&1 | head -30 flan=$root/_build/default/bin/main.exe fi test -x "$flan" || { echo "build failed"; exit 1; } corpus=${SURVEY_CORPUS:-$root} tmp=$(mktemp -d "${TMPDIR:-/tmp}/flan-annot.XXXXXX") trap 'rm -rf "$tmp"' EXIT same=0; differ=0; skip=0 # Every section either object has, not a fixed list: a section that exists on # one side and not the other is itself a difference, and comparing a named list # would miss one that annotation invented. sections () { objdump -h "$1" | awk '$1 ~ /^[0-9]+$/ { print $2 }' } check () { src=$1; shift name=$(basename "$src" .flan) tag="$name${*:+ $*}" a=$tmp/a.s; b=$tmp/b.s if ! "$flan" emit --x86 "$@" "$src" > "$a" 2>"$tmp/err"; then skip=$((skip + 1)); echo "SKIP $tag"; return fi if ! "$flan" emit --x86 --no-annotate "$@" "$src" > "$b" 2>/dev/null; then skip=$((skip + 1)); echo "SKIP $tag"; return fi if ! as --64 -o "$tmp/a.o" "$a" 2>"$tmp/err"; then differ=$((differ + 1)) echo "BADASM $tag -- the annotated listing does not assemble" head -3 "$tmp/err"; return fi if ! as --64 -o "$tmp/b.o" "$b" 2>/dev/null; then skip=$((skip + 1)); echo "SKIP $tag"; return fi bad= for sec in $(sections "$tmp/a.o"; sections "$tmp/b.o"); do case " $bad " in *" $sec "*) continue;; esac objcopy -O binary --only-section="$sec" "$tmp/a.o" "$tmp/a.bin" 2>/dev/null objcopy -O binary --only-section="$sec" "$tmp/b.o" "$tmp/b.bin" 2>/dev/null cmp -s "$tmp/a.bin" "$tmp/b.bin" || bad="$bad $sec" done if [ -z "$bad" ]; then same=$((same + 1)); echo "SAME $tag" else differ=$((differ + 1)); echo "DIFFER $tag --$bad" fi } for src in "$corpus"/test/programs/*.flan "$corpus"/spike/x86/*.flan; do [ -f "$src" ] || continue if [ $# -gt 0 ]; then hit= for pat in "$@"; do case $src in *"$pat"*) hit=1;; esac; done [ -n "$hit" ] || continue fi check "$src" check "$src" --dev check "$src" --debug done echo echo "$same SAME / $differ DIFFER / $skip SKIP" [ "$differ" -eq 0 ]