summaryrefslogtreecommitdiff
path: root/tools/encode.sh
diff options
context:
space:
mode:
Diffstat (limited to 'tools/encode.sh')
-rwxr-xr-xtools/encode.sh55
1 files changed, 55 insertions, 0 deletions
diff --git a/tools/encode.sh b/tools/encode.sh
new file mode 100755
index 0000000..bbb1483
--- /dev/null
+++ b/tools/encode.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# Encoding oracle for the ESP32-P4's vendor ISA extensions (xespv / xesploop, "PIE").
+#
+# Upstream LLVM - and therefore Zig's integrated assembler - cannot spell these mnemonics, so
+# inline assembly does not close the gap: it goes through the same assembler and fails the same way.
+#
+# asm volatile ("esp.vld.128.ip q0, a0, 16")
+# -> error: <inline asm>:1:2: unrecognized instruction mnemonic
+#
+# But the encodings are just words, and `.insn` places any word into the instruction stream. So
+# assemble once, offline, with a toolchain that does know the mnemonics (Espressif's GAS), and bake
+# the words into the source. The vendor toolchain becomes a build-time oracle, not a runtime
+# dependency: nothing ships, `zig build` never invokes it, and each word sits next to the mnemonic
+# it came from. Registers named by the encoding must then be pinned by hand ("{a0}" constraints),
+# because LLVM cannot see through a `.insn`.
+#
+# Usage: tools/encode.sh 'esp.vld.128.ip q0, a0, 16' ['esp.movx.w.sar a1' ...]
+# tools/encode.sh -f list.txt # one instruction per line, # comments ok
+#
+# Output: 0x0201223b .insn 4, 0x0201223b // esp.vld.128.ip q0, a0, 16
+#
+# The -march string is load-bearing: the vendor extension is versioned and the versions do not
+# share encodings. `esp.vld.128.ip q0, a0, 16` is 0x0311009f under `xespv` (defaults to 1.0) and
+# 0x0201223b under `xespv2p1`. The P4 wants the latter - it is the pair in the .riscv.attributes of
+# every ESP-IDF image for this chip (xesploop1p0_xespv2p1), and 0x0201223b is what Espressif's
+# objdump reads back as this mnemonic out of such an image.
+
+set -e
+
+MARCH=rv32imafc_xespv2p1_xesploop1p0
+
+AS=$(ls "$HOME"/.espressif/tools/riscv32-esp-elf/*/riscv32-esp-elf/bin/riscv32-esp-elf-as 2>/dev/null | head -1)
+OD=$(ls "$HOME"/.espressif/tools/riscv32-esp-elf/*/riscv32-esp-elf/bin/riscv32-esp-elf-objdump 2>/dev/null | head -1)
+[ -x "$AS" ] || { echo "no Espressif as found (needed only to generate encodings, never to build)" >&2; exit 1; }
+
+TMP=$(mktemp -d)
+trap 'rm -rf "$TMP"' EXIT
+
+encode() {
+ printf '%s\n' "$1" > "$TMP/in.S"
+ if "$AS" -march="$MARCH" -o "$TMP/out.o" "$TMP/in.S" 2>"$TMP/err"; then
+ word=$("$OD" -d "$TMP/out.o" | awk '/^[[:space:]]*0:/ {print $2; exit}')
+ printf '0x%-10s .insn 4, 0x%s // %s\n' "$word" "$word" "$1"
+ else
+ printf '%-12s // %s -- %s\n' "REJECTED" "$1" "$(sed -n '2s/.*Error: //p' "$TMP/err")"
+ fi
+}
+
+if [ "$1" = "-f" ]; then
+ sed 's/#.*//; s/^[[:space:]]*//; s/[[:space:]]*$//; /^$/d' "$2" | while IFS= read -r line; do
+ encode "$line"
+ done
+else
+ for insn in "$@"; do encode "$insn"; done
+fi