]> Git Repo - qemu.git/blame - accel/tcg/translator.c
cfi: Initial support for cfi-icall in QEMU
[qemu.git] / accel / tcg / translator.c
CommitLineData
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. */
27void 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
35void 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}
This page took 0.176054 seconds and 4 git commands to generate.