diff --git a/Makefile b/Makefile index dd0890f..29d93a4 100644 --- a/Makefile +++ b/Makefile @@ -67,11 +67,15 @@ RVOPT_BAD_ENC := \ ebreak:05d00893,00100073 \ ecall-with-rd:05d00893,000000f3 \ ecall-with-rs1:05d00893,00008073 +# The jump-over cases exercise reachability, not the decode gate: an illegal +# word off the reachable path must not change what the reachable path lowers to. RVOPT_OK_ENC := \ JALR:00800093,00008067,05d00893,00000073 \ ret:00c000ef,05d00893,00000073,00008067 \ ecall-exit:05d00893,00000073 \ - ecall-write:04000893,00000073 + ecall-write:04000893,00000073 \ + jump-over-reserved-JALR:0100006f,01400093,00009067,00008067,05d00893,00000073 \ + jump-over-CSR:0100006f,01400093,00001073,00008067,05d00893,00000073 PACK_WORDS = python3 -c 'import sys, struct; sys.stdout.buffer.write(b"".join(struct.pack("/dev/null 2>&1; then \ for c in $(RVOPT_BAD_ENC); do \ - $(PACK_WORDS) "$${c#*:}" > $(TMPDIR)/rvopt-enc.bin; \ + $(PACK_WORDS) "$${c#*:}" > $(TMPDIR)/rvopt-enc.bin \ + || { echo "verify-mux: cannot encode $${c%%:*}"; exit 1; }; \ ! $(RVOPT) mux $(TMPDIR)/rvopt-enc.bin >/dev/null 2>$(TMPDIR)/rvopt-enc.err \ && grep -q 'unsupported op' $(TMPDIR)/rvopt-enc.err \ || { echo "verify-mux: accepted $${c%%:*}"; exit 1; }; \ done; \ for c in $(RVOPT_OK_ENC); do \ - $(PACK_WORDS) "$${c#*:}" > $(TMPDIR)/rvopt-enc.bin; \ + $(PACK_WORDS) "$${c#*:}" > $(TMPDIR)/rvopt-enc.bin \ + || { echo "verify-mux: cannot encode $${c%%:*}"; exit 1; }; \ $(RVOPT) mux $(TMPDIR)/rvopt-enc.bin >/dev/null 2>&1 \ || { echo "verify-mux: rejected $${c%%:*}"; exit 1; }; \ done; \ diff --git a/rvopt.c b/rvopt.c index e4b0797..7997f4d 100644 --- a/rvopt.c +++ b/rvopt.c @@ -321,11 +321,13 @@ static int addr2node(const struct graph *g, uint32_t pc) return (idx >= 1 && idx < g->count) ? idx : NONE; } -/* A control transfer ends a basic block; its successor starts a new one. */ +/* A control transfer ends a basic block, and so does an illegal word (nothing + * runs past one); its successor starts a new one. + */ static bool is_block_end(int kind) { - return kind == K_JAL || kind == K_JALR || kind == K_BRANCH || - kind == K_SYSTEM; + return kind == K_ILL || kind == K_JAL || kind == K_JALR || + kind == K_BRANCH || kind == K_SYSTEM; } /* Build the def-use lists from the vd1/vd2 producer edges: a flat pool indexed @@ -813,7 +815,7 @@ static void resolve_jalr(struct graph *g) * terminates) plus a branch/jump target. Writes up to 2 node indexes into * succ[] and returns the count. Only 'jal ra' and resolved 'jalr ra,...' have a * return site (matching the ret model); 'j', 'jal x5', and runtime JALR do not - * fall through; an ecall exit terminates. + * fall through; an ecall exit terminates, and so does an illegal word. */ static int successors(const struct graph *g, const struct sysinfo *sys, @@ -826,7 +828,7 @@ static int successors(const struct graph *g, const int rd = (nd->word >> 7) & 31; const bool link = rd == 1 && (nd->kind == K_JAL || (nd->kind == K_JALR && nd->target != NONE)); - const bool terminates = (nd->kind == K_JAL && !link) || + const bool terminates = nd->kind == K_ILL || (nd->kind == K_JAL && !link) || (nd->kind == K_JALR && !link) || (nd->kind == K_SYSTEM && sys[i].kind == SYS_EXIT); if (!terminates && fall != NONE)