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