Right now translator stops right *after* the end of a page, which
breaks reporting of fault locations when the last instruction of a
multi-insn translation block crosses a page boundary.
An implementation, like the one arm and s390x have, would require an
i386 length disassembler, which is burdensome to maintain. Another
alternative would be to single-step at the end of a guest page, but
this may come with a performance impact.
Fix by snapshotting disassembly state and restoring it after we figure
out we crossed a page boundary. This includes rolling back cc_op
updates and emitted ops. Even though i386 is the only architecture that
does rollback, split it into common and architecture-dependent parts to
improve readability.
Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
---
accel/tcg/translator.c | 8 ++++++++
include/exec/translator.h | 3 +++
target/i386/tcg/translate.c | 21 ++++++++++++++++++++-
3 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index fe7af9b943..2c4dd09df8 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -56,6 +56,7 @@ void translator_loop(const TranslatorOps *ops,
DisasContextBase *db,
{
uint32_t cflags = tb_cflags(tb);
bool plugin_enabled;
+ TCGOp *last_op;
/* Initialize DisasContext */
db->tb = tb;
@@ -82,6 +83,7 @@ void translator_loop(const TranslatorOps *ops,
DisasContextBase *db,
while (true) {
db->num_insns++;
+ last_op = tcg_last_op();
ops->insn_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
@@ -103,6 +105,12 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
ops->translate_insn(db, cpu);
}
+ if (db->is_jmp == DISAS_TOO_MANY_UNDO) {
+ db->num_insns--;
+ tcg_remove_ops_after(last_op);
+ db->is_jmp = DISAS_TOO_MANY;
+ }
+
/* Stop translation if translate_insn so indicated. */
if (db->is_jmp != DISAS_NEXT) {
break;
diff --git a/include/exec/translator.h b/include/exec/translator.h
index d27f8c33b6..e1533aee87 100644
--- a/include/exec/translator.h
+++ b/include/exec/translator.h
@@ -31,6 +31,8 @@
* DisasJumpType:
* @DISAS_NEXT: Next instruction in program order.
* @DISAS_TOO_MANY: Too many instructions translated.
+ * @DISAS_TOO_MANY_UNDO: Too many instructions translated. Everything that was
+ * done for the current instruction must be undone.
* @DISAS_NORETURN: Following code is dead.
* @DISAS_TARGET_*: Start of target-specific conditions.
*
@@ -39,6 +41,7 @@
typedef enum DisasJumpType {
DISAS_NEXT,
DISAS_TOO_MANY,
+ DISAS_TOO_MANY_UNDO,