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