xref: /qemu/accel/tcg/translator.c (revision 8b86d6d25807e13a63ab6ea879f976b9f18cc45a)
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-common.h"
12bb2e0039SLluís Vilanova #include "qemu/error-report.h"
13bb2e0039SLluís Vilanova #include "cpu.h"
14bb2e0039SLluís Vilanova #include "tcg/tcg.h"
15bb2e0039SLluís Vilanova #include "tcg/tcg-op.h"
16bb2e0039SLluís Vilanova #include "exec/exec-all.h"
17bb2e0039SLluís Vilanova #include "exec/gen-icount.h"
18bb2e0039SLluís Vilanova #include "exec/log.h"
19bb2e0039SLluís Vilanova #include "exec/translator.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 
34bb2e0039SLluís Vilanova void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
35*8b86d6d2SRichard Henderson                      CPUState *cpu, TranslationBlock *tb, int max_insns)
36bb2e0039SLluís Vilanova {
37f9f1f56eSPavel Dovgalyuk     int bp_insn = 0;
38f9f1f56eSPavel Dovgalyuk 
39bb2e0039SLluís Vilanova     /* Initialize DisasContext */
40bb2e0039SLluís Vilanova     db->tb = tb;
41bb2e0039SLluís Vilanova     db->pc_first = tb->pc;
42bb2e0039SLluís Vilanova     db->pc_next = db->pc_first;
43bb2e0039SLluís Vilanova     db->is_jmp = DISAS_NEXT;
44bb2e0039SLluís Vilanova     db->num_insns = 0;
45*8b86d6d2SRichard Henderson     db->max_insns = max_insns;
46bb2e0039SLluís Vilanova     db->singlestep_enabled = cpu->singlestep_enabled;
47bb2e0039SLluís Vilanova 
48b542683dSEmilio G. Cota     ops->init_disas_context(db, cpu);
49bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
50bb2e0039SLluís Vilanova 
51bb2e0039SLluís Vilanova     /* Reset the temp count so that we can identify leaks */
52bb2e0039SLluís Vilanova     tcg_clear_temp_count();
53bb2e0039SLluís Vilanova 
54bb2e0039SLluís Vilanova     /* Start translating.  */
55bb2e0039SLluís Vilanova     gen_tb_start(db->tb);
56bb2e0039SLluís Vilanova     ops->tb_start(db, cpu);
57bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
58bb2e0039SLluís Vilanova 
59bb2e0039SLluís Vilanova     while (true) {
60bb2e0039SLluís Vilanova         db->num_insns++;
61bb2e0039SLluís Vilanova         ops->insn_start(db, cpu);
62bb2e0039SLluís Vilanova         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
63bb2e0039SLluís Vilanova 
64bb2e0039SLluís Vilanova         /* Pass breakpoint hits to target for further processing */
65f9f1f56eSPavel Dovgalyuk         if (!db->singlestep_enabled
66f9f1f56eSPavel Dovgalyuk             && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
67bb2e0039SLluís Vilanova             CPUBreakpoint *bp;
68bb2e0039SLluís Vilanova             QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
69bb2e0039SLluís Vilanova                 if (bp->pc == db->pc_next) {
70bb2e0039SLluís Vilanova                     if (ops->breakpoint_check(db, cpu, bp)) {
71f9f1f56eSPavel Dovgalyuk                         bp_insn = 1;
72bb2e0039SLluís Vilanova                         break;
73bb2e0039SLluís Vilanova                     }
74bb2e0039SLluís Vilanova                 }
75bb2e0039SLluís Vilanova             }
76bb2e0039SLluís Vilanova             /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
77bb2e0039SLluís Vilanova                that only one more instruction is to be executed.  Otherwise
78bb2e0039SLluís Vilanova                it should use DISAS_NORETURN when generating an exception,
79bb2e0039SLluís Vilanova                but may use a DISAS_TARGET_* value for Something Else.  */
80bb2e0039SLluís Vilanova             if (db->is_jmp > DISAS_TOO_MANY) {
81bb2e0039SLluís Vilanova                 break;
82bb2e0039SLluís Vilanova             }
83bb2e0039SLluís Vilanova         }
84bb2e0039SLluís Vilanova 
85bb2e0039SLluís Vilanova         /* Disassemble one instruction.  The translate_insn hook should
86bb2e0039SLluís Vilanova            update db->pc_next and db->is_jmp to indicate what should be
87bb2e0039SLluís Vilanova            done next -- either exiting this loop or locate the start of
88bb2e0039SLluís Vilanova            the next instruction.  */
89b542683dSEmilio G. Cota         if (db->num_insns == db->max_insns
90b542683dSEmilio G. Cota             && (tb_cflags(db->tb) & CF_LAST_IO)) {
91bb2e0039SLluís Vilanova             /* Accept I/O on the last instruction.  */
92bb2e0039SLluís Vilanova             gen_io_start();
93bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
94bb2e0039SLluís Vilanova             gen_io_end();
95bb2e0039SLluís Vilanova         } else {
96bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
97bb2e0039SLluís Vilanova         }
98bb2e0039SLluís Vilanova 
99bb2e0039SLluís Vilanova         /* Stop translation if translate_insn so indicated.  */
100bb2e0039SLluís Vilanova         if (db->is_jmp != DISAS_NEXT) {
101bb2e0039SLluís Vilanova             break;
102bb2e0039SLluís Vilanova         }
103bb2e0039SLluís Vilanova 
104bb2e0039SLluís Vilanova         /* Stop translation if the output buffer is full,
105bb2e0039SLluís Vilanova            or we have executed all of the allowed instructions.  */
106b542683dSEmilio G. Cota         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
107bb2e0039SLluís Vilanova             db->is_jmp = DISAS_TOO_MANY;
108bb2e0039SLluís Vilanova             break;
109bb2e0039SLluís Vilanova         }
110bb2e0039SLluís Vilanova     }
111bb2e0039SLluís Vilanova 
112bb2e0039SLluís Vilanova     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
113bb2e0039SLluís Vilanova     ops->tb_stop(db, cpu);
114f9f1f56eSPavel Dovgalyuk     gen_tb_end(db->tb, db->num_insns - bp_insn);
115bb2e0039SLluís Vilanova 
116bb2e0039SLluís Vilanova     /* The disas_log hook may use these values rather than recompute.  */
117bb2e0039SLluís Vilanova     db->tb->size = db->pc_next - db->pc_first;
118bb2e0039SLluís Vilanova     db->tb->icount = db->num_insns;
119bb2e0039SLluís Vilanova 
120bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS
121bb2e0039SLluís Vilanova     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
122bb2e0039SLluís Vilanova         && qemu_log_in_addr_range(db->pc_first)) {
123bb2e0039SLluís Vilanova         qemu_log_lock();
124bb2e0039SLluís Vilanova         qemu_log("----------------\n");
125bb2e0039SLluís Vilanova         ops->disas_log(db, cpu);
126bb2e0039SLluís Vilanova         qemu_log("\n");
127bb2e0039SLluís Vilanova         qemu_log_unlock();
128bb2e0039SLluís Vilanova     }
129bb2e0039SLluís Vilanova #endif
130bb2e0039SLluís Vilanova }
131