qemu/accel/tcg/translator.c
<<
>>
Prefs
   1/*
   2 * Generic intermediate code generation.
   3 *
   4 * Copyright (C) 2016-2017 LluĂ­s Vilanova <vilanova@ac.upc.edu>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "qemu/error-report.h"
  12#include "cpu.h"
  13#include "tcg/tcg.h"
  14#include "tcg/tcg-op.h"
  15#include "exec/exec-all.h"
  16#include "exec/gen-icount.h"
  17#include "exec/log.h"
  18#include "exec/translator.h"
  19#include "exec/plugin-gen.h"
  20#include "trace-tcg.h"
  21
  22/* Pairs with tcg_clear_temp_count.
  23   To be called by #TranslatorOps.{translate_insn,tb_stop} if
  24   (1) the target is sufficiently clean to support reporting,
  25   (2) as and when all temporaries are known to be consumed.
  26   For most targets, (2) is at the end of translate_insn.  */
  27void translator_loop_temp_check(DisasContextBase *db)
  28{
  29    if (tcg_check_temp_count()) {
  30        qemu_log("warning: TCG temporary leaks before "
  31                 TARGET_FMT_lx "\n", db->pc_next);
  32    }
  33}
  34
  35static TCGOp *gen_trace_tb_enter(TranslationBlock *tb)
  36{
  37    TCGOp *last_pc_op;
  38
  39    TCGv pc_end = tcg_temp_new();
  40
  41    /* The last PC value is not known yet */
  42    tcg_gen_movi_tl(pc_end, 0xdeadbeef);
  43    last_pc_op = tcg_last_op();
  44
  45    trace_tb_enter_tcg(tcg_ctx->cpu, cpu_env, tb->pc, pc_end);
  46    tcg_temp_free(pc_end);
  47
  48    return last_pc_op;
  49}
  50
  51void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
  52                     CPUState *cpu, TranslationBlock *tb, int max_insns)
  53{
  54    TCGOp *trace_pc_end;
  55    int bp_insn = 0;
  56    bool plugin_enabled;
  57
  58    /* Initialize DisasContext */
  59    db->tb = tb;
  60    db->pc_first = tb->pc;
  61    db->pc_next = db->pc_first;
  62    db->is_jmp = DISAS_NEXT;
  63    db->num_insns = 0;
  64    db->max_insns = max_insns;
  65    db->singlestep_enabled = cpu->singlestep_enabled;
  66
  67    ops->init_disas_context(db, cpu);
  68    tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
  69
  70    /* Reset the temp count so that we can identify leaks */
  71    tcg_clear_temp_count();
  72
  73    /* Start translating.  */
  74    gen_tb_start(db->tb);
  75
  76    trace_pc_end = gen_trace_tb_enter(tb);
  77
  78    ops->tb_start(db, cpu);
  79    tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
  80
  81    plugin_enabled = plugin_gen_tb_start(cpu, tb);
  82
  83    while (true) {
  84        db->num_insns++;
  85        ops->insn_start(db, cpu);
  86        tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
  87
  88        if (plugin_enabled) {
  89            plugin_gen_insn_start(cpu, db);
  90        }
  91
  92        /* Pass breakpoint hits to target for further processing */
  93        if (!db->singlestep_enabled
  94            && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
  95            CPUBreakpoint *bp;
  96            QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
  97                if (bp->pc == db->pc_next) {
  98                    if (ops->breakpoint_check(db, cpu, bp)) {
  99                        bp_insn = 1;
 100                        break;
 101                    }
 102                }
 103            }
 104            /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
 105               that only one more instruction is to be executed.  Otherwise
 106               it should use DISAS_NORETURN when generating an exception,
 107               but may use a DISAS_TARGET_* value for Something Else.  */
 108            if (db->is_jmp > DISAS_TOO_MANY) {
 109                break;
 110            }
 111        }
 112
 113        /* Disassemble one instruction.  The translate_insn hook should
 114           update db->pc_next and db->is_jmp to indicate what should be
 115           done next -- either exiting this loop or locate the start of
 116           the next instruction.  */
 117        if (db->num_insns == db->max_insns
 118            && (tb_cflags(db->tb) & CF_LAST_IO)) {
 119            /* Accept I/O on the last instruction.  */
 120            gen_io_start();
 121            ops->translate_insn(db, cpu);
 122        } else {
 123            ops->translate_insn(db, cpu);
 124        }
 125
 126        /* Stop translation if translate_insn so indicated.  */
 127        if (db->is_jmp != DISAS_NEXT) {
 128            break;
 129        }
 130
 131        /*
 132         * We can't instrument after instructions that change control
 133         * flow although this only really affects post-load operations.
 134         */
 135        if (plugin_enabled) {
 136            plugin_gen_insn_end();
 137        }
 138
 139        /* Stop translation if the output buffer is full,
 140           or we have executed all of the allowed instructions.  */
 141        if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
 142            db->is_jmp = DISAS_TOO_MANY;
 143            break;
 144        }
 145    }
 146
 147    /* Emit code to exit the TB, as indicated by db->is_jmp.  */
 148    ops->tb_stop(db, cpu);
 149    gen_tb_end(db->tb, db->num_insns - bp_insn);
 150
 151    /* Fixup the last PC value in the tb_enter trace now that we know it */
 152    tcg_set_insn_param(trace_pc_end, 1, db->pc_next);
 153
 154    if (plugin_enabled) {
 155        plugin_gen_tb_end(cpu);
 156    }
 157
 158    /* The disas_log hook may use these values rather than recompute.  */
 159    db->tb->size = db->pc_next - db->pc_first;
 160    db->tb->icount = db->num_insns;
 161
 162#ifdef DEBUG_DISAS
 163    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
 164        && qemu_log_in_addr_range(db->pc_first)) {
 165        qemu_log_lock();
 166        qemu_log("----------------\n");
 167        ops->disas_log(db, cpu);
 168        qemu_log("\n");
 169        qemu_log_unlock();
 170    }
 171#endif
 172}
 173