qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC 38/48] translator: implement 2-pass translation


From: Emilio G. Cota
Subject: [Qemu-devel] [RFC 38/48] translator: implement 2-pass translation
Date: Thu, 25 Oct 2018 13:20:47 -0400

The second pass only occurs when a plugin has subscribed to
TB translation events.

Signed-off-by: Emilio G. Cota <address@hidden>
---
 tcg/tcg.h              |  8 ++++
 accel/tcg/translator.c | 91 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/tcg/tcg.h b/tcg/tcg.h
index d5afe25c97..479b57d65f 100644
--- a/tcg/tcg.h
+++ b/tcg/tcg.h
@@ -720,6 +720,14 @@ struct TCGContext {
 
     TCGLabel *exitreq_label;
 
+    /*
+     * We keep one plugin_tb struct per TCGContext. Note that on every TB
+     * translation we clear but do not free its contents; this way we
+     * avoid a lot of malloc/free churn, since after a few TB's it's
+     * unlikely that we'll need to allocate either more instructions or more
+     * space for instructions (for variable-instruction-length ISAs).
+     */
+    struct qemu_plugin_tb plugin_tb;
     struct qemu_plugin_dyn_cb_arr *plugin_mem_cb;
     struct qemu_plugin_insn *plugin_insn;
 
diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index 8591e4b72a..88f9ac62a3 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -17,6 +17,7 @@
 #include "exec/gen-icount.h"
 #include "exec/log.h"
 #include "exec/translator.h"
+#include "exec/plugin-gen.h"
 
 /* Pairs with tcg_clear_temp_count.
    To be called by #TranslatorOps.{translate_insn,tb_stop} if
@@ -35,6 +36,21 @@ void translator_loop(const TranslatorOps *ops, 
DisasContextBase *db,
                      CPUState *cpu, TranslationBlock *tb)
 {
     int bp_insn = 0;
+    int insn_idx = 0;
+    bool tb_trans_cb = false;
+    bool first_pass = true; /* second pass otherwise */
+    void *saved_dc = g_alloca(ops->ctx_size);
+    /* tb->plugin_mask is a u32 */
+    unsigned long plugin_mask = tb->plugin_mask;
+    struct qemu_plugin_tb *plugin_tb = &tcg_ctx->plugin_tb;
+
+    if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, &plugin_mask)) {
+        tb_trans_cb = true;
+        plugin_tb->cbs.n = 0;
+        plugin_tb->n = 0;
+        plugin_tb->vaddr = tb->pc;
+        tcg_ctx->plugin_mem_cb = NULL;
+    }
 
     /* Initialize DisasContext */
     db->tb = tb;
@@ -56,6 +72,21 @@ void translator_loop(const TranslatorOps *ops, 
DisasContextBase *db,
         db->max_insns = 1;
     }
 
+ translate:
+    tcg_func_start(tcg_ctx);
+
+    /* See the "2-pass translation" comment below */
+    if (tb_trans_cb) {
+        void *dc = db;
+
+        dc -= ops->ctx_base_offset;
+        if (first_pass) {
+            memcpy(saved_dc, dc, ops->ctx_size);
+        } else {
+            memcpy(dc, saved_dc, ops->ctx_size);
+        }
+    }
+
     ops->init_disas_context(db, cpu);
     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 
@@ -67,7 +98,53 @@ void translator_loop(const TranslatorOps *ops, 
DisasContextBase *db,
     ops->tb_start(db, cpu);
     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
 
+    if (!first_pass && plugin_tb->cbs.n) {
+        qemu_plugin_gen_vcpu_udata_callbacks(&plugin_tb->cbs);
+    }
+
     while (true) {
+        struct qemu_plugin_insn *plugin_insn = NULL;
+        bool mem_helpers = false;
+
+        /*
+         * 2-pass translation.
+         *
+         * In the first pass we fully determine the TB.
+         * If no plugins have subscribed to TB translation events, we're done.
+         *
+         * If they have, we first share with plugins a TB descriptor so
+         * that plugins can subscribe to instruction-related events, e.g.
+         * memory accesses of particular instructions, or TB execution.
+         * With this info, which is kept in plugin_tb, we then do a second 
pass,
+         * inserting the appropriate instrumentation into the translated TB.
+         *
+         * Since all translation state is kept in DisasContext, we copy it
+         * before the first pass, and restore it before the second.
+         */
+        if (tb_trans_cb) {
+            if (first_pass) {
+                plugin_insn = qemu_plugin_tb_insn_get(plugin_tb);
+                tcg_ctx->plugin_insn = plugin_insn;
+                plugin_insn->vaddr = db->pc_next;
+                g_assert(tcg_ctx->plugin_mem_cb == NULL);
+            } else {
+                struct qemu_plugin_insn *insn = &plugin_tb->insns[insn_idx++];
+
+                tcg_ctx->plugin_insn = NULL;
+                if (unlikely(insn->exec_cbs.n)) {
+                    qemu_plugin_gen_vcpu_udata_callbacks(&insn->exec_cbs);
+                }
+                if (insn->mem_cbs.n) {
+                    tcg_ctx->plugin_mem_cb = &insn->mem_cbs;
+                    if (insn->calls_helpers) {
+                        qemu_plugin_gen_enable_mem_helpers(&insn->mem_cbs);
+                        mem_helpers = true;
+                    }
+                } else {
+                    tcg_ctx->plugin_mem_cb = NULL;
+                }
+            }
+        }
         db->num_insns++;
         ops->insn_start(db, cpu);
         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
@@ -101,10 +178,14 @@ void translator_loop(const TranslatorOps *ops, 
DisasContextBase *db,
             && (tb_cflags(db->tb) & CF_LAST_IO)) {
             /* Accept I/O on the last instruction.  */
             gen_io_start();
-            ops->translate_insn(db, cpu, NULL);
+            ops->translate_insn(db, cpu, plugin_insn);
             gen_io_end();
         } else {
-            ops->translate_insn(db, cpu, NULL);
+            ops->translate_insn(db, cpu, plugin_insn);
+        }
+
+        if (unlikely(mem_helpers)) {
+            qemu_plugin_gen_disable_mem_helpers();
         }
 
         /* Stop translation if translate_insn so indicated.  */
@@ -120,6 +201,12 @@ void translator_loop(const TranslatorOps *ops, 
DisasContextBase *db,
         }
     }
 
+    if (tb_trans_cb && first_pass) {
+        qemu_plugin_tb_trans_cb(cpu, plugin_tb);
+        first_pass = false;
+        goto translate;
+    }
+
     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
     ops->tb_stop(db, cpu);
     gen_tb_end(db->tb, db->num_insns - bp_insn);
-- 
2.17.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]