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.
10 #include "qemu/osdep.h"
11 #include "qemu/error-report.h"
13 #include "tcg/tcg-op.h"
14 #include "exec/exec-all.h"
15 #include "exec/gen-icount.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
) {
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
);
39 /* Initialize DisasContext */
43 db
->is_jmp
= DISAS_NEXT
;
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
54 ops
->init_disas_context(db
, cpu
);
55 tcg_debug_assert(db
->is_jmp
== DISAS_NEXT
); /* no early exit */
57 /* Start translating. */
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
);
65 *max_insns
= ++db
->num_insns
;
66 ops
->insn_start(db
, cpu
);
67 tcg_debug_assert(db
->is_jmp
== DISAS_NEXT
); /* no early exit */
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. */
80 ops
->translate_insn(db
, cpu
);
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.
97 plugin_gen_insn_end();
100 /* Stop translation if translate_insn so indicated. */
101 if (db
->is_jmp
!= DISAS_NEXT
) {
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
;
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
;
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();
130 fprintf(logfile
, "----------------\n");
131 ops
->disas_log(db
, cpu
, logfile
);
132 fprintf(logfile
, "\n");
133 qemu_log_unlock(logfile
);
139 static void *translator_access(CPUArchState
*env
, DisasContextBase
*db
,
140 target_ulong pc
, size_t len
)
143 target_ulong base
, end
;
144 TranslationBlock
*tb
;
148 /* Use slow path if first page is MMIO. */
149 if (unlikely(tb_page_addr0(tb
) == -1)) {
154 if (likely(is_same_page(db
, end
))) {
155 host
= db
->host_addr
[0];
158 host
= db
->host_addr
[1];
159 base
= TARGET_PAGE_ALIGN(db
->pc_first
);
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);
173 tb_set_page_addr1(tb
, phys_page
);
174 #ifdef CONFIG_USER_ONLY
177 host
= db
->host_addr
[1];
180 /* Use slow path when crossing pages. */
181 if (is_same_page(db
, pc
)) {
186 tcg_debug_assert(pc
>= base
);
187 return host
+ (pc
- base
);
190 uint8_t translator_ldub(CPUArchState
*env
, DisasContextBase
*db
, abi_ptr pc
)
193 void *p
= translator_access(env
, db
, pc
, sizeof(ret
));
196 plugin_insn_append(pc
, p
, sizeof(ret
));
199 ret
= cpu_ldub_code(env
, pc
);
200 plugin_insn_append(pc
, &ret
, sizeof(ret
));
204 uint16_t translator_lduw(CPUArchState
*env
, DisasContextBase
*db
, abi_ptr pc
)
207 void *p
= translator_access(env
, db
, pc
, sizeof(ret
));
210 plugin_insn_append(pc
, p
, sizeof(ret
));
213 ret
= cpu_lduw_code(env
, pc
);
215 plugin_insn_append(pc
, &plug
, sizeof(ret
));
219 uint32_t translator_ldl(CPUArchState
*env
, DisasContextBase
*db
, abi_ptr pc
)
222 void *p
= translator_access(env
, db
, pc
, sizeof(ret
));
225 plugin_insn_append(pc
, p
, sizeof(ret
));
228 ret
= cpu_ldl_code(env
, pc
);
230 plugin_insn_append(pc
, &plug
, sizeof(ret
));
234 uint64_t translator_ldq(CPUArchState
*env
, DisasContextBase
*db
, abi_ptr pc
)
237 void *p
= translator_access(env
, db
, pc
, sizeof(ret
));
240 plugin_insn_append(pc
, p
, sizeof(ret
));
243 ret
= cpu_ldq_code(env
, pc
);
245 plugin_insn_append(pc
, &plug
, sizeof(ret
));