xref: /qemu/accel/tcg/translator.c (revision 56234233594d05b1092b3cb04de845aeffa27f4c)
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 "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"
20 
21 
22 void gen_io_start(void)
23 {
24     tcg_gen_st_i32(tcg_constant_i32(1), cpu_env,
25                    offsetof(ArchCPU, parent_obj.can_do_io) -
26                    offsetof(ArchCPU, env));
27 }
28 
29 static TCGOp *gen_tb_start(uint32_t cflags)
30 {
31     TCGv_i32 count = tcg_temp_new_i32();
32     TCGOp *icount_start_insn = NULL;
33 
34     tcg_gen_ld_i32(count, cpu_env,
35                    offsetof(ArchCPU, neg.icount_decr.u32) -
36                    offsetof(ArchCPU, env));
37 
38     if (cflags & CF_USE_ICOUNT) {
39         /*
40          * We emit a sub with a dummy immediate argument. Keep the insn index
41          * of the sub so that we later (when we know the actual insn count)
42          * can update the argument with the actual insn count.
43          */
44         tcg_gen_sub_i32(count, count, tcg_constant_i32(0));
45         icount_start_insn = tcg_last_op();
46     }
47 
48     /*
49      * Emit the check against icount_decr.u32 to see if we should exit
50      * unless we suppress the check with CF_NOIRQ. If we are using
51      * icount and have suppressed interruption the higher level code
52      * should have ensured we don't run more instructions than the
53      * budget.
54      */
55     if (cflags & CF_NOIRQ) {
56         tcg_ctx->exitreq_label = NULL;
57     } else {
58         tcg_ctx->exitreq_label = gen_new_label();
59         tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label);
60     }
61 
62     if (cflags & CF_USE_ICOUNT) {
63         tcg_gen_st16_i32(count, cpu_env,
64                          offsetof(ArchCPU, neg.icount_decr.u16.low) -
65                          offsetof(ArchCPU, env));
66         /*
67          * cpu->can_do_io is cleared automatically here at the beginning of
68          * each translation block.  The cost is minimal and only paid for
69          * -icount, plus it would be very easy to forget doing it in the
70          * translator. Doing it here means we don't need a gen_io_end() to
71          * go with gen_io_start().
72          */
73         tcg_gen_st_i32(tcg_constant_i32(0), cpu_env,
74                        offsetof(ArchCPU, parent_obj.can_do_io) -
75                        offsetof(ArchCPU, env));
76     }
77 
78     return icount_start_insn;
79 }
80 
81 static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags,
82                        TCGOp *icount_start_insn, int num_insns)
83 {
84     if (cflags & CF_USE_ICOUNT) {
85         /*
86          * Update the num_insn immediate parameter now that we know
87          * the actual insn count.
88          */
89         tcg_set_insn_param(icount_start_insn, 2,
90                            tcgv_i32_arg(tcg_constant_i32(num_insns)));
91     }
92 
93     if (tcg_ctx->exitreq_label) {
94         gen_set_label(tcg_ctx->exitreq_label);
95         tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED);
96     }
97 }
98 
99 bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
100 {
101     /* Suppress goto_tb if requested. */
102     if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
103         return false;
104     }
105 
106     /* Check for the dest on the same page as the start of the TB.  */
107     return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
108 }
109 
110 void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
111                      target_ulong pc, void *host_pc,
112                      const TranslatorOps *ops, DisasContextBase *db)
113 {
114     uint32_t cflags = tb_cflags(tb);
115     TCGOp *icount_start_insn;
116     bool plugin_enabled;
117 
118     /* Initialize DisasContext */
119     db->tb = tb;
120     db->pc_first = pc;
121     db->pc_next = pc;
122     db->is_jmp = DISAS_NEXT;
123     db->num_insns = 0;
124     db->max_insns = *max_insns;
125     db->singlestep_enabled = cflags & CF_SINGLE_STEP;
126     db->host_addr[0] = host_pc;
127     db->host_addr[1] = NULL;
128 
129 #ifdef CONFIG_USER_ONLY
130     page_protect(pc);
131 #endif
132 
133     ops->init_disas_context(db, cpu);
134     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
135 
136     /* Start translating.  */
137     icount_start_insn = gen_tb_start(cflags);
138     ops->tb_start(db, cpu);
139     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
140 
141     plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
142 
143     while (true) {
144         *max_insns = ++db->num_insns;
145         ops->insn_start(db, cpu);
146         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
147 
148         if (plugin_enabled) {
149             plugin_gen_insn_start(cpu, db);
150         }
151 
152         /* Disassemble one instruction.  The translate_insn hook should
153            update db->pc_next and db->is_jmp to indicate what should be
154            done next -- either exiting this loop or locate the start of
155            the next instruction.  */
156         if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
157             /* Accept I/O on the last instruction.  */
158             gen_io_start();
159             ops->translate_insn(db, cpu);
160         } else {
161             /* we should only see CF_MEMI_ONLY for io_recompile */
162             tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
163             ops->translate_insn(db, cpu);
164         }
165 
166         /*
167          * We can't instrument after instructions that change control
168          * flow although this only really affects post-load operations.
169          *
170          * Calling plugin_gen_insn_end() before we possibly stop translation
171          * is important. Even if this ends up as dead code, plugin generation
172          * needs to see a matching plugin_gen_insn_{start,end}() pair in order
173          * to accurately track instrumented helpers that might access memory.
174          */
175         if (plugin_enabled) {
176             plugin_gen_insn_end();
177         }
178 
179         /* Stop translation if translate_insn so indicated.  */
180         if (db->is_jmp != DISAS_NEXT) {
181             break;
182         }
183 
184         /* Stop translation if the output buffer is full,
185            or we have executed all of the allowed instructions.  */
186         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
187             db->is_jmp = DISAS_TOO_MANY;
188             break;
189         }
190     }
191 
192     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
193     ops->tb_stop(db, cpu);
194     gen_tb_end(tb, cflags, icount_start_insn, db->num_insns);
195 
196     if (plugin_enabled) {
197         plugin_gen_tb_end(cpu);
198     }
199 
200     /* The disas_log hook may use these values rather than recompute.  */
201     tb->size = db->pc_next - db->pc_first;
202     tb->icount = db->num_insns;
203 
204     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
205         && qemu_log_in_addr_range(db->pc_first)) {
206         FILE *logfile = qemu_log_trylock();
207         if (logfile) {
208             fprintf(logfile, "----------------\n");
209             ops->disas_log(db, cpu, logfile);
210             fprintf(logfile, "\n");
211             qemu_log_unlock(logfile);
212         }
213     }
214 }
215 
216 static void *translator_access(CPUArchState *env, DisasContextBase *db,
217                                target_ulong pc, size_t len)
218 {
219     void *host;
220     target_ulong base, end;
221     TranslationBlock *tb;
222 
223     tb = db->tb;
224 
225     /* Use slow path if first page is MMIO. */
226     if (unlikely(tb_page_addr0(tb) == -1)) {
227         return NULL;
228     }
229 
230     end = pc + len - 1;
231     if (likely(is_same_page(db, end))) {
232         host = db->host_addr[0];
233         base = db->pc_first;
234     } else {
235         host = db->host_addr[1];
236         base = TARGET_PAGE_ALIGN(db->pc_first);
237         if (host == NULL) {
238             tb_page_addr_t phys_page =
239                 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
240 
241             /*
242              * If the second page is MMIO, treat as if the first page
243              * was MMIO as well, so that we do not cache the TB.
244              */
245             if (unlikely(phys_page == -1)) {
246                 tb_set_page_addr0(tb, -1);
247                 return NULL;
248             }
249 
250             tb_set_page_addr1(tb, phys_page);
251 #ifdef CONFIG_USER_ONLY
252             page_protect(end);
253 #endif
254             host = db->host_addr[1];
255         }
256 
257         /* Use slow path when crossing pages. */
258         if (is_same_page(db, pc)) {
259             return NULL;
260         }
261     }
262 
263     tcg_debug_assert(pc >= base);
264     return host + (pc - base);
265 }
266 
267 uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
268 {
269     uint8_t ret;
270     void *p = translator_access(env, db, pc, sizeof(ret));
271 
272     if (p) {
273         plugin_insn_append(pc, p, sizeof(ret));
274         return ldub_p(p);
275     }
276     ret = cpu_ldub_code(env, pc);
277     plugin_insn_append(pc, &ret, sizeof(ret));
278     return ret;
279 }
280 
281 uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
282 {
283     uint16_t ret, plug;
284     void *p = translator_access(env, db, pc, sizeof(ret));
285 
286     if (p) {
287         plugin_insn_append(pc, p, sizeof(ret));
288         return lduw_p(p);
289     }
290     ret = cpu_lduw_code(env, pc);
291     plug = tswap16(ret);
292     plugin_insn_append(pc, &plug, sizeof(ret));
293     return ret;
294 }
295 
296 uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
297 {
298     uint32_t ret, plug;
299     void *p = translator_access(env, db, pc, sizeof(ret));
300 
301     if (p) {
302         plugin_insn_append(pc, p, sizeof(ret));
303         return ldl_p(p);
304     }
305     ret = cpu_ldl_code(env, pc);
306     plug = tswap32(ret);
307     plugin_insn_append(pc, &plug, sizeof(ret));
308     return ret;
309 }
310 
311 uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
312 {
313     uint64_t ret, plug;
314     void *p = translator_access(env, db, pc, sizeof(ret));
315 
316     if (p) {
317         plugin_insn_append(pc, p, sizeof(ret));
318         return ldq_p(p);
319     }
320     ret = cpu_ldq_code(env, pc);
321     plug = tswap64(ret);
322     plugin_insn_append(pc, &plug, sizeof(ret));
323     return ret;
324 }
325