xref: /qemu/accel/tcg/translator.c (revision 28905cfbd521c40ebc6d7b4c5941c0ec1ca935eb)
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 
103bb2e0039SLluís Vilanova         /* Stop translation if translate_insn so indicated.  */
104bb2e0039SLluís Vilanova         if (db->is_jmp != DISAS_NEXT) {
105bb2e0039SLluís Vilanova             break;
106bb2e0039SLluís Vilanova         }
107bb2e0039SLluís Vilanova 
1086ba6f818SEmilio G. Cota         /*
1096ba6f818SEmilio G. Cota          * We can't instrument after instructions that change control
1106ba6f818SEmilio G. Cota          * flow although this only really affects post-load operations.
1116ba6f818SEmilio G. Cota          */
1126ba6f818SEmilio G. Cota         if (plugin_enabled) {
1136ba6f818SEmilio G. Cota             plugin_gen_insn_end();
1146ba6f818SEmilio G. Cota         }
1156ba6f818SEmilio G. Cota 
116bb2e0039SLluís Vilanova         /* Stop translation if the output buffer is full,
117bb2e0039SLluís Vilanova            or we have executed all of the allowed instructions.  */
118b542683dSEmilio G. Cota         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
119bb2e0039SLluís Vilanova             db->is_jmp = DISAS_TOO_MANY;
120bb2e0039SLluís Vilanova             break;
121bb2e0039SLluís Vilanova         }
122bb2e0039SLluís Vilanova     }
123bb2e0039SLluís Vilanova 
124bb2e0039SLluís Vilanova     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
125bb2e0039SLluís Vilanova     ops->tb_stop(db, cpu);
12610c37828SRichard Henderson     gen_tb_end(db->tb, db->num_insns);
127bb2e0039SLluís Vilanova 
1286ba6f818SEmilio G. Cota     if (plugin_enabled) {
1296ba6f818SEmilio G. Cota         plugin_gen_tb_end(cpu);
1306ba6f818SEmilio G. Cota     }
1316ba6f818SEmilio G. Cota 
132bb2e0039SLluís Vilanova     /* The disas_log hook may use these values rather than recompute.  */
133d9971435SRichard Henderson     tb->size = db->pc_next - db->pc_first;
134d9971435SRichard Henderson     tb->icount = db->num_insns;
135bb2e0039SLluís Vilanova 
136bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS
137bb2e0039SLluís Vilanova     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
138bb2e0039SLluís Vilanova         && qemu_log_in_addr_range(db->pc_first)) {
139c60f599bSRichard Henderson         FILE *logfile = qemu_log_trylock();
14078b54858SRichard Henderson         if (logfile) {
14178b54858SRichard Henderson             fprintf(logfile, "----------------\n");
1428eb806a7SRichard Henderson             ops->disas_log(db, cpu, logfile);
14378b54858SRichard Henderson             fprintf(logfile, "\n");
144fc59d2d8SRobert Foley             qemu_log_unlock(logfile);
145bb2e0039SLluís Vilanova         }
14678b54858SRichard Henderson     }
147bb2e0039SLluís Vilanova #endif
148bb2e0039SLluís Vilanova }
149f025692cSIlya Leoshkevich 
15050627f1bSRichard Henderson static void *translator_access(CPUArchState *env, DisasContextBase *db,
151f025692cSIlya Leoshkevich                                target_ulong pc, size_t len)
152f025692cSIlya Leoshkevich {
15350627f1bSRichard Henderson     void *host;
15450627f1bSRichard Henderson     target_ulong base, end;
15550627f1bSRichard Henderson     TranslationBlock *tb;
15650627f1bSRichard Henderson 
15750627f1bSRichard Henderson     tb = db->tb;
15850627f1bSRichard Henderson 
15950627f1bSRichard Henderson     /* Use slow path if first page is MMIO. */
160*28905cfbSRichard Henderson     if (unlikely(tb_page_addr0(tb) == -1)) {
16150627f1bSRichard Henderson         return NULL;
16250627f1bSRichard Henderson     }
16350627f1bSRichard Henderson 
16450627f1bSRichard Henderson     end = pc + len - 1;
16550627f1bSRichard Henderson     if (likely(is_same_page(db, end))) {
16650627f1bSRichard Henderson         host = db->host_addr[0];
16750627f1bSRichard Henderson         base = db->pc_first;
16850627f1bSRichard Henderson     } else {
16950627f1bSRichard Henderson         host = db->host_addr[1];
17050627f1bSRichard Henderson         base = TARGET_PAGE_ALIGN(db->pc_first);
17150627f1bSRichard Henderson         if (host == NULL) {
172*28905cfbSRichard Henderson             tb_page_addr_t phys_page =
17350627f1bSRichard Henderson                 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
174*28905cfbSRichard Henderson             /* We cannot handle MMIO as second page. */
175*28905cfbSRichard Henderson             assert(phys_page != -1);
176*28905cfbSRichard Henderson             tb_set_page_addr1(tb, phys_page);
177f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY
17850627f1bSRichard Henderson             page_protect(end);
179f025692cSIlya Leoshkevich #endif
18050627f1bSRichard Henderson             host = db->host_addr[1];
181f025692cSIlya Leoshkevich         }
182f025692cSIlya Leoshkevich 
18350627f1bSRichard Henderson         /* Use slow path when crossing pages. */
18450627f1bSRichard Henderson         if (is_same_page(db, pc)) {
18550627f1bSRichard Henderson             return NULL;
18650627f1bSRichard Henderson         }
187f025692cSIlya Leoshkevich     }
188f025692cSIlya Leoshkevich 
18950627f1bSRichard Henderson     tcg_debug_assert(pc >= base);
19050627f1bSRichard Henderson     return host + (pc - base);
19150627f1bSRichard Henderson }
192f025692cSIlya Leoshkevich 
19350627f1bSRichard Henderson uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
19450627f1bSRichard Henderson {
19550627f1bSRichard Henderson     uint8_t ret;
19650627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
19750627f1bSRichard Henderson 
19850627f1bSRichard Henderson     if (p) {
19950627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
20050627f1bSRichard Henderson         return ldub_p(p);
20150627f1bSRichard Henderson     }
20250627f1bSRichard Henderson     ret = cpu_ldub_code(env, pc);
20350627f1bSRichard Henderson     plugin_insn_append(pc, &ret, sizeof(ret));
20450627f1bSRichard Henderson     return ret;
20550627f1bSRichard Henderson }
20650627f1bSRichard Henderson 
20750627f1bSRichard Henderson uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
20850627f1bSRichard Henderson {
20950627f1bSRichard Henderson     uint16_t ret, plug;
21050627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
21150627f1bSRichard Henderson 
21250627f1bSRichard Henderson     if (p) {
21350627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
21450627f1bSRichard Henderson         return lduw_p(p);
21550627f1bSRichard Henderson     }
21650627f1bSRichard Henderson     ret = cpu_lduw_code(env, pc);
21750627f1bSRichard Henderson     plug = tswap16(ret);
21850627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
21950627f1bSRichard Henderson     return ret;
22050627f1bSRichard Henderson }
22150627f1bSRichard Henderson 
22250627f1bSRichard Henderson uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
22350627f1bSRichard Henderson {
22450627f1bSRichard Henderson     uint32_t ret, plug;
22550627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
22650627f1bSRichard Henderson 
22750627f1bSRichard Henderson     if (p) {
22850627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
22950627f1bSRichard Henderson         return ldl_p(p);
23050627f1bSRichard Henderson     }
23150627f1bSRichard Henderson     ret = cpu_ldl_code(env, pc);
23250627f1bSRichard Henderson     plug = tswap32(ret);
23350627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
23450627f1bSRichard Henderson     return ret;
23550627f1bSRichard Henderson }
23650627f1bSRichard Henderson 
23750627f1bSRichard Henderson uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
23850627f1bSRichard Henderson {
23950627f1bSRichard Henderson     uint64_t ret, plug;
24050627f1bSRichard Henderson     void *p = translator_access(env, db, pc, sizeof(ret));
24150627f1bSRichard Henderson 
24250627f1bSRichard Henderson     if (p) {
24350627f1bSRichard Henderson         plugin_insn_append(pc, p, sizeof(ret));
24450627f1bSRichard Henderson         return ldq_p(p);
24550627f1bSRichard Henderson     }
24650627f1bSRichard Henderson     ret = cpu_ldq_code(env, pc);
24750627f1bSRichard Henderson     plug = tswap64(ret);
24850627f1bSRichard Henderson     plugin_insn_append(pc, &plug, sizeof(ret));
24950627f1bSRichard Henderson     return ret;
25050627f1bSRichard Henderson }
251