xref: /qemu/accel/tcg/translator.c (revision 84f1561629ba5b6942b6edd825a1d14c9f51a25c)
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 {
36*84f15616SRichard Henderson     /* Suppress goto_tb if requested. */
37*84f15616SRichard Henderson     if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
38*84f15616SRichard Henderson         return false;
39*84f15616SRichard Henderson     }
40*84f15616SRichard Henderson 
41d3a2a1d8SRichard Henderson     /* Suppress goto_tb in the case of single-steping.  */
42d3a2a1d8SRichard Henderson     if (db->singlestep_enabled || singlestep) {
43d3a2a1d8SRichard Henderson         return false;
44d3a2a1d8SRichard Henderson     }
45d3a2a1d8SRichard Henderson 
46d3a2a1d8SRichard Henderson     /* Check for the dest on the same page as the start of the TB.  */
47d3a2a1d8SRichard Henderson     return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0;
48d3a2a1d8SRichard Henderson }
49d3a2a1d8SRichard Henderson 
50bb2e0039SLluís Vilanova void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
518b86d6d2SRichard Henderson                      CPUState *cpu, TranslationBlock *tb, int max_insns)
52bb2e0039SLluís Vilanova {
53f9f1f56eSPavel Dovgalyuk     int bp_insn = 0;
546ba6f818SEmilio G. Cota     bool plugin_enabled;
55f9f1f56eSPavel Dovgalyuk 
56bb2e0039SLluís Vilanova     /* Initialize DisasContext */
57bb2e0039SLluís Vilanova     db->tb = tb;
58bb2e0039SLluís Vilanova     db->pc_first = tb->pc;
59bb2e0039SLluís Vilanova     db->pc_next = db->pc_first;
60bb2e0039SLluís Vilanova     db->is_jmp = DISAS_NEXT;
61bb2e0039SLluís Vilanova     db->num_insns = 0;
628b86d6d2SRichard Henderson     db->max_insns = max_insns;
63bb2e0039SLluís Vilanova     db->singlestep_enabled = cpu->singlestep_enabled;
64bb2e0039SLluís Vilanova 
65b542683dSEmilio G. Cota     ops->init_disas_context(db, cpu);
66bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
67bb2e0039SLluís Vilanova 
68bb2e0039SLluís Vilanova     /* Reset the temp count so that we can identify leaks */
69bb2e0039SLluís Vilanova     tcg_clear_temp_count();
70bb2e0039SLluís Vilanova 
71bb2e0039SLluís Vilanova     /* Start translating.  */
72bb2e0039SLluís Vilanova     gen_tb_start(db->tb);
73bb2e0039SLluís Vilanova     ops->tb_start(db, cpu);
74bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
75bb2e0039SLluís Vilanova 
76cfd405eaSAlex Bennée     plugin_enabled = plugin_gen_tb_start(cpu, tb,
77cfd405eaSAlex Bennée                                          tb_cflags(db->tb) & CF_MEMI_ONLY);
786ba6f818SEmilio G. Cota 
79bb2e0039SLluís Vilanova     while (true) {
80bb2e0039SLluís Vilanova         db->num_insns++;
81bb2e0039SLluís Vilanova         ops->insn_start(db, cpu);
82bb2e0039SLluís Vilanova         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
83bb2e0039SLluís Vilanova 
846ba6f818SEmilio G. Cota         if (plugin_enabled) {
856ba6f818SEmilio G. Cota             plugin_gen_insn_start(cpu, db);
866ba6f818SEmilio G. Cota         }
876ba6f818SEmilio G. Cota 
88bb2e0039SLluís Vilanova         /* Pass breakpoint hits to target for further processing */
89f9f1f56eSPavel Dovgalyuk         if (!db->singlestep_enabled
90f9f1f56eSPavel Dovgalyuk             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
91bb2e0039SLluís Vilanova             CPUBreakpoint *bp;
92bb2e0039SLluís Vilanova             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
93bb2e0039SLluís Vilanova                 if (bp->pc == db->pc_next) {
94bb2e0039SLluís Vilanova                     if (ops->breakpoint_check(db, cpu, bp)) {
95f9f1f56eSPavel Dovgalyuk                         bp_insn = 1;
96bb2e0039SLluís Vilanova                         break;
97bb2e0039SLluís Vilanova                     }
98bb2e0039SLluís Vilanova                 }
99bb2e0039SLluís Vilanova             }
100bb2e0039SLluís Vilanova             /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
101bb2e0039SLluís Vilanova                that only one more instruction is to be executed.  Otherwise
102bb2e0039SLluís Vilanova                it should use DISAS_NORETURN when generating an exception,
103bb2e0039SLluís Vilanova                but may use a DISAS_TARGET_* value for Something Else.  */
104bb2e0039SLluís Vilanova             if (db->is_jmp > DISAS_TOO_MANY) {
105bb2e0039SLluís Vilanova                 break;
106bb2e0039SLluís Vilanova             }
107bb2e0039SLluís Vilanova         }
108bb2e0039SLluís Vilanova 
109bb2e0039SLluís Vilanova         /* Disassemble one instruction.  The translate_insn hook should
110bb2e0039SLluís Vilanova            update db->pc_next and db->is_jmp to indicate what should be
111bb2e0039SLluís Vilanova            done next -- either exiting this loop or locate the start of
112bb2e0039SLluís Vilanova            the next instruction.  */
113b542683dSEmilio G. Cota         if (db->num_insns == db->max_insns
114b542683dSEmilio G. Cota             && (tb_cflags(db->tb) & CF_LAST_IO)) {
115bb2e0039SLluís Vilanova             /* Accept I/O on the last instruction.  */
116bb2e0039SLluís Vilanova             gen_io_start();
117bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
118bb2e0039SLluís Vilanova         } else {
119cfd405eaSAlex Bennée             /* we should only see CF_MEMI_ONLY for io_recompile */
120cfd405eaSAlex Bennée             tcg_debug_assert(!(tb_cflags(db->tb) & CF_MEMI_ONLY));
121bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
122bb2e0039SLluís Vilanova         }
123bb2e0039SLluís Vilanova 
124bb2e0039SLluís Vilanova         /* Stop translation if translate_insn so indicated.  */
125bb2e0039SLluís Vilanova         if (db->is_jmp != DISAS_NEXT) {
126bb2e0039SLluís Vilanova             break;
127bb2e0039SLluís Vilanova         }
128bb2e0039SLluís Vilanova 
1296ba6f818SEmilio G. Cota         /*
1306ba6f818SEmilio G. Cota          * We can't instrument after instructions that change control
1316ba6f818SEmilio G. Cota          * flow although this only really affects post-load operations.
1326ba6f818SEmilio G. Cota          */
1336ba6f818SEmilio G. Cota         if (plugin_enabled) {
1346ba6f818SEmilio G. Cota             plugin_gen_insn_end();
1356ba6f818SEmilio G. Cota         }
1366ba6f818SEmilio G. Cota 
137bb2e0039SLluís Vilanova         /* Stop translation if the output buffer is full,
138bb2e0039SLluís Vilanova            or we have executed all of the allowed instructions.  */
139b542683dSEmilio G. Cota         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
140bb2e0039SLluís Vilanova             db->is_jmp = DISAS_TOO_MANY;
141bb2e0039SLluís Vilanova             break;
142bb2e0039SLluís Vilanova         }
143bb2e0039SLluís Vilanova     }
144bb2e0039SLluís Vilanova 
145bb2e0039SLluís Vilanova     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
146bb2e0039SLluís Vilanova     ops->tb_stop(db, cpu);
147f9f1f56eSPavel Dovgalyuk     gen_tb_end(db->tb, db->num_insns - bp_insn);
148bb2e0039SLluís Vilanova 
1496ba6f818SEmilio G. Cota     if (plugin_enabled) {
1506ba6f818SEmilio G. Cota         plugin_gen_tb_end(cpu);
1516ba6f818SEmilio G. Cota     }
1526ba6f818SEmilio G. Cota 
153bb2e0039SLluís Vilanova     /* The disas_log hook may use these values rather than recompute.  */
154d9971435SRichard Henderson     tb->size = db->pc_next - db->pc_first;
155d9971435SRichard Henderson     tb->icount = db->num_insns;
156bb2e0039SLluís Vilanova 
157bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS
158bb2e0039SLluís Vilanova     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
159bb2e0039SLluís Vilanova         && qemu_log_in_addr_range(db->pc_first)) {
160fc59d2d8SRobert Foley         FILE *logfile = qemu_log_lock();
161bb2e0039SLluís Vilanova         qemu_log("----------------\n");
162bb2e0039SLluís Vilanova         ops->disas_log(db, cpu);
163bb2e0039SLluís Vilanova         qemu_log("\n");
164fc59d2d8SRobert Foley         qemu_log_unlock(logfile);
165bb2e0039SLluís Vilanova     }
166bb2e0039SLluís Vilanova #endif
167bb2e0039SLluís Vilanova }
168