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