xref: /qemu/accel/tcg/translator.c (revision 357af9be5ca47ae8ac2bc439de4bb9a39e186fd4)
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 
45f025692cSIlya Leoshkevich static inline void translator_page_protect(DisasContextBase *dcbase,
46f025692cSIlya Leoshkevich                                            target_ulong pc)
47f025692cSIlya Leoshkevich {
48f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY
49f025692cSIlya Leoshkevich     dcbase->page_protect_end = pc | ~TARGET_PAGE_MASK;
50f025692cSIlya Leoshkevich     page_protect(pc);
51f025692cSIlya Leoshkevich #endif
52f025692cSIlya Leoshkevich }
53f025692cSIlya Leoshkevich 
54bb2e0039SLluís Vilanova void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
558b86d6d2SRichard Henderson                      CPUState *cpu, TranslationBlock *tb, int max_insns)
56bb2e0039SLluís Vilanova {
57d40c5c79SRichard Henderson     uint32_t cflags = tb_cflags(tb);
586ba6f818SEmilio G. Cota     bool plugin_enabled;
59f9f1f56eSPavel Dovgalyuk 
60bb2e0039SLluís Vilanova     /* Initialize DisasContext */
61bb2e0039SLluís Vilanova     db->tb = tb;
62bb2e0039SLluís Vilanova     db->pc_first = tb->pc;
63bb2e0039SLluís Vilanova     db->pc_next = db->pc_first;
64bb2e0039SLluís Vilanova     db->is_jmp = DISAS_NEXT;
65bb2e0039SLluís Vilanova     db->num_insns = 0;
668b86d6d2SRichard Henderson     db->max_insns = max_insns;
67c2ffd754SRichard Henderson     db->singlestep_enabled = cflags & CF_SINGLE_STEP;
68f025692cSIlya Leoshkevich     translator_page_protect(db, db->pc_next);
69bb2e0039SLluís Vilanova 
70b542683dSEmilio G. Cota     ops->init_disas_context(db, cpu);
71bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
72bb2e0039SLluís Vilanova 
73bb2e0039SLluís Vilanova     /* Reset the temp count so that we can identify leaks */
74bb2e0039SLluís Vilanova     tcg_clear_temp_count();
75bb2e0039SLluís Vilanova 
76bb2e0039SLluís Vilanova     /* Start translating.  */
77bb2e0039SLluís Vilanova     gen_tb_start(db->tb);
78bb2e0039SLluís Vilanova     ops->tb_start(db, cpu);
79bb2e0039SLluís Vilanova     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
80bb2e0039SLluís Vilanova 
81d40c5c79SRichard Henderson     plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY);
826ba6f818SEmilio G. Cota 
83bb2e0039SLluís Vilanova     while (true) {
84bb2e0039SLluís Vilanova         db->num_insns++;
85bb2e0039SLluís Vilanova         ops->insn_start(db, cpu);
86bb2e0039SLluís Vilanova         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
87bb2e0039SLluís Vilanova 
886ba6f818SEmilio G. Cota         if (plugin_enabled) {
896ba6f818SEmilio G. Cota             plugin_gen_insn_start(cpu, db);
906ba6f818SEmilio G. Cota         }
916ba6f818SEmilio G. Cota 
92bb2e0039SLluís Vilanova         /* Disassemble one instruction.  The translate_insn hook should
93bb2e0039SLluís Vilanova            update db->pc_next and db->is_jmp to indicate what should be
94bb2e0039SLluís Vilanova            done next -- either exiting this loop or locate the start of
95bb2e0039SLluís Vilanova            the next instruction.  */
96d40c5c79SRichard Henderson         if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
97bb2e0039SLluís Vilanova             /* Accept I/O on the last instruction.  */
98bb2e0039SLluís Vilanova             gen_io_start();
99bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
100bb2e0039SLluís Vilanova         } else {
101cfd405eaSAlex Bennée             /* we should only see CF_MEMI_ONLY for io_recompile */
102d40c5c79SRichard Henderson             tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
103bb2e0039SLluís Vilanova             ops->translate_insn(db, cpu);
104bb2e0039SLluís Vilanova         }
105bb2e0039SLluís Vilanova 
106bb2e0039SLluís Vilanova         /* Stop translation if translate_insn so indicated.  */
107bb2e0039SLluís Vilanova         if (db->is_jmp != DISAS_NEXT) {
108bb2e0039SLluís Vilanova             break;
109bb2e0039SLluís Vilanova         }
110bb2e0039SLluís Vilanova 
1116ba6f818SEmilio G. Cota         /*
1126ba6f818SEmilio G. Cota          * We can't instrument after instructions that change control
1136ba6f818SEmilio G. Cota          * flow although this only really affects post-load operations.
1146ba6f818SEmilio G. Cota          */
1156ba6f818SEmilio G. Cota         if (plugin_enabled) {
1166ba6f818SEmilio G. Cota             plugin_gen_insn_end();
1176ba6f818SEmilio G. Cota         }
1186ba6f818SEmilio G. Cota 
119bb2e0039SLluís Vilanova         /* Stop translation if the output buffer is full,
120bb2e0039SLluís Vilanova            or we have executed all of the allowed instructions.  */
121b542683dSEmilio G. Cota         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
122bb2e0039SLluís Vilanova             db->is_jmp = DISAS_TOO_MANY;
123bb2e0039SLluís Vilanova             break;
124bb2e0039SLluís Vilanova         }
125bb2e0039SLluís Vilanova     }
126bb2e0039SLluís Vilanova 
127bb2e0039SLluís Vilanova     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
128bb2e0039SLluís Vilanova     ops->tb_stop(db, cpu);
12910c37828SRichard Henderson     gen_tb_end(db->tb, db->num_insns);
130bb2e0039SLluís Vilanova 
1316ba6f818SEmilio G. Cota     if (plugin_enabled) {
1326ba6f818SEmilio G. Cota         plugin_gen_tb_end(cpu);
1336ba6f818SEmilio G. Cota     }
1346ba6f818SEmilio G. Cota 
135bb2e0039SLluís Vilanova     /* The disas_log hook may use these values rather than recompute.  */
136d9971435SRichard Henderson     tb->size = db->pc_next - db->pc_first;
137d9971435SRichard Henderson     tb->icount = db->num_insns;
138bb2e0039SLluís Vilanova 
139bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS
140bb2e0039SLluís Vilanova     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
141bb2e0039SLluís Vilanova         && qemu_log_in_addr_range(db->pc_first)) {
142fc59d2d8SRobert Foley         FILE *logfile = qemu_log_lock();
143bb2e0039SLluís Vilanova         qemu_log("----------------\n");
144bb2e0039SLluís Vilanova         ops->disas_log(db, cpu);
145bb2e0039SLluís Vilanova         qemu_log("\n");
146fc59d2d8SRobert Foley         qemu_log_unlock(logfile);
147bb2e0039SLluís Vilanova     }
148bb2e0039SLluís Vilanova #endif
149bb2e0039SLluís Vilanova }
150f025692cSIlya Leoshkevich 
151f025692cSIlya Leoshkevich static inline void translator_maybe_page_protect(DisasContextBase *dcbase,
152f025692cSIlya Leoshkevich                                                  target_ulong pc, size_t len)
153f025692cSIlya Leoshkevich {
154f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY
155f025692cSIlya Leoshkevich     target_ulong end = pc + len - 1;
156f025692cSIlya Leoshkevich 
157f025692cSIlya Leoshkevich     if (end > dcbase->page_protect_end) {
158f025692cSIlya Leoshkevich         translator_page_protect(dcbase, end);
159f025692cSIlya Leoshkevich     }
160f025692cSIlya Leoshkevich #endif
161f025692cSIlya Leoshkevich }
162f025692cSIlya Leoshkevich 
163f025692cSIlya Leoshkevich #define GEN_TRANSLATOR_LD(fullname, type, load_fn, swap_fn)             \
164f025692cSIlya Leoshkevich     type fullname ## _swap(CPUArchState *env, DisasContextBase *dcbase, \
165f025692cSIlya Leoshkevich                            abi_ptr pc, bool do_swap)                    \
166f025692cSIlya Leoshkevich     {                                                                   \
167f025692cSIlya Leoshkevich         translator_maybe_page_protect(dcbase, pc, sizeof(type));        \
168f025692cSIlya Leoshkevich         type ret = load_fn(env, pc);                                    \
169f025692cSIlya Leoshkevich         if (do_swap) {                                                  \
170f025692cSIlya Leoshkevich             ret = swap_fn(ret);                                         \
171f025692cSIlya Leoshkevich         }                                                               \
172*357af9beSAlex Bennée         plugin_insn_append(pc, &ret, sizeof(ret));                      \
173f025692cSIlya Leoshkevich         return ret;                                                     \
174f025692cSIlya Leoshkevich     }
175f025692cSIlya Leoshkevich 
176f025692cSIlya Leoshkevich FOR_EACH_TRANSLATOR_LD(GEN_TRANSLATOR_LD)
177f025692cSIlya Leoshkevich 
178f025692cSIlya Leoshkevich #undef GEN_TRANSLATOR_LD
179