67 lines
2.8 KiB
Bash
Executable File
67 lines
2.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# Every lowering of one program, side by side: the LLVM IR the frontend emits,
|
|
# what LLVM makes of it at -O0 and at -O2, and what the hand-written backend
|
|
# emits. Reading one against another is the only way to check a lowering by eye,
|
|
# and the whole reason the second backend is trustworthy is that the two agree.
|
|
#
|
|
# $ spike/x86/dump.sh file.flan [name]
|
|
#
|
|
# With a name, each output is narrowed to that function -- which is almost
|
|
# always what you want, because the prelude is emitted too and a two-line
|
|
# program is ten thousand lines of IR. The name is the Flan one: `twice`, not
|
|
# `flan.twice`.
|
|
#
|
|
# Output goes to a directory printed at the end and is not cleaned up; these are
|
|
# files you are going to read several times and diff against each other.
|
|
#
|
|
# FLAN overrides the compiler; the default is the one dune just built. Flags
|
|
# after the name are passed to every stage that understands them, so
|
|
# `dump.sh f.flan twice --dev` compares the four dev lowerings rather than the
|
|
# four release ones.
|
|
set -e
|
|
here=$(cd "$(dirname "$0")" && pwd)
|
|
root=$(cd "$here/../.." && pwd)
|
|
FLAN=${FLAN:-$root/_build/default/bin/main.exe}
|
|
case $FLAN in /*) ;; *) FLAN=$(cd "$(dirname "$FLAN")" && pwd)/$(basename "$FLAN") ;; esac
|
|
|
|
[ -n "$1" ] || { echo "usage: $0 <file.flan> [name] [flags...]" >&2; exit 2; }
|
|
src=$1; shift
|
|
case $1 in ''|-*) fn= ;; *) fn=$1; shift ;; esac
|
|
|
|
out=$(mktemp -d "${TMPDIR:-/tmp}/flan-dump.XXXXXX")
|
|
base=$(basename "$src" .flan)
|
|
|
|
"$FLAN" emit "$src" "$@" > "$out/$base.ll"
|
|
llc -O0 "$out/$base.ll" -o "$out/$base.O0.s"
|
|
llc -O2 "$out/$base.ll" -o "$out/$base.O2.s"
|
|
# The backend writes machine code, not mnemonics, so its .s is .byte blobs.
|
|
# Assembling and disassembling is what makes it readable -- and it is also a
|
|
# check that the bytes are well formed, which reading them never would be.
|
|
"$FLAN" emit --x86 "$src" "$@" > "$out/$base.x86.s"
|
|
as --64 -o "$out/$base.x86.o" "$out/$base.x86.s"
|
|
objdump -d --no-show-raw-insn "$out/$base.x86.o" > "$out/$base.x86.dis"
|
|
|
|
if [ -n "$fn" ]; then
|
|
# LLVM names a Flan function `flan.<name>`; the IR spells it @"flan.<name>"
|
|
# when it needs quoting, and the assembler output as a plain label.
|
|
awk -v f="flan.$fn" '
|
|
$0 ~ "^define .*@\"?" f "\"?\\(" {p=1} p; p && /^}/ {p=0}' \
|
|
"$out/$base.ll" > "$out/$base.fn.ll" || true
|
|
for o in O0 O2; do
|
|
awk -v f="flan.$fn" '
|
|
$0 ~ "^\"?" f "\"?:" {p=1} p; p && /\.size/ {p=0}' \
|
|
"$out/$base.$o.s" > "$out/$base.fn.$o.s" || true
|
|
done
|
|
awk -v f="<flan.$fn>:" '$0 ~ f {p=1} p; p && /^$/ {p=0}' \
|
|
"$out/$base.x86.dis" > "$out/$base.fn.x86" || true
|
|
|
|
for f in "$out/$base.fn.ll" "$out/$base.fn.O0.s" "$out/$base.fn.O2.s" "$out/$base.fn.x86"; do
|
|
[ -s "$f" ] || { echo "nothing for '$fn' in $(basename "$f")" >&2; continue; }
|
|
echo "=== $(basename "$f") ==="
|
|
cat "$f"
|
|
echo
|
|
done
|
|
fi
|
|
|
|
echo "all four in $out"
|