qapi: Improve specificity of type/member descriptions
[qemu/armbru.git] / accel / tcg / translator.c
blob7bda43ff61c720f535d25fc94bb7ae803c06d8ce
1 /*
2 * Generic intermediate code generation.
4 * Copyright (C) 2016-2017 LluĂ­s Vilanova <vilanova@ac.upc.edu>
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 */
10 #include "qemu/osdep.h"
11 #include "qemu/error-report.h"
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"
18 #include "exec/plugin-gen.h"
19 #include "exec/replay-core.h"
21 bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
23 /* Suppress goto_tb if requested. */
24 if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
25 return false;
28 /* Check for the dest on the same page as the start of the TB. */
29 return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
32 void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
33 target_ulong pc, void *host_pc,
34 const TranslatorOps *ops, DisasContextBase *db)
36 uint32_t cflags = tb_cflags(tb);
37 bool plugin_enabled;
39 /* Initialize DisasContext */
40 db->tb = tb;
41 db->pc_first = pc;
42 db->pc_next = pc;
43 db->is_jmp = DISAS_NEXT;
44 db->num_insns = 0;
45 db->max_insns = *max_insns;
46 db->singlestep_enabled = cflags & CF_SINGLE_STEP;
47 db->host_addr[0] = host_pc;
48 db->host_addr[1] = NULL;
50 #ifdef CONFIG_USER_ONLY
51 page_protect(pc);
52 #endif
54 ops->init_disas_context(db, cpu);
55 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
57 /* Start translating. */
58 gen_tb_start(db->tb);
59 ops->tb_start(db, cpu);
60 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
62 plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
64 while (true) {
65 *max_insns = ++db->num_insns;
66 ops->insn_start(db, cpu);
67 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
69 if (plugin_enabled) {
70 plugin_gen_insn_start(cpu, db);
73 /* Disassemble one instruction. The translate_insn hook should
74 update db->pc_next and db->is_jmp to indicate what should be
75 done next -- either exiting this loop or locate the start of
76 the next instruction. */
77 if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
78 /* Accept I/O on the last instruction. */
79 gen_io_start();
80 ops->translate_insn(db, cpu);
81 } else {
82 /* we should only see CF_MEMI_ONLY for io_recompile */
83 tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
84 ops->translate_insn(db, cpu);
88 * We can't instrument after instructions that change control
89 * flow although this only really affects post-load operations.
91 * Calling plugin_gen_insn_end() before we possibly stop translation
92 * is important. Even if this ends up as dead code, plugin generation
93 * needs to see a matching plugin_gen_insn_{start,end}() pair in order
94 * to accurately track instrumented helpers that might access memory.
96 if (plugin_enabled) {
97 plugin_gen_insn_end();
100 /* Stop translation if translate_insn so indicated. */
101 if (db->is_jmp != DISAS_NEXT) {
102 break;
105 /* Stop translation if the output buffer is full,
106 or we have executed all of the allowed instructions. */
107 if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
108 db->is_jmp = DISAS_TOO_MANY;
109 break;
113 /* Emit code to exit the TB, as indicated by db->is_jmp. */
114 ops->tb_stop(db, cpu);
115 gen_tb_end(db->tb, db->num_insns);
117 if (plugin_enabled) {
118 plugin_gen_tb_end(cpu);
121 /* The disas_log hook may use these values rather than recompute. */
122 tb->size = db->pc_next - db->pc_first;
123 tb->icount = db->num_insns;
125 #ifdef DEBUG_DISAS
126 if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
127 && qemu_log_in_addr_range(db->pc_first)) {
128 FILE *logfile = qemu_log_trylock();
129 if (logfile) {
130 fprintf(logfile, "----------------\n");
131 ops->disas_log(db, cpu, logfile);
132 fprintf(logfile, "\n");
133 qemu_log_unlock(logfile);
136 #endif
139 static void *translator_access(CPUArchState *env, DisasContextBase *db,
140 target_ulong pc, size_t len)
142 void *host;
143 target_ulong base, end;
144 TranslationBlock *tb;
146 tb = db->tb;
148 /* Use slow path if first page is MMIO. */
149 if (unlikely(tb_page_addr0(tb) == -1)) {
150 return NULL;
153 end = pc + len - 1;
154 if (likely(is_same_page(db, end))) {
155 host = db->host_addr[0];
156 base = db->pc_first;
157 } else {
158 host = db->host_addr[1];
159 base = TARGET_PAGE_ALIGN(db->pc_first);
160 if (host == NULL) {
161 tb_page_addr_t phys_page =
162 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
165 * If the second page is MMIO, treat as if the first page
166 * was MMIO as well, so that we do not cache the TB.
168 if (unlikely(phys_page == -1)) {
169 tb_set_page_addr0(tb, -1);
170 return NULL;
173 tb_set_page_addr1(tb, phys_page);
174 #ifdef CONFIG_USER_ONLY
175 page_protect(end);
176 #endif
177 host = db->host_addr[1];
180 /* Use slow path when crossing pages. */
181 if (is_same_page(db, pc)) {
182 return NULL;
186 tcg_debug_assert(pc >= base);
187 return host + (pc - base);
190 uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
192 uint8_t ret;
193 void *p = translator_access(env, db, pc, sizeof(ret));
195 if (p) {
196 plugin_insn_append(pc, p, sizeof(ret));
197 return ldub_p(p);
199 ret = cpu_ldub_code(env, pc);
200 plugin_insn_append(pc, &ret, sizeof(ret));
201 return ret;
204 uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
206 uint16_t ret, plug;
207 void *p = translator_access(env, db, pc, sizeof(ret));
209 if (p) {
210 plugin_insn_append(pc, p, sizeof(ret));
211 return lduw_p(p);
213 ret = cpu_lduw_code(env, pc);
214 plug = tswap16(ret);
215 plugin_insn_append(pc, &plug, sizeof(ret));
216 return ret;
219 uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
221 uint32_t ret, plug;
222 void *p = translator_access(env, db, pc, sizeof(ret));
224 if (p) {
225 plugin_insn_append(pc, p, sizeof(ret));
226 return ldl_p(p);
228 ret = cpu_ldl_code(env, pc);
229 plug = tswap32(ret);
230 plugin_insn_append(pc, &plug, sizeof(ret));
231 return ret;
234 uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
236 uint64_t ret, plug;
237 void *p = translator_access(env, db, pc, sizeof(ret));
239 if (p) {
240 plugin_insn_append(pc, p, sizeof(ret));
241 return ldq_p(p);
243 ret = cpu_ldq_code(env, pc);
244 plug = tswap64(ret);
245 plugin_insn_append(pc, &plug, sizeof(ret));
246 return ret;