]> git.proxmox.com Git - mirror_qemu.git/blame - accel/tcg/translator.c
accel/tcg: Pass max_insn to gen_intermediate_code by pointer
[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 11#include "qemu/error-report.h"
bb2e0039
LV
12#include "tcg/tcg.h"
13#include "tcg/tcg-op.h"
14#include "exec/exec-all.h"
15#include "exec/gen-icount.h"
16#include "exec/log.h"
17#include "exec/translator.h"
6ba6f818 18#include "exec/plugin-gen.h"
5b5968c4 19#include "exec/replay-core.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
d3a2a1d8
RH
34bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
35{
84f15616
RH
36 /* Suppress goto_tb if requested. */
37 if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
38 return false;
39 }
40
d3a2a1d8
RH
41 /* Check for the dest on the same page as the start of the TB. */
42 return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
43}
44
597f9b2d 45void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
306c8721
RH
46 target_ulong pc, void *host_pc,
47 const TranslatorOps *ops, DisasContextBase *db)
bb2e0039 48{
d40c5c79 49 uint32_t cflags = tb_cflags(tb);
6ba6f818 50 bool plugin_enabled;
f9f1f56e 51
bb2e0039
LV
52 /* Initialize DisasContext */
53 db->tb = tb;
306c8721
RH
54 db->pc_first = pc;
55 db->pc_next = pc;
bb2e0039
LV
56 db->is_jmp = DISAS_NEXT;
57 db->num_insns = 0;
597f9b2d 58 db->max_insns = *max_insns;
c2ffd754 59 db->singlestep_enabled = cflags & CF_SINGLE_STEP;
50627f1b
RH
60 db->host_addr[0] = host_pc;
61 db->host_addr[1] = NULL;
62
63#ifdef CONFIG_USER_ONLY
64 page_protect(pc);
65#endif
bb2e0039 66
b542683d 67 ops->init_disas_context(db, cpu);
bb2e0039
LV
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 ops->tb_start(db, cpu);
76 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
77
b21af662 78 plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
6ba6f818 79
bb2e0039
LV
80 while (true) {
81 db->num_insns++;
82 ops->insn_start(db, cpu);
83 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
84
6ba6f818
EC
85 if (plugin_enabled) {
86 plugin_gen_insn_start(cpu, db);
87 }
88
bb2e0039
LV
89 /* Disassemble one instruction. The translate_insn hook should
90 update db->pc_next and db->is_jmp to indicate what should be
91 done next -- either exiting this loop or locate the start of
92 the next instruction. */
d40c5c79 93 if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
bb2e0039
LV
94 /* Accept I/O on the last instruction. */
95 gen_io_start();
96 ops->translate_insn(db, cpu);
bb2e0039 97 } else {
cfd405ea 98 /* we should only see CF_MEMI_ONLY for io_recompile */
d40c5c79 99 tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
bb2e0039
LV
100 ops->translate_insn(db, cpu);
101 }
102
6ba6f818
EC
103 /*
104 * We can't instrument after instructions that change control
105 * flow although this only really affects post-load operations.
0f92d94a
EC
106 *
107 * Calling plugin_gen_insn_end() before we possibly stop translation
108 * is important. Even if this ends up as dead code, plugin generation
109 * needs to see a matching plugin_gen_insn_{start,end}() pair in order
110 * to accurately track instrumented helpers that might access memory.
6ba6f818
EC
111 */
112 if (plugin_enabled) {
113 plugin_gen_insn_end();
114 }
115
0f92d94a
EC
116 /* Stop translation if translate_insn so indicated. */
117 if (db->is_jmp != DISAS_NEXT) {
118 break;
119 }
120
bb2e0039
LV
121 /* Stop translation if the output buffer is full,
122 or we have executed all of the allowed instructions. */
b542683d 123 if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
bb2e0039
LV
124 db->is_jmp = DISAS_TOO_MANY;
125 break;
126 }
127 }
128
129 /* Emit code to exit the TB, as indicated by db->is_jmp. */
130 ops->tb_stop(db, cpu);
10c37828 131 gen_tb_end(db->tb, db->num_insns);
bb2e0039 132
6ba6f818
EC
133 if (plugin_enabled) {
134 plugin_gen_tb_end(cpu);
135 }
136
bb2e0039 137 /* The disas_log hook may use these values rather than recompute. */
d9971435
RH
138 tb->size = db->pc_next - db->pc_first;
139 tb->icount = db->num_insns;
bb2e0039
LV
140
141#ifdef DEBUG_DISAS
142 if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
143 && qemu_log_in_addr_range(db->pc_first)) {
c60f599b 144 FILE *logfile = qemu_log_trylock();
78b54858
RH
145 if (logfile) {
146 fprintf(logfile, "----------------\n");
8eb806a7 147 ops->disas_log(db, cpu, logfile);
78b54858
RH
148 fprintf(logfile, "\n");
149 qemu_log_unlock(logfile);
150 }
bb2e0039
LV
151 }
152#endif
153}
f025692c 154
50627f1b
RH
155static void *translator_access(CPUArchState *env, DisasContextBase *db,
156 target_ulong pc, size_t len)
f025692c 157{
50627f1b
RH
158 void *host;
159 target_ulong base, end;
160 TranslationBlock *tb;
161
162 tb = db->tb;
f025692c 163
50627f1b 164 /* Use slow path if first page is MMIO. */
28905cfb 165 if (unlikely(tb_page_addr0(tb) == -1)) {
50627f1b 166 return NULL;
f025692c 167 }
50627f1b
RH
168
169 end = pc + len - 1;
170 if (likely(is_same_page(db, end))) {
171 host = db->host_addr[0];
172 base = db->pc_first;
173 } else {
174 host = db->host_addr[1];
175 base = TARGET_PAGE_ALIGN(db->pc_first);
176 if (host == NULL) {
28905cfb 177 tb_page_addr_t phys_page =
50627f1b 178 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
2627e452
RH
179
180 /*
181 * If the second page is MMIO, treat as if the first page
182 * was MMIO as well, so that we do not cache the TB.
183 */
184 if (unlikely(phys_page == -1)) {
185 tb_set_page_addr0(tb, -1);
186 return NULL;
187 }
188
28905cfb 189 tb_set_page_addr1(tb, phys_page);
50627f1b
RH
190#ifdef CONFIG_USER_ONLY
191 page_protect(end);
f025692c 192#endif
50627f1b
RH
193 host = db->host_addr[1];
194 }
195
196 /* Use slow path when crossing pages. */
197 if (is_same_page(db, pc)) {
198 return NULL;
199 }
200 }
201
202 tcg_debug_assert(pc >= base);
203 return host + (pc - base);
f025692c
IL
204}
205
50627f1b
RH
206uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
207{
208 uint8_t ret;
209 void *p = translator_access(env, db, pc, sizeof(ret));
210
211 if (p) {
212 plugin_insn_append(pc, p, sizeof(ret));
213 return ldub_p(p);
f025692c 214 }
50627f1b
RH
215 ret = cpu_ldub_code(env, pc);
216 plugin_insn_append(pc, &ret, sizeof(ret));
217 return ret;
218}
f025692c 219
50627f1b
RH
220uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
221{
222 uint16_t ret, plug;
223 void *p = translator_access(env, db, pc, sizeof(ret));
f025692c 224
50627f1b
RH
225 if (p) {
226 plugin_insn_append(pc, p, sizeof(ret));
227 return lduw_p(p);
228 }
229 ret = cpu_lduw_code(env, pc);
230 plug = tswap16(ret);
231 plugin_insn_append(pc, &plug, sizeof(ret));
232 return ret;
233}
234
235uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
236{
237 uint32_t ret, plug;
238 void *p = translator_access(env, db, pc, sizeof(ret));
239
240 if (p) {
241 plugin_insn_append(pc, p, sizeof(ret));
242 return ldl_p(p);
243 }
244 ret = cpu_ldl_code(env, pc);
245 plug = tswap32(ret);
246 plugin_insn_append(pc, &plug, sizeof(ret));
247 return ret;
248}
249
250uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
251{
252 uint64_t ret, plug;
253 void *p = translator_access(env, db, pc, sizeof(ret));
254
255 if (p) {
256 plugin_insn_append(pc, p, sizeof(ret));
257 return ldq_p(p);
258 }
259 ret = cpu_ldq_code(env, pc);
260 plug = tswap64(ret);
261 plugin_insn_append(pc, &plug, sizeof(ret));
262 return ret;
263}