xref: /qemu/accel/tcg/translator.c (revision 2627e4524ea6c6ba14f9d6b298e08c9d4d3cc4fe)
1bb2e0039SLluís Vilanova /*
2bb2e0039SLluís Vilanova  * Generic intermediate code generation.
3bb2e0039SLluís Vilanova  *
4bb2e0039SLluís Vilanova  * Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu>
5bb2e0039SLluís Vilanova  *
6bb2e0039SLluís Vilanova  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7bb2e0039SLluís Vilanova  * See the COPYING file in the top-level directory.
8bb2e0039SLluís Vilanova  */
9bb2e0039SLluís Vilanova 
10bb2e0039SLluís Vilanova #include "qemu/osdep.h"
11bb2e0039SLluís Vilanova #include "qemu/error-report.h"
12bb2e0039SLluís Vilanova #include "tcg/tcg.h"
13bb2e0039SLluís Vilanova #include "tcg/tcg-op.h"
14bb2e0039SLluís Vilanova #include "exec/exec-all.h"
15bb2e0039SLluís Vilanova #include "exec/gen-icount.h"
16bb2e0039SLluís Vilanova #include "exec/log.h"
17bb2e0039SLluís Vilanova #include "exec/translator.h"
186ba6f818SEmilio G. Cota #include "exec/plugin-gen.h"
19fda8458bSPavel Dovgalyuk #include "sysemu/replay.h"
20bb2e0039SLluís Vilanova 
21bb2e0039SLluís Vilanova /* Pairs with tcg_clear_temp_count.
22bb2e0039SLluís Vilanova    To be called by #TranslatorOps.{translate_insn,tb_stop} if
23bb2e0039SLluís Vilanova    (1) the target is sufficiently clean to support reporting,
24bb2e0039SLluís Vilanova    (2) as and when all temporaries are known to be consumed.
25bb2e0039SLluís Vilanova    For most targets, (2) is at the end of translate_insn.  */
26bb2e0039SLluís Vilanova void translator_loop_temp_check(DisasContextBase *db)
27bb2e0039SLluís Vilanova {
28bb2e0039SLluís Vilanova     if (tcg_check_temp_count()) {
29bb2e0039SLluís Vilanova         qemu_log("warning: TCG temporary leaks before "
30bb2e0039SLluís Vilanova                  TARGET_FMT_lx "\n", db->pc_next);
31bb2e0039SLluís Vilanova     }
32bb2e0039SLluís Vilanova }
33bb2e0039SLluís Vilanova 
34d3a2a1d8SRichard Henderson bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
35d3a2a1d8SRichard Henderson {
3684f15616SRichard Henderson     /* Suppress goto_tb if requested. */
3784f15616SRichard Henderson     if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
3884f15616SRichard Henderson         return false;
3984f15616SRichard Henderson     }
4084f15616SRichard Henderson 
41d3a2a1d8SRichard Henderson     /* Check for the dest on the same page as the start of the TB.  */
42d3a2a1d8SRichard Henderson     return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
43d3a2a1d8SRichard Henderson }
44d3a2a1d8SRichard Henderson 
45306c8721SRichard Henderson void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns,
46306c8721SRichard Henderson                      target_ulong pc, void *host_pc,
47306c8721SRichard Henderson                      const TranslatorOps *ops, DisasContextBase *db)
48bb2e0039SLluís Vilanova {
49d40c5c79SRichard Henderson     uint32_t cflags = tb_cflags(tb);
506ba6f818SEmilio G. Cota     bool plugin_enabled;
51f9f1f56eSPavel Dovgalyuk 
52bb2e0039SLluís Vilanova     /* Initialize DisasContext */
53bb2e0039SLluís Vilanova     db->tb = tb;
54306c8721SRichard Henderson     db->pc_first = pc;
55306c8721SRichard Henderson     db->pc_next = pc;
56bb2e0039SLluís Vilanova     db->is_jmp = DISAS_NEXT;
57bb2e0039SLluís Vilanova     db->num_insns = 0;
588b86d6d2SRichard Henderson     db->max_insns = max_insns;
59c2ffd754SRichard Henderson     db->singlestep_enabled = cflags & CF_SINGLE_STEP;
6050627f1bSRichard Henderson     db->host_addr[0] = host_pc;
6150627f1bSRichard Henderson     db->host_addr[1] = NULL;
6250627f1bSRichard Henderson 
6350627f1bSRichard Henderson #ifdef CONFIG_USER_ONLY
6450627f1bSRichard Henderson     page_protect(pc);
6550627f1bSRichard Henderson #endif
66bb2e0039SLluís Vilanova 
67b542683dSEmilio G. Cota     ops->init_disas_context(db, cpu);
68bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
69bb2e0039SLluís Vilanova 
70bb2e0039SLluís Vilanova     /* Reset the temp count so that we can identify leaks */
71bb2e0039SLluís Vilanova     tcg_clear_temp_count();
72bb2e0039SLluís Vilanova 
73bb2e0039SLluís Vilanova     /* Start translating.  */
74bb2e0039SLluís Vilanova     gen_tb_start(db->tb);
75bb2e0039SLluís Vilanova     ops->tb_start(db, cpu);
76bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
77bb2e0039SLluís Vilanova 
78b21af662SRichard Henderson     plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
796ba6f818SEmilio G. Cota 
80bb2e0039SLluís Vilanova     while (true) {
81bb2e0039SLluís Vilanova         db->num_insns++;
82bb2e0039SLluís Vilanova         ops->insn_start(db, cpu);
83bb2e0039SLluís Vilanova         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
84bb2e0039SLluís Vilanova 
856ba6f818SEmilio G. Cota         if (plugin_enabled) {
866ba6f818SEmilio G. Cota             plugin_gen_insn_start(cpu, db);
876ba6f818SEmilio G. Cota         }
886ba6f818SEmilio G. Cota 
89bb2e0039SLluís Vilanova         /* Disassemble one instruction.  The translate_insn hook should
90bb2e0039SLluís Vilanova            update db->pc_next and db->is_jmp to indicate what should be
91bb2e0039SLluís Vilanova            done next -- either exiting this loop or locate the start of
92bb2e0039SLluís Vilanova            the next instruction.  */
93d40c5c79SRichard Henderson         if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
94bb2e0039SLluís Vilanova             /* Accept I/O on the last instruction.  */
95bb2e0039SLluís Vilanova             gen_io_start();
96bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
97bb2e0039SLluís Vilanova         } else {
98cfd405eaSAlex Bennée             /* we should only see CF_MEMI_ONLY for io_recompile */
99d40c5c79SRichard Henderson             tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
100bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
101bb2e0039SLluís Vilanova         }
102bb2e0039SLluís Vilanova 
1036ba6f818SEmilio G. Cota         /*
1046ba6f818SEmilio G. Cota          * We can't instrument after instructions that change control
1056ba6f818SEmilio G. Cota          * flow although this only really affects post-load operations.
1060f92d94aSEmilio Cota          *
1070f92d94aSEmilio Cota          * Calling plugin_gen_insn_end() before we possibly stop translation
1080f92d94aSEmilio Cota          * is important. Even if this ends up as dead code, plugin generation
1090f92d94aSEmilio Cota          * needs to see a matching plugin_gen_insn_{start,end}() pair in order
1100f92d94aSEmilio Cota          * to accurately track instrumented helpers that might access memory.
1116ba6f818SEmilio G. Cota          */
1126ba6f818SEmilio G. Cota         if (plugin_enabled) {
1136ba6f818SEmilio G. Cota             plugin_gen_insn_end();
1146ba6f818SEmilio G. Cota         }
1156ba6f818SEmilio G. Cota 
1160f92d94aSEmilio Cota         /* Stop translation if translate_insn so indicated.  */
1170f92d94aSEmilio Cota         if (db->is_jmp != DISAS_NEXT) {
1180f92d94aSEmilio Cota             break;
1190f92d94aSEmilio Cota         }
1200f92d94aSEmilio Cota 
121bb2e0039SLluís Vilanova         /* Stop translation if the output buffer is full,
122bb2e0039SLluís Vilanova            or we have executed all of the allowed instructions.  */
123b542683dSEmilio G. Cota         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
124bb2e0039SLluís Vilanova             db->is_jmp = DISAS_TOO_MANY;
125bb2e0039SLluís Vilanova             break;
126bb2e0039SLluís Vilanova         }
127bb2e0039SLluís Vilanova     }
128bb2e0039SLluís Vilanova 
129bb2e0039SLluís Vilanova     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
130bb2e0039SLluís Vilanova     ops->tb_stop(db, cpu);
13110c37828SRichard Henderson     gen_tb_end(db->tb, db->num_insns);
132bb2e0039SLluís Vilanova 
1336ba6f818SEmilio G. Cota     if (plugin_enabled) {
1346ba6f818SEmilio G. Cota         plugin_gen_tb_end(cpu);
1356ba6f818SEmilio G. Cota     }
1366ba6f818SEmilio G. Cota 
137bb2e0039SLluís Vilanova     /* The disas_log hook may use these values rather than recompute.  */
138d9971435SRichard Henderson     tb->size = db->pc_next - db->pc_first;
139d9971435SRichard Henderson     tb->icount = db->num_insns;
140bb2e0039SLluís Vilanova 
141bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS
142bb2e0039SLluís Vilanova     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
143bb2e0039SLluís Vilanova         && qemu_log_in_addr_range(db->pc_first)) {
144c60f599bSRichard Henderson         FILE *logfile = qemu_log_trylock();
14578b54858SRichard Henderson         if (logfile) {
14678b54858SRichard Henderson             fprintf(logfile, "----------------\n");
1478eb806a7SRichard Henderson             ops->disas_log(db, cpu, logfile);
14878b54858SRichard Henderson             fprintf(logfile, "\n");
149fc59d2d8SRobert Foley             qemu_log_unlock(logfile);
150bb2e0039SLluís Vilanova         }
15178b54858SRichard Henderson     }
152bb2e0039SLluís Vilanova #endif
153bb2e0039SLluís Vilanova }
154f025692cSIlya Leoshkevich 
15550627f1bSRichard Henderson static void *translator_access(CPUArchState *env, DisasContextBase *db,
156f025692cSIlya Leoshkevich                                target_ulong pc, size_t len)
157f025692cSIlya Leoshkevich {
15850627f1bSRichard Henderson     void *host;
15950627f1bSRichard Henderson     target_ulong base, end;
16050627f1bSRichard Henderson     TranslationBlock *tb;
16150627f1bSRichard Henderson 
16250627f1bSRichard Henderson     tb = db->tb;
16350627f1bSRichard Henderson 
16450627f1bSRichard Henderson     /* Use slow path if first page is MMIO. */
16528905cfbSRichard Henderson     if (unlikely(tb_page_addr0(tb) == -1)) {
16650627f1bSRichard Henderson         return NULL;
16750627f1bSRichard Henderson     }
16850627f1bSRichard Henderson 
16950627f1bSRichard Henderson     end = pc + len - 1;
17050627f1bSRichard Henderson     if (likely(is_same_page(db, end))) {
17150627f1bSRichard Henderson         host = db->host_addr[0];
17250627f1bSRichard Henderson         base = db->pc_first;
17350627f1bSRichard Henderson     } else {
17450627f1bSRichard Henderson         host = db->host_addr[1];
17550627f1bSRichard Henderson         base = TARGET_PAGE_ALIGN(db->pc_first);
17650627f1bSRichard Henderson         if (host == NULL) {
17728905cfbSRichard Henderson             tb_page_addr_t phys_page =
17850627f1bSRichard Henderson                 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
179*2627e452SRichard Henderson 
180*2627e452SRichard Henderson             /*
181*2627e452SRichard Henderson              * If the second page is MMIO, treat as if the first page
182*2627e452SRichard Henderson              * was MMIO as well, so that we do not cache the TB.
183*2627e452SRichard Henderson              */
184*2627e452SRichard Henderson             if (unlikely(phys_page == -1)) {
185*2627e452SRichard Henderson                 tb_set_page_addr0(tb, -1);
186*2627e452SRichard Henderson                 return NULL;
187*2627e452SRichard Henderson             }
188*2627e452SRichard Henderson 
18928905cfbSRichard Henderson             tb_set_page_addr1(tb, phys_page);
190f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY
19150627f1bSRichard Henderson             page_protect(end);
192f025692cSIlya Leoshkevich #endif
19350627f1bSRichard Henderson             host = db->host_addr[1];
194f025692cSIlya Leoshkevich         }
195f025692cSIlya Leoshkevich 
19650627f1bSRichard Henderson         /* Use slow path when crossing pages. */
19750627f1bSRichard Henderson         if (is_same_page(db, pc)) {
19850627f1bSRichard Henderson             return NULL;
19950627f1bSRichard Henderson         }
200f025692cSIlya Leoshkevich     }
201f025692cSIlya Leoshkevich 
20250627f1bSRichard Henderson     tcg_debug_assert(pc >= base);
20350627f1bSRichard Henderson     return host + (pc - base);
20450627f1bSRichard Henderson }
205f025692cSIlya Leoshkevich 
20650627f1bSRichard Henderson uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
20750627f1bSRichard Henderson {
20850627f1bSRichard Henderson     uint8_t ret;
20950627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
21050627f1bSRichard Henderson 
21150627f1bSRichard Henderson     if (p) {
21250627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
21350627f1bSRichard Henderson         return ldub_p(p);
21450627f1bSRichard Henderson     }
21550627f1bSRichard Henderson     ret = cpu_ldub_code(env, pc);
21650627f1bSRichard Henderson     plugin_insn_append(pc, &ret, sizeof(ret));
21750627f1bSRichard Henderson     return ret;
21850627f1bSRichard Henderson }
21950627f1bSRichard Henderson 
22050627f1bSRichard Henderson uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
22150627f1bSRichard Henderson {
22250627f1bSRichard Henderson     uint16_t ret, plug;
22350627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
22450627f1bSRichard Henderson 
22550627f1bSRichard Henderson     if (p) {
22650627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
22750627f1bSRichard Henderson         return lduw_p(p);
22850627f1bSRichard Henderson     }
22950627f1bSRichard Henderson     ret = cpu_lduw_code(env, pc);
23050627f1bSRichard Henderson     plug = tswap16(ret);
23150627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
23250627f1bSRichard Henderson     return ret;
23350627f1bSRichard Henderson }
23450627f1bSRichard Henderson 
23550627f1bSRichard Henderson uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
23650627f1bSRichard Henderson {
23750627f1bSRichard Henderson     uint32_t ret, plug;
23850627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
23950627f1bSRichard Henderson 
24050627f1bSRichard Henderson     if (p) {
24150627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
24250627f1bSRichard Henderson         return ldl_p(p);
24350627f1bSRichard Henderson     }
24450627f1bSRichard Henderson     ret = cpu_ldl_code(env, pc);
24550627f1bSRichard Henderson     plug = tswap32(ret);
24650627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
24750627f1bSRichard Henderson     return ret;
24850627f1bSRichard Henderson }
24950627f1bSRichard Henderson 
25050627f1bSRichard Henderson uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
25150627f1bSRichard Henderson {
25250627f1bSRichard Henderson     uint64_t ret, plug;
25350627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
25450627f1bSRichard Henderson 
25550627f1bSRichard Henderson     if (p) {
25650627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
25750627f1bSRichard Henderson         return ldq_p(p);
25850627f1bSRichard Henderson     }
25950627f1bSRichard Henderson     ret = cpu_ldq_code(env, pc);
26050627f1bSRichard Henderson     plug = tswap64(ret);
26150627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
26250627f1bSRichard Henderson     return ret;
26350627f1bSRichard Henderson }
264