xref: /qemu/accel/tcg/translator.c (revision 309e014dd10f3e98f4ca8025e7682443d4ce32f4)
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/log.h"
16bb2e0039SLluís Vilanova #include "exec/translator.h"
176ba6f818SEmilio G. Cota #include "exec/plugin-gen.h"
185b5968c4SPhilippe Mathieu-Daudé #include "exec/replay-core.h"
19bb2e0039SLluís Vilanova 
2056234233SRichard Henderson 
21dfd1b812SRichard Henderson static void gen_io_start(void)
2256234233SRichard Henderson {
2356234233SRichard Henderson     tcg_gen_st_i32(tcg_constant_i32(1), cpu_env,
2456234233SRichard Henderson                    offsetof(ArchCPU, parent_obj.can_do_io) -
2556234233SRichard Henderson                    offsetof(ArchCPU, env));
2656234233SRichard Henderson }
2756234233SRichard Henderson 
28dfd1b812SRichard Henderson bool translator_io_start(DisasContextBase *db)
29dfd1b812SRichard Henderson {
30dfd1b812SRichard Henderson     uint32_t cflags = tb_cflags(db->tb);
31dfd1b812SRichard Henderson 
32dfd1b812SRichard Henderson     if (!(cflags & CF_USE_ICOUNT)) {
33dfd1b812SRichard Henderson         return false;
34dfd1b812SRichard Henderson     }
35dfd1b812SRichard Henderson     if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
36dfd1b812SRichard Henderson         /* Already started in translator_loop. */
37dfd1b812SRichard Henderson         return true;
38dfd1b812SRichard Henderson     }
39dfd1b812SRichard Henderson 
40dfd1b812SRichard Henderson     gen_io_start();
41dfd1b812SRichard Henderson 
42dfd1b812SRichard Henderson     /*
43dfd1b812SRichard Henderson      * Ensure that this instruction will be the last in the TB.
44dfd1b812SRichard Henderson      * The target may override this to something more forceful.
45dfd1b812SRichard Henderson      */
46dfd1b812SRichard Henderson     if (db->is_jmp == DISAS_NEXT) {
47dfd1b812SRichard Henderson         db->is_jmp = DISAS_TOO_MANY;
48dfd1b812SRichard Henderson     }
49dfd1b812SRichard Henderson     return true;
50dfd1b812SRichard Henderson }
51dfd1b812SRichard Henderson 
5256234233SRichard Henderson static TCGOp *gen_tb_start(uint32_t cflags)
5356234233SRichard Henderson {
5456234233SRichard Henderson     TCGv_i32 count = tcg_temp_new_i32();
5556234233SRichard Henderson     TCGOp *icount_start_insn = NULL;
5656234233SRichard Henderson 
5756234233SRichard Henderson     tcg_gen_ld_i32(count, cpu_env,
5856234233SRichard Henderson                    offsetof(ArchCPU, neg.icount_decr.u32) -
5956234233SRichard Henderson                    offsetof(ArchCPU, env));
6056234233SRichard Henderson 
6156234233SRichard Henderson     if (cflags & CF_USE_ICOUNT) {
6256234233SRichard Henderson         /*
6356234233SRichard Henderson          * We emit a sub with a dummy immediate argument. Keep the insn index
6456234233SRichard Henderson          * of the sub so that we later (when we know the actual insn count)
6556234233SRichard Henderson          * can update the argument with the actual insn count.
6656234233SRichard Henderson          */
6756234233SRichard Henderson         tcg_gen_sub_i32(count, count, tcg_constant_i32(0));
6856234233SRichard Henderson         icount_start_insn = tcg_last_op();
6956234233SRichard Henderson     }
7056234233SRichard Henderson 
7156234233SRichard Henderson     /*
7256234233SRichard Henderson      * Emit the check against icount_decr.u32 to see if we should exit
7356234233SRichard Henderson      * unless we suppress the check with CF_NOIRQ. If we are using
7456234233SRichard Henderson      * icount and have suppressed interruption the higher level code
7556234233SRichard Henderson      * should have ensured we don't run more instructions than the
7656234233SRichard Henderson      * budget.
7756234233SRichard Henderson      */
7856234233SRichard Henderson     if (cflags & CF_NOIRQ) {
7956234233SRichard Henderson         tcg_ctx->exitreq_label = NULL;
8056234233SRichard Henderson     } else {
8156234233SRichard Henderson         tcg_ctx->exitreq_label = gen_new_label();
8256234233SRichard Henderson         tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label);
8356234233SRichard Henderson     }
8456234233SRichard Henderson 
8556234233SRichard Henderson     if (cflags & CF_USE_ICOUNT) {
8656234233SRichard Henderson         tcg_gen_st16_i32(count, cpu_env,
8756234233SRichard Henderson                          offsetof(ArchCPU, neg.icount_decr.u16.low) -
8856234233SRichard Henderson                          offsetof(ArchCPU, env));
8956234233SRichard Henderson         /*
9056234233SRichard Henderson          * cpu->can_do_io is cleared automatically here at the beginning of
9156234233SRichard Henderson          * each translation block.  The cost is minimal and only paid for
9256234233SRichard Henderson          * -icount, plus it would be very easy to forget doing it in the
9356234233SRichard Henderson          * translator. Doing it here means we don't need a gen_io_end() to
9456234233SRichard Henderson          * go with gen_io_start().
9556234233SRichard Henderson          */
9656234233SRichard Henderson         tcg_gen_st_i32(tcg_constant_i32(0), cpu_env,
9756234233SRichard Henderson                        offsetof(ArchCPU, parent_obj.can_do_io) -
9856234233SRichard Henderson                        offsetof(ArchCPU, env));
9956234233SRichard Henderson     }
10056234233SRichard Henderson 
10156234233SRichard Henderson     return icount_start_insn;
10256234233SRichard Henderson }
10356234233SRichard Henderson 
10456234233SRichard Henderson static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags,
10556234233SRichard Henderson                        TCGOp *icount_start_insn, int num_insns)
10656234233SRichard Henderson {
10756234233SRichard Henderson     if (cflags & CF_USE_ICOUNT) {
10856234233SRichard Henderson         /*
10956234233SRichard Henderson          * Update the num_insn immediate parameter now that we know
11056234233SRichard Henderson          * the actual insn count.
11156234233SRichard Henderson          */
11256234233SRichard Henderson         tcg_set_insn_param(icount_start_insn, 2,
11356234233SRichard Henderson                            tcgv_i32_arg(tcg_constant_i32(num_insns)));
11456234233SRichard Henderson     }
11556234233SRichard Henderson 
11656234233SRichard Henderson     if (tcg_ctx->exitreq_label) {
11756234233SRichard Henderson         gen_set_label(tcg_ctx->exitreq_label);
11856234233SRichard Henderson         tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED);
11956234233SRichard Henderson     }
12056234233SRichard Henderson }
12156234233SRichard Henderson 
122d3a2a1d8SRichard Henderson bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
123d3a2a1d8SRichard Henderson {
12484f15616SRichard Henderson     /* Suppress goto_tb if requested. */
12584f15616SRichard Henderson     if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
12684f15616SRichard Henderson         return false;
12784f15616SRichard Henderson     }
12884f15616SRichard Henderson 
129d3a2a1d8SRichard Henderson     /* Check for the dest on the same page as the start of the TB.  */
130d3a2a1d8SRichard Henderson     return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
131d3a2a1d8SRichard Henderson }
132d3a2a1d8SRichard Henderson 
133597f9b2dSRichard Henderson void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
134306c8721SRichard Henderson                      target_ulong pc, void *host_pc,
135306c8721SRichard Henderson                      const TranslatorOps *ops, DisasContextBase *db)
136bb2e0039SLluís Vilanova {
137d40c5c79SRichard Henderson     uint32_t cflags = tb_cflags(tb);
13856234233SRichard Henderson     TCGOp *icount_start_insn;
1396ba6f818SEmilio G. Cota     bool plugin_enabled;
140f9f1f56eSPavel Dovgalyuk 
141bb2e0039SLluís Vilanova     /* Initialize DisasContext */
142bb2e0039SLluís Vilanova     db->tb = tb;
143306c8721SRichard Henderson     db->pc_first = pc;
144306c8721SRichard Henderson     db->pc_next = pc;
145bb2e0039SLluís Vilanova     db->is_jmp = DISAS_NEXT;
146bb2e0039SLluís Vilanova     db->num_insns = 0;
147597f9b2dSRichard Henderson     db->max_insns = *max_insns;
148c2ffd754SRichard Henderson     db->singlestep_enabled = cflags & CF_SINGLE_STEP;
14950627f1bSRichard Henderson     db->host_addr[0] = host_pc;
15050627f1bSRichard Henderson     db->host_addr[1] = NULL;
15150627f1bSRichard Henderson 
15250627f1bSRichard Henderson #ifdef CONFIG_USER_ONLY
15350627f1bSRichard Henderson     page_protect(pc);
15450627f1bSRichard Henderson #endif
155bb2e0039SLluís Vilanova 
156b542683dSEmilio G. Cota     ops->init_disas_context(db, cpu);
157bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
158bb2e0039SLluís Vilanova 
159bb2e0039SLluís Vilanova     /* Start translating.  */
16056234233SRichard Henderson     icount_start_insn = gen_tb_start(cflags);
161bb2e0039SLluís Vilanova     ops->tb_start(db, cpu);
162bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
163bb2e0039SLluís Vilanova 
164b21af662SRichard Henderson     plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
1656ba6f818SEmilio G. Cota 
166bb2e0039SLluís Vilanova     while (true) {
1679b1890adSRichard Henderson         *max_insns = ++db->num_insns;
168bb2e0039SLluís Vilanova         ops->insn_start(db, cpu);
169bb2e0039SLluís Vilanova         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
170bb2e0039SLluís Vilanova 
1716ba6f818SEmilio G. Cota         if (plugin_enabled) {
1726ba6f818SEmilio G. Cota             plugin_gen_insn_start(cpu, db);
1736ba6f818SEmilio G. Cota         }
1746ba6f818SEmilio G. Cota 
175bb2e0039SLluís Vilanova         /* Disassemble one instruction.  The translate_insn hook should
176bb2e0039SLluís Vilanova            update db->pc_next and db->is_jmp to indicate what should be
177bb2e0039SLluís Vilanova            done next -- either exiting this loop or locate the start of
178bb2e0039SLluís Vilanova            the next instruction.  */
179d40c5c79SRichard Henderson         if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
180bb2e0039SLluís Vilanova             /* Accept I/O on the last instruction.  */
181bb2e0039SLluís Vilanova             gen_io_start();
182bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
183bb2e0039SLluís Vilanova         } else {
184cfd405eaSAlex Bennée             /* we should only see CF_MEMI_ONLY for io_recompile */
185d40c5c79SRichard Henderson             tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
186bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
187bb2e0039SLluís Vilanova         }
188bb2e0039SLluís Vilanova 
1896ba6f818SEmilio G. Cota         /*
1906ba6f818SEmilio G. Cota          * We can't instrument after instructions that change control
1916ba6f818SEmilio G. Cota          * flow although this only really affects post-load operations.
1920f92d94aSEmilio Cota          *
1930f92d94aSEmilio Cota          * Calling plugin_gen_insn_end() before we possibly stop translation
1940f92d94aSEmilio Cota          * is important. Even if this ends up as dead code, plugin generation
1950f92d94aSEmilio Cota          * needs to see a matching plugin_gen_insn_{start,end}() pair in order
1960f92d94aSEmilio Cota          * to accurately track instrumented helpers that might access memory.
1976ba6f818SEmilio G. Cota          */
1986ba6f818SEmilio G. Cota         if (plugin_enabled) {
1996ba6f818SEmilio G. Cota             plugin_gen_insn_end();
2006ba6f818SEmilio G. Cota         }
2016ba6f818SEmilio G. Cota 
2020f92d94aSEmilio Cota         /* Stop translation if translate_insn so indicated.  */
2030f92d94aSEmilio Cota         if (db->is_jmp != DISAS_NEXT) {
2040f92d94aSEmilio Cota             break;
2050f92d94aSEmilio Cota         }
2060f92d94aSEmilio Cota 
207bb2e0039SLluís Vilanova         /* Stop translation if the output buffer is full,
208bb2e0039SLluís Vilanova            or we have executed all of the allowed instructions.  */
209b542683dSEmilio G. Cota         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
210bb2e0039SLluís Vilanova             db->is_jmp = DISAS_TOO_MANY;
211bb2e0039SLluís Vilanova             break;
212bb2e0039SLluís Vilanova         }
213bb2e0039SLluís Vilanova     }
214bb2e0039SLluís Vilanova 
215bb2e0039SLluís Vilanova     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
216bb2e0039SLluís Vilanova     ops->tb_stop(db, cpu);
21756234233SRichard Henderson     gen_tb_end(tb, cflags, icount_start_insn, db->num_insns);
218bb2e0039SLluís Vilanova 
2196ba6f818SEmilio G. Cota     if (plugin_enabled) {
2206ba6f818SEmilio G. Cota         plugin_gen_tb_end(cpu);
2216ba6f818SEmilio G. Cota     }
2226ba6f818SEmilio G. Cota 
223bb2e0039SLluís Vilanova     /* The disas_log hook may use these values rather than recompute.  */
224d9971435SRichard Henderson     tb->size = db->pc_next - db->pc_first;
225d9971435SRichard Henderson     tb->icount = db->num_insns;
226bb2e0039SLluís Vilanova 
227bb2e0039SLluís Vilanova     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
228bb2e0039SLluís Vilanova         && qemu_log_in_addr_range(db->pc_first)) {
229c60f599bSRichard Henderson         FILE *logfile = qemu_log_trylock();
23078b54858SRichard Henderson         if (logfile) {
23178b54858SRichard Henderson             fprintf(logfile, "----------------\n");
2328eb806a7SRichard Henderson             ops->disas_log(db, cpu, logfile);
23378b54858SRichard Henderson             fprintf(logfile, "\n");
234fc59d2d8SRobert Foley             qemu_log_unlock(logfile);
235bb2e0039SLluís Vilanova         }
23678b54858SRichard Henderson     }
237bb2e0039SLluís Vilanova }
238f025692cSIlya Leoshkevich 
23950627f1bSRichard Henderson static void *translator_access(CPUArchState *env, DisasContextBase *db,
240f025692cSIlya Leoshkevich                                target_ulong pc, size_t len)
241f025692cSIlya Leoshkevich {
24250627f1bSRichard Henderson     void *host;
24350627f1bSRichard Henderson     target_ulong base, end;
24450627f1bSRichard Henderson     TranslationBlock *tb;
24550627f1bSRichard Henderson 
24650627f1bSRichard Henderson     tb = db->tb;
24750627f1bSRichard Henderson 
24850627f1bSRichard Henderson     /* Use slow path if first page is MMIO. */
24928905cfbSRichard Henderson     if (unlikely(tb_page_addr0(tb) == -1)) {
25050627f1bSRichard Henderson         return NULL;
25150627f1bSRichard Henderson     }
25250627f1bSRichard Henderson 
25350627f1bSRichard Henderson     end = pc + len - 1;
25450627f1bSRichard Henderson     if (likely(is_same_page(db, end))) {
25550627f1bSRichard Henderson         host = db->host_addr[0];
25650627f1bSRichard Henderson         base = db->pc_first;
25750627f1bSRichard Henderson     } else {
25850627f1bSRichard Henderson         host = db->host_addr[1];
25950627f1bSRichard Henderson         base = TARGET_PAGE_ALIGN(db->pc_first);
26050627f1bSRichard Henderson         if (host == NULL) {
26128905cfbSRichard Henderson             tb_page_addr_t phys_page =
26250627f1bSRichard Henderson                 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
2632627e452SRichard Henderson 
2642627e452SRichard Henderson             /*
2652627e452SRichard Henderson              * If the second page is MMIO, treat as if the first page
2662627e452SRichard Henderson              * was MMIO as well, so that we do not cache the TB.
2672627e452SRichard Henderson              */
2682627e452SRichard Henderson             if (unlikely(phys_page == -1)) {
2692627e452SRichard Henderson                 tb_set_page_addr0(tb, -1);
2702627e452SRichard Henderson                 return NULL;
2712627e452SRichard Henderson             }
2722627e452SRichard Henderson 
27328905cfbSRichard Henderson             tb_set_page_addr1(tb, phys_page);
274f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY
27550627f1bSRichard Henderson             page_protect(end);
276f025692cSIlya Leoshkevich #endif
27750627f1bSRichard Henderson             host = db->host_addr[1];
278f025692cSIlya Leoshkevich         }
279f025692cSIlya Leoshkevich 
28050627f1bSRichard Henderson         /* Use slow path when crossing pages. */
28150627f1bSRichard Henderson         if (is_same_page(db, pc)) {
28250627f1bSRichard Henderson             return NULL;
28350627f1bSRichard Henderson         }
284f025692cSIlya Leoshkevich     }
285f025692cSIlya Leoshkevich 
28650627f1bSRichard Henderson     tcg_debug_assert(pc >= base);
28750627f1bSRichard Henderson     return host + (pc - base);
28850627f1bSRichard Henderson }
289f025692cSIlya Leoshkevich 
29050627f1bSRichard Henderson uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
29150627f1bSRichard Henderson {
29250627f1bSRichard Henderson     uint8_t ret;
29350627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
29450627f1bSRichard Henderson 
29550627f1bSRichard Henderson     if (p) {
29650627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
29750627f1bSRichard Henderson         return ldub_p(p);
29850627f1bSRichard Henderson     }
29950627f1bSRichard Henderson     ret = cpu_ldub_code(env, pc);
30050627f1bSRichard Henderson     plugin_insn_append(pc, &ret, sizeof(ret));
30150627f1bSRichard Henderson     return ret;
30250627f1bSRichard Henderson }
30350627f1bSRichard Henderson 
30450627f1bSRichard Henderson uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
30550627f1bSRichard Henderson {
30650627f1bSRichard Henderson     uint16_t ret, plug;
30750627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
30850627f1bSRichard Henderson 
30950627f1bSRichard Henderson     if (p) {
31050627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
31150627f1bSRichard Henderson         return lduw_p(p);
31250627f1bSRichard Henderson     }
31350627f1bSRichard Henderson     ret = cpu_lduw_code(env, pc);
31450627f1bSRichard Henderson     plug = tswap16(ret);
31550627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
31650627f1bSRichard Henderson     return ret;
31750627f1bSRichard Henderson }
31850627f1bSRichard Henderson 
31950627f1bSRichard Henderson uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
32050627f1bSRichard Henderson {
32150627f1bSRichard Henderson     uint32_t ret, plug;
32250627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
32350627f1bSRichard Henderson 
32450627f1bSRichard Henderson     if (p) {
32550627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
32650627f1bSRichard Henderson         return ldl_p(p);
32750627f1bSRichard Henderson     }
32850627f1bSRichard Henderson     ret = cpu_ldl_code(env, pc);
32950627f1bSRichard Henderson     plug = tswap32(ret);
33050627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
33150627f1bSRichard Henderson     return ret;
33250627f1bSRichard Henderson }
33350627f1bSRichard Henderson 
33450627f1bSRichard Henderson uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
33550627f1bSRichard Henderson {
33650627f1bSRichard Henderson     uint64_t ret, plug;
33750627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
33850627f1bSRichard Henderson 
33950627f1bSRichard Henderson     if (p) {
34050627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
34150627f1bSRichard Henderson         return ldq_p(p);
34250627f1bSRichard Henderson     }
34350627f1bSRichard Henderson     ret = cpu_ldq_code(env, pc);
34450627f1bSRichard Henderson     plug = tswap64(ret);
34550627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
34650627f1bSRichard Henderson     return ret;
34750627f1bSRichard Henderson }
348*309e014dSRichard Henderson 
349*309e014dSRichard Henderson void translator_fake_ldb(uint8_t insn8, abi_ptr pc)
350*309e014dSRichard Henderson {
351*309e014dSRichard Henderson     plugin_insn_append(pc, &insn8, sizeof(insn8));
352*309e014dSRichard Henderson }
353