]>
Commit | Line | Data |
---|---|---|
bb2e0039 LV |
1 | /* |
2 | * Generic intermediate code generation. | |
3 | * | |
4 | * Copyright (C) 2016-2017 Lluís Vilanova <[email protected]> | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
7 | * See the COPYING file in the top-level directory. | |
8 | */ | |
9 | ||
10 | #include "qemu/osdep.h" | |
bb2e0039 LV |
11 | #include "qemu/error-report.h" |
12 | #include "cpu.h" | |
13 | #include "tcg/tcg.h" | |
14 | #include "tcg/tcg-op.h" | |
15 | #include "exec/exec-all.h" | |
16 | #include "exec/gen-icount.h" | |
17 | #include "exec/log.h" | |
18 | #include "exec/translator.h" | |
6ba6f818 | 19 | #include "exec/plugin-gen.h" |
fda8458b | 20 | #include "sysemu/replay.h" |
bb2e0039 LV |
21 | |
22 | /* Pairs with tcg_clear_temp_count. | |
23 | To be called by #TranslatorOps.{translate_insn,tb_stop} if | |
24 | (1) the target is sufficiently clean to support reporting, | |
25 | (2) as and when all temporaries are known to be consumed. | |
26 | For most targets, (2) is at the end of translate_insn. */ | |
27 | void translator_loop_temp_check(DisasContextBase *db) | |
28 | { | |
29 | if (tcg_check_temp_count()) { | |
30 | qemu_log("warning: TCG temporary leaks before " | |
31 | TARGET_FMT_lx "\n", db->pc_next); | |
32 | } | |
33 | } | |
34 | ||
35 | void translator_loop(const TranslatorOps *ops, DisasContextBase *db, | |
8b86d6d2 | 36 | CPUState *cpu, TranslationBlock *tb, int max_insns) |
bb2e0039 | 37 | { |
f9f1f56e | 38 | int bp_insn = 0; |
6ba6f818 | 39 | bool plugin_enabled; |
f9f1f56e | 40 | |
bb2e0039 LV |
41 | /* Initialize DisasContext */ |
42 | db->tb = tb; | |
43 | db->pc_first = tb->pc; | |
44 | db->pc_next = db->pc_first; | |
45 | db->is_jmp = DISAS_NEXT; | |
46 | db->num_insns = 0; | |
8b86d6d2 | 47 | db->max_insns = max_insns; |
bb2e0039 LV |
48 | db->singlestep_enabled = cpu->singlestep_enabled; |
49 | ||
b542683d | 50 | ops->init_disas_context(db, cpu); |
bb2e0039 LV |
51 | tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ |
52 | ||
53 | /* Reset the temp count so that we can identify leaks */ | |
54 | tcg_clear_temp_count(); | |
55 | ||
56 | /* Start translating. */ | |
57 | gen_tb_start(db->tb); | |
58 | ops->tb_start(db, cpu); | |
59 | tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ | |
60 | ||
6ba6f818 EC |
61 | plugin_enabled = plugin_gen_tb_start(cpu, tb); |
62 | ||
bb2e0039 LV |
63 | while (true) { |
64 | db->num_insns++; | |
65 | ops->insn_start(db, cpu); | |
66 | tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ | |
67 | ||
6ba6f818 EC |
68 | if (plugin_enabled) { |
69 | plugin_gen_insn_start(cpu, db); | |
70 | } | |
71 | ||
bb2e0039 | 72 | /* Pass breakpoint hits to target for further processing */ |
f9f1f56e PD |
73 | if (!db->singlestep_enabled |
74 | && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { | |
bb2e0039 LV |
75 | CPUBreakpoint *bp; |
76 | QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { | |
77 | if (bp->pc == db->pc_next) { | |
78 | if (ops->breakpoint_check(db, cpu, bp)) { | |
f9f1f56e | 79 | bp_insn = 1; |
bb2e0039 LV |
80 | break; |
81 | } | |
82 | } | |
83 | } | |
84 | /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate | |
85 | that only one more instruction is to be executed. Otherwise | |
86 | it should use DISAS_NORETURN when generating an exception, | |
87 | but may use a DISAS_TARGET_* value for Something Else. */ | |
88 | if (db->is_jmp > DISAS_TOO_MANY) { | |
89 | break; | |
90 | } | |
91 | } | |
92 | ||
93 | /* Disassemble one instruction. The translate_insn hook should | |
94 | update db->pc_next and db->is_jmp to indicate what should be | |
95 | done next -- either exiting this loop or locate the start of | |
96 | the next instruction. */ | |
b542683d EC |
97 | if (db->num_insns == db->max_insns |
98 | && (tb_cflags(db->tb) & CF_LAST_IO)) { | |
bb2e0039 LV |
99 | /* Accept I/O on the last instruction. */ |
100 | gen_io_start(); | |
101 | ops->translate_insn(db, cpu); | |
bb2e0039 LV |
102 | } else { |
103 | ops->translate_insn(db, cpu); | |
104 | } | |
105 | ||
106 | /* Stop translation if translate_insn so indicated. */ | |
107 | if (db->is_jmp != DISAS_NEXT) { | |
108 | break; | |
109 | } | |
110 | ||
6ba6f818 EC |
111 | /* |
112 | * We can't instrument after instructions that change control | |
113 | * flow although this only really affects post-load operations. | |
114 | */ | |
115 | if (plugin_enabled) { | |
116 | plugin_gen_insn_end(); | |
117 | } | |
118 | ||
bb2e0039 LV |
119 | /* Stop translation if the output buffer is full, |
120 | or we have executed all of the allowed instructions. */ | |
b542683d | 121 | if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { |
bb2e0039 LV |
122 | db->is_jmp = DISAS_TOO_MANY; |
123 | break; | |
124 | } | |
125 | } | |
126 | ||
127 | /* Emit code to exit the TB, as indicated by db->is_jmp. */ | |
128 | ops->tb_stop(db, cpu); | |
f9f1f56e | 129 | gen_tb_end(db->tb, db->num_insns - bp_insn); |
bb2e0039 | 130 | |
6ba6f818 EC |
131 | if (plugin_enabled) { |
132 | plugin_gen_tb_end(cpu); | |
133 | } | |
134 | ||
bb2e0039 LV |
135 | /* The disas_log hook may use these values rather than recompute. */ |
136 | db->tb->size = db->pc_next - db->pc_first; | |
137 | db->tb->icount = db->num_insns; | |
138 | ||
139 | #ifdef DEBUG_DISAS | |
140 | if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) | |
141 | && qemu_log_in_addr_range(db->pc_first)) { | |
fc59d2d8 | 142 | FILE *logfile = qemu_log_lock(); |
bb2e0039 LV |
143 | qemu_log("----------------\n"); |
144 | ops->disas_log(db, cpu); | |
145 | qemu_log("\n"); | |
fc59d2d8 | 146 | qemu_log_unlock(logfile); |
bb2e0039 LV |
147 | } |
148 | #endif | |
149 | } |