#!/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 [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.`; the IR spells it @"flan." # 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=":" '$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"