xref: /qemu/accel/tcg/translator.c (revision 0ec8384f)
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 "sysemu/replay.h"
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.  */
26 void 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 
34 bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
35 {
36     /* Suppress goto_tb if requested. */
37     if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
38         return false;
39     }
40 
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 
45 void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns,
46                      target_ulong pc, void *host_pc,
47                      const TranslatorOps *ops, DisasContextBase *db)
48 {
49     uint32_t cflags = tb_cflags(tb);
50     bool plugin_enabled;
51 
52     /* Initialize DisasContext */
53     db->tb = tb;
54     db->pc_first = pc;
55     db->pc_next = pc;
56     db->is_jmp = DISAS_NEXT;
57     db->num_insns = 0;
58     db->max_insns = max_insns;
59     db->singlestep_enabled = cflags & CF_SINGLE_STEP;
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
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     ops->tb_start(db, cpu);
76     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
77 
78     plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
79 
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 
85         if (plugin_enabled) {
86             plugin_gen_insn_start(cpu, db);
87         }
88 
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.  */
93         if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
94             /* Accept I/O on the last instruction.  */
95             gen_io_start();
96             ops->translate_insn(db, cpu);
97         } else {
98             /* we should only see CF_MEMI_ONLY for io_recompile */
99             tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
100             ops->translate_insn(db, cpu);
101         }
102 
103         /*
104          * We can't instrument after instructions that change control
105          * flow although this only really affects post-load operations.
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.
111          */
112         if (plugin_enabled) {
113             plugin_gen_insn_end();
114         }
115 
116         /* Stop translation if translate_insn so indicated.  */
117         if (db->is_jmp != DISAS_NEXT) {
118             break;
119         }
120 
121         /* Stop translation if the output buffer is full,
122            or we have executed all of the allowed instructions.  */
123         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
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);
131     gen_tb_end(db->tb, db->num_insns);
132 
133     if (plugin_enabled) {
134         plugin_gen_tb_end(cpu);
135     }
136 
137     /* The disas_log hook may use these values rather than recompute.  */
138     tb->size = db->pc_next - db->pc_first;
139     tb->icount = db->num_insns;
140 
141 #ifdef DEBUG_DISAS
142     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
143         && qemu_log_in_addr_range(db->pc_first)) {
144         FILE *logfile = qemu_log_trylock();
145         if (logfile) {
146             fprintf(logfile, "----------------\n");
147             ops->disas_log(db, cpu, logfile);
148             fprintf(logfile, "\n");
149             qemu_log_unlock(logfile);
150         }
151     }
152 #endif
153 }
154 
155 static void *translator_access(CPUArchState *env, DisasContextBase *db,
156                                target_ulong pc, size_t len)
157 {
158     void *host;
159     target_ulong base, end;
160     TranslationBlock *tb;
161 
162     tb = db->tb;
163 
164     /* Use slow path if first page is MMIO. */
165     if (unlikely(tb_page_addr0(tb) == -1)) {
166         return NULL;
167     }
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) {
177             tb_page_addr_t phys_page =
178                 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
179             /* We cannot handle MMIO as second page. */
180             assert(phys_page != -1);
181             tb_set_page_addr1(tb, phys_page);
182 #ifdef CONFIG_USER_ONLY
183             page_protect(end);
184 #endif
185             host = db->host_addr[1];
186         }
187 
188         /* Use slow path when crossing pages. */
189         if (is_same_page(db, pc)) {
190             return NULL;
191         }
192     }
193 
194     tcg_debug_assert(pc >= base);
195     return host + (pc - base);
196 }
197 
198 uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
199 {
200     uint8_t ret;
201     void *p = translator_access(env, db, pc, sizeof(ret));
202 
203     if (p) {
204         plugin_insn_append(pc, p, sizeof(ret));
205         return ldub_p(p);
206     }
207     ret = cpu_ldub_code(env, pc);
208     plugin_insn_append(pc, &ret, sizeof(ret));
209     return ret;
210 }
211 
212 uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
213 {
214     uint16_t ret, plug;
215     void *p = translator_access(env, db, pc, sizeof(ret));
216 
217     if (p) {
218         plugin_insn_append(pc, p, sizeof(ret));
219         return lduw_p(p);
220     }
221     ret = cpu_lduw_code(env, pc);
222     plug = tswap16(ret);
223     plugin_insn_append(pc, &plug, sizeof(ret));
224     return ret;
225 }
226 
227 uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
228 {
229     uint32_t ret, plug;
230     void *p = translator_access(env, db, pc, sizeof(ret));
231 
232     if (p) {
233         plugin_insn_append(pc, p, sizeof(ret));
234         return ldl_p(p);
235     }
236     ret = cpu_ldl_code(env, pc);
237     plug = tswap32(ret);
238     plugin_insn_append(pc, &plug, sizeof(ret));
239     return ret;
240 }
241 
242 uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
243 {
244     uint64_t ret, plug;
245     void *p = translator_access(env, db, pc, sizeof(ret));
246 
247     if (p) {
248         plugin_insn_append(pc, p, sizeof(ret));
249         return ldq_p(p);
250     }
251     ret = cpu_ldq_code(env, pc);
252     plug = tswap64(ret);
253     plugin_insn_append(pc, &plug, sizeof(ret));
254     return ret;
255 }
256