]> git.proxmox.com Git - mirror_qemu.git/blame - accel/tcg/translator.c
Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
[mirror_qemu.git] / accel / tcg / translator.c
CommitLineData
bb2e0039
LV
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"
bb2e0039
LV
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"
6ba6f818 19#include "exec/plugin-gen.h"
bb2e0039
LV
20
21/* Pairs with tcg_clear_temp_count.
22 To be called by #TranslatorOps.{translate_insn,tb_stop} if
23 (1) the target is sufficiently clean to support reporting,
24 (2) as and when all temporaries are known to be consumed.
25 For most targets, (2) is at the end of translate_insn. */
26void translator_loop_temp_check(DisasContextBase *db)
27{
28 if (tcg_check_temp_count()) {
29 qemu_log("warning: TCG temporary leaks before "
30 TARGET_FMT_lx "\n", db->pc_next);
31 }
32}
33
34void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
8b86d6d2 35 CPUState *cpu, TranslationBlock *tb, int max_insns)
bb2e0039 36{
f9f1f56e 37 int bp_insn = 0;
6ba6f818 38 bool plugin_enabled;
f9f1f56e 39
bb2e0039
LV
40 /* Initialize DisasContext */
41 db->tb = tb;
42 db->pc_first = tb->pc;
43 db->pc_next = db->pc_first;
44 db->is_jmp = DISAS_NEXT;
45 db->num_insns = 0;
8b86d6d2 46 db->max_insns = max_insns;
bb2e0039
LV
47 db->singlestep_enabled = cpu->singlestep_enabled;
48
b542683d 49 ops->init_disas_context(db, cpu);
bb2e0039
LV
50 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
51
52 /* Reset the temp count so that we can identify leaks */
53 tcg_clear_temp_count();
54
55 /* Start translating. */
56 gen_tb_start(db->tb);
57 ops->tb_start(db, cpu);
58 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
59
6ba6f818
EC
60 plugin_enabled = plugin_gen_tb_start(cpu, tb);
61
bb2e0039
LV
62 while (true) {
63 db->num_insns++;
64 ops->insn_start(db, cpu);
65 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
66
6ba6f818
EC
67 if (plugin_enabled) {
68 plugin_gen_insn_start(cpu, db);
69 }
70
bb2e0039 71 /* Pass breakpoint hits to target for further processing */
f9f1f56e
PD
72 if (!db->singlestep_enabled
73 && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
bb2e0039
LV
74 CPUBreakpoint *bp;
75 QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
76 if (bp->pc == db->pc_next) {
77 if (ops->breakpoint_check(db, cpu, bp)) {
f9f1f56e 78 bp_insn = 1;
bb2e0039
LV
79 break;
80 }
81 }
82 }
83 /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
84 that only one more instruction is to be executed. Otherwise
85 it should use DISAS_NORETURN when generating an exception,
86 but may use a DISAS_TARGET_* value for Something Else. */
87 if (db->is_jmp > DISAS_TOO_MANY) {
88 break;
89 }
90 }
91
92 /* Disassemble one instruction. The translate_insn hook should
93 update db->pc_next and db->is_jmp to indicate what should be
94 done next -- either exiting this loop or locate the start of
95 the next instruction. */
b542683d
EC
96 if (db->num_insns == db->max_insns
97 && (tb_cflags(db->tb) & CF_LAST_IO)) {
bb2e0039
LV
98 /* Accept I/O on the last instruction. */
99 gen_io_start();
100 ops->translate_insn(db, cpu);
bb2e0039
LV
101 } else {
102 ops->translate_insn(db, cpu);
103 }
104
105 /* Stop translation if translate_insn so indicated. */
106 if (db->is_jmp != DISAS_NEXT) {
107 break;
108 }
109
6ba6f818
EC
110 /*
111 * We can't instrument after instructions that change control
112 * flow although this only really affects post-load operations.
113 */
114 if (plugin_enabled) {
115 plugin_gen_insn_end();
116 }
117
bb2e0039
LV
118 /* Stop translation if the output buffer is full,
119 or we have executed all of the allowed instructions. */
b542683d 120 if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
bb2e0039
LV
121 db->is_jmp = DISAS_TOO_MANY;
122 break;
123 }
124 }
125
126 /* Emit code to exit the TB, as indicated by db->is_jmp. */
127 ops->tb_stop(db, cpu);
f9f1f56e 128 gen_tb_end(db->tb, db->num_insns - bp_insn);
bb2e0039 129
6ba6f818
EC
130 if (plugin_enabled) {
131 plugin_gen_tb_end(cpu);
132 }
133
bb2e0039
LV
134 /* The disas_log hook may use these values rather than recompute. */
135 db->tb->size = db->pc_next - db->pc_first;
136 db->tb->icount = db->num_insns;
137
138#ifdef DEBUG_DISAS
139 if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
140 && qemu_log_in_addr_range(db->pc_first)) {
fc59d2d8 141 FILE *logfile = qemu_log_lock();
bb2e0039
LV
142 qemu_log("----------------\n");
143 ops->disas_log(db, cpu);
144 qemu_log("\n");
fc59d2d8 145 qemu_log_unlock(logfile);
bb2e0039
LV
146 }
147#endif
148}