1
From f96f1df1c9b50914fb618369b747fff4ae66b72e Mon Sep 17 00:00:00 2001
2
From: Peter Maydell <peter.maydell@linaro.org>
3
Date: Tue, 17 Dec 2013 19:42:31 +0000
4
Subject: [PATCH 24/49] target-arm: Split A64 from A32/T32
5
gen_intermediate_code_internal()
7
The A32/T32 gen_intermediate_code_internal() is complicated because it
9
* conditionally executed instructions
12
* M profile exception-exit special casing
14
None of these apply to A64, so putting the "this is A64 so
15
call the A64 decoder" check in the middle of the A32/T32
16
loop is confusing and means the A64 decoder's handling of
17
things like conditional jump and singlestepping has to take
18
account of the conditional-execution jumps the main loop
21
Refactor the code to give A64 its own gen_intermediate_code_internal
24
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
25
Reviewed-by: Richard Henderson <rth@twiddle.net>
27
target-arm/translate-a64.c | 209 +++++++++++++++++++++++++++++++++++++++++++--
28
target-arm/translate.c | 62 ++++++--------
29
target-arm/translate.h | 20 ++++-
30
3 files changed, 246 insertions(+), 45 deletions(-)
32
Index: qemu/target-arm/translate-a64.c
33
===================================================================
34
--- qemu.orig/target-arm/translate-a64.c 2014-01-10 12:08:03.444166815 -0600
35
+++ qemu/target-arm/translate-a64.c 2014-01-10 12:08:03.436166815 -0600
37
#include "translate.h"
38
#include "qemu/host-utils.h"
40
+#include "exec/gen-icount.h"
45
@@ -106,7 +108,42 @@ static void gen_exception_insn(DisasCont
47
gen_a64_set_pc_im(s->pc - offset);
49
- s->is_jmp = DISAS_JUMP;
50
+ s->is_jmp = DISAS_EXC;
53
+static inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest)
55
+ /* No direct tb linking with singlestep or deterministic io */
56
+ if (s->singlestep_enabled || (s->tb->cflags & CF_LAST_IO)) {
60
+ /* Only link tbs from inside the same guest page */
61
+ if ((s->tb->pc & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
68
+static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
70
+ TranslationBlock *tb;
73
+ if (use_goto_tb(s, n, dest)) {
75
+ gen_a64_set_pc_im(dest);
76
+ tcg_gen_exit_tb((tcg_target_long)tb + n);
77
+ s->is_jmp = DISAS_TB_JUMP;
79
+ gen_a64_set_pc_im(dest);
80
+ if (s->singlestep_enabled) {
81
+ gen_exception(EXCP_DEBUG);
84
+ s->is_jmp = DISAS_JUMP;
88
static void real_unallocated_encoding(DisasContext *s)
89
@@ -120,7 +157,7 @@ static void real_unallocated_encoding(Di
90
real_unallocated_encoding(s); \
93
-void disas_a64_insn(CPUARMState *env, DisasContext *s)
94
+static void disas_a64_insn(CPUARMState *env, DisasContext *s)
98
@@ -133,9 +170,171 @@ void disas_a64_insn(CPUARMState *env, Di
99
unallocated_encoding(s);
104
- if (unlikely(s->singlestep_enabled) && (s->is_jmp == DISAS_TB_JUMP)) {
105
- /* go through the main loop for single step */
106
- s->is_jmp = DISAS_JUMP;
107
+void gen_intermediate_code_internal_a64(ARMCPU *cpu,
108
+ TranslationBlock *tb,
111
+ CPUState *cs = CPU(cpu);
112
+ CPUARMState *env = &cpu->env;
113
+ DisasContext dc1, *dc = &dc1;
115
+ uint16_t *gen_opc_end;
117
+ target_ulong pc_start;
118
+ target_ulong next_page_start;
126
+ gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE;
128
+ dc->is_jmp = DISAS_NEXT;
130
+ dc->singlestep_enabled = cs->singlestep_enabled;
135
+ dc->bswap_code = 0;
136
+ dc->condexec_mask = 0;
137
+ dc->condexec_cond = 0;
138
+#if !defined(CONFIG_USER_ONLY)
141
+ dc->vfp_enabled = 0;
143
+ dc->vec_stride = 0;
145
+ next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
148
+ max_insns = tb->cflags & CF_COUNT_MASK;
149
+ if (max_insns == 0) {
150
+ max_insns = CF_COUNT_MASK;
155
+ tcg_clear_temp_count();
158
+ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
159
+ QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
160
+ if (bp->pc == dc->pc) {
161
+ gen_exception_insn(dc, 0, EXCP_DEBUG);
162
+ /* Advance PC so that clearing the breakpoint will
163
+ invalidate this TB. */
165
+ goto done_generating;
171
+ j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf;
175
+ tcg_ctx.gen_opc_instr_start[lj++] = 0;
178
+ tcg_ctx.gen_opc_pc[lj] = dc->pc;
179
+ tcg_ctx.gen_opc_instr_start[lj] = 1;
180
+ tcg_ctx.gen_opc_icount[lj] = num_insns;
183
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
187
+ if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) {
188
+ tcg_gen_debug_insn_start(dc->pc);
191
+ disas_a64_insn(env, dc);
193
+ if (tcg_check_temp_count()) {
194
+ fprintf(stderr, "TCG temporary leak before "TARGET_FMT_lx"\n",
198
+ /* Translation stops when a conditional branch is encountered.
199
+ * Otherwise the subsequent code could get translated several times.
200
+ * Also stop translation when a page boundary is reached. This
201
+ * ensures prefetch aborts occur at the right place.
204
+ } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end &&
205
+ !cs->singlestep_enabled &&
207
+ dc->pc < next_page_start &&
208
+ num_insns < max_insns);
210
+ if (tb->cflags & CF_LAST_IO) {
214
+ if (unlikely(cs->singlestep_enabled) && dc->is_jmp != DISAS_EXC) {
215
+ /* Note that this means single stepping WFI doesn't halt the CPU.
216
+ * For conditional branch insns this is harmless unreachable code as
217
+ * gen_goto_tb() has already handled emitting the debug exception
218
+ * (and thus a tb-jump is not possible when singlestepping).
220
+ assert(dc->is_jmp != DISAS_TB_JUMP);
221
+ if (dc->is_jmp != DISAS_JUMP) {
222
+ gen_a64_set_pc_im(dc->pc);
224
+ gen_exception(EXCP_DEBUG);
226
+ switch (dc->is_jmp) {
228
+ gen_goto_tb(dc, 1, dc->pc);
233
+ /* indicate that the hash table must be used to find the next TB */
234
+ tcg_gen_exit_tb(0);
236
+ case DISAS_TB_JUMP:
241
+ /* This is a special case because we don't want to just halt the CPU
242
+ * if trying to debug across a WFI.
244
+ gen_helper_wfi(cpu_env);
250
+ gen_tb_end(tb, num_insns);
251
+ *tcg_ctx.gen_opc_ptr = INDEX_op_end;
254
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
255
+ qemu_log("----------------\n");
256
+ qemu_log("IN: %s\n", lookup_symbol(pc_start));
257
+ log_target_disas(env, pc_start, dc->pc - pc_start,
258
+ dc->thumb | (dc->bswap_code << 1));
263
+ j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf;
266
+ tcg_ctx.gen_opc_instr_start[lj++] = 0;
269
+ tb->size = dc->pc - pc_start;
270
+ tb->icount = num_insns;
273
Index: qemu/target-arm/translate.c
274
===================================================================
275
--- qemu.orig/target-arm/translate.c 2014-01-10 12:07:58.348166701 -0600
276
+++ qemu/target-arm/translate.c 2014-01-10 12:14:18.576175248 -0600
278
#include "translate.h"
279
static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE];
281
-#if defined(CONFIG_USER_ONLY)
282
-#define IS_USER(s) 1
284
-#define IS_USER(s) (s->user)
287
/* These instructions trap after executing, so defer them until after the
288
conditional execution state has been updated. */
293
+#if defined(CONFIG_USER_ONLY)
294
+#define IS_USER(s) 1
296
+#define IS_USER(s) (s->user)
300
/* We reuse the same 64-bit temporaries for efficiency. */
301
static TCGv_i64 cpu_V0, cpu_V1, cpu_M0;
302
@@ -907,11 +907,7 @@ static inline void gen_smc(CPUARMState *
304
static inline void gen_set_pc_im(DisasContext *s, target_ulong val)
307
- gen_a64_set_pc_im(val);
309
- tcg_gen_movi_i32(cpu_R[15], val);
311
+ tcg_gen_movi_i32(cpu_R[15], val);
314
/* Force a TB lookup after an instruction that changes the CPU state. */
315
@@ -10235,6 +10231,15 @@ static inline void gen_intermediate_code
318
/* generate intermediate code */
320
+ /* The A64 decoder has its own top level loop, because it doesn't need
321
+ * the A32/T32 complexity to do with conditional execution/IT blocks/etc.
323
+ if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
324
+ gen_intermediate_code_internal_a64(cpu, tb, search_pc);
331
@@ -10246,31 +10251,18 @@ static inline void gen_intermediate_code
332
dc->singlestep_enabled = cs->singlestep_enabled;
335
- if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) {
338
- dc->bswap_code = 0;
339
- dc->condexec_mask = 0;
340
- dc->condexec_cond = 0;
341
-#if !defined(CONFIG_USER_ONLY)
344
- dc->vfp_enabled = 0;
346
- dc->vec_stride = 0;
349
- dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
350
- dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
351
- dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
352
- dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
354
+ dc->thumb = ARM_TBFLAG_THUMB(tb->flags);
355
+ dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags);
356
+ dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1;
357
+ dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4;
358
#if !defined(CONFIG_USER_ONLY)
359
- dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
360
+ dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0);
362
- dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
363
- dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
364
- dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
366
+ dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags);
367
+ dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags);
368
+ dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags);
370
cpu_F0s = tcg_temp_new_i32();
371
cpu_F1s = tcg_temp_new_i32();
372
cpu_F0d = tcg_temp_new_i64();
373
@@ -10332,7 +10324,7 @@ static inline void gen_intermediate_code
375
#ifdef CONFIG_USER_ONLY
376
/* Intercept jump to the magic kernel page. */
377
- if (!dc->aarch64 && dc->pc >= 0xffff0000) {
378
+ if (dc->pc >= 0xffff0000) {
379
/* We always get here via a jump, so know we are not in a
380
conditional execution block. */
381
gen_exception(EXCP_KERNEL_TRAP);
382
@@ -10380,9 +10372,7 @@ static inline void gen_intermediate_code
383
tcg_gen_debug_insn_start(dc->pc);
387
- disas_a64_insn(env, dc);
388
- } else if (dc->thumb) {
390
disas_thumb_insn(env, dc);
391
if (dc->condexec_mask) {
392
dc->condexec_cond = (dc->condexec_cond & 0xe)
393
@@ -10584,8 +10574,9 @@ void restore_state_to_opc(CPUARMState *e
396
env->pc = tcg_ctx.gen_opc_pc[pc_pos];
397
+ env->condexec_bits = 0;
399
env->regs[15] = tcg_ctx.gen_opc_pc[pc_pos];
400
+ env->condexec_bits = gen_opc_condexec_bits[pc_pos];
402
- env->condexec_bits = gen_opc_condexec_bits[pc_pos];
404
Index: qemu/target-arm/translate.h
405
===================================================================
406
--- qemu.orig/target-arm/translate.h 2014-01-10 12:08:03.444166815 -0600
407
+++ qemu/target-arm/translate.h 2014-01-10 12:08:03.440166815 -0600
408
@@ -28,16 +28,32 @@ typedef struct DisasContext {
410
extern TCGv_ptr cpu_env;
412
+/* target-specific extra values for is_jmp */
413
+/* These instructions trap after executing, so the A32/T32 decoder must
414
+ * defer them until after the conditional execution state has been updated.
415
+ * WFI also needs special handling when single-stepping.
419
+/* For instructions which unconditionally cause an exception we can skip
420
+ * emitting unreachable code at the end of the TB in the A64 decoder
424
#ifdef TARGET_AARCH64
425
void a64_translate_init(void);
426
-void disas_a64_insn(CPUARMState *env, DisasContext *s);
427
+void gen_intermediate_code_internal_a64(ARMCPU *cpu,
428
+ TranslationBlock *tb,
430
void gen_a64_set_pc_im(uint64_t val);
432
static inline void a64_translate_init(void)
436
-static inline void disas_a64_insn(CPUARMState *env, DisasContext *s)
437
+static inline void gen_intermediate_code_internal_a64(ARMCPU *cpu,
438
+ TranslationBlock *tb,