]>
Commit | Line | Data |
---|---|---|
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. */ | |
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 | ||
d3a2a1d8 RH |
34 | bool 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 | 45 | void 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 |
155 | static 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 |
206 | uint8_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 |
220 | uint16_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 | ||
235 | uint32_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 | ||
250 | uint64_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 | } |