diff options
-rw-r--r-- | exec-all.h | 8 | ||||
-rw-r--r-- | target-arm/it_helper.h | 83 | ||||
-rw-r--r-- | target-arm/translate.c | 64 |
3 files changed, 131 insertions, 24 deletions
@@ -172,6 +172,14 @@ struct TranslationBlock { #endif // CONFIG_MEMCHECK uint32_t icount; + + /* ITSTATE at the beginning of the translated block. + * This field is set to CPU's condexec_bits value when this TB is first + * translating. This field is then used to properly calculate ITSTATE when + * this TB gets "retranslated" because of an exception, or other + * conditions that cause gen_intermediate_code_pc to be called for this TB. + */ + uint8_t itstate; }; static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc) diff --git a/target-arm/it_helper.h b/target-arm/it_helper.h new file mode 100644 index 0000000..7982174 --- /dev/null +++ b/target-arm/it_helper.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2007-2010 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +/* + * Contains implementation of helper routines for IT block support. + */ + +#ifndef QEMU_TARGET_ARM_IT_HELPER_H +#define QEMU_TARGET_ARM_IT_HELPER_H + +/* Gets condition bits from ITSTATE. + * Return: + * ITSTATE condition bits in the low 4 bits of the returned value. + */ +static inline uint8_t +itstate_cond(uint8_t itstate) +{ + return (itstate >> 4) & 0xf; +} + +/* Checks if ITSTATE defines the last instruction in an IT block. + * Param: + * itstate - ITSTATE for an instruction in an IT block. + * Return: + * boolean: 1 if an instruction is the last instruction in its IT block, + * or zero if there are more instruction in the IT block. + */ +static inline int +itstate_is_last_it_insn(uint8_t itstate) +{ + return (itstate & 0x7) == 0; +} + +/* Checks if ITSTATE suggests that an IT block instruction is being translated. + * Return: + * boolean: 1 if an IT block instruction is being translated, or zero if + * a "normal" instruction is being translated. + */ +static inline int +itstate_is_in_it_block(uint8_t itstate) +{ + return (itstate & 0xff) != 0; +} + +/* Advances ITSTATE to the next instruction in an IT block. + * Param: + * itstate - ITSTATE of the currently executing instruction. + * Retunn: + * ITSTATE for the next instruction in an IT block, or zero is currently + * executing instruction was the last IT block instruction. + */ +static inline uint8_t +itstate_advance(uint8_t itstate) +{ + if (itstate_is_last_it_insn(itstate)) { + return 0; + } else { + return (itstate & 0xe0) | ((itstate & 0xf) << 1); + } +} + +/* Checks if given THUMB instruction is an IT instruction. + * Param: + * insn - THUMB instruction to check. + * Return: + * boolean: 1 if instruction is IT instruction, or zero otherwise. + */ +static inline int +is_it_insn(uint16_t insn) +{ + return (insn & 0xff00) == 0xbf00 && (insn & 0x000f) != 0; +} + +#endif // QEMU_TARGET_ARM_IT_HELPER_H diff --git a/target-arm/translate.c b/target-arm/translate.c index 4432c7b..6c8ebde 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -30,6 +30,7 @@ #include "disas.h" #include "tcg-op.h" #include "qemu-log.h" +#include "it_helper.h" #ifdef CONFIG_TRACE #include "trace.h" @@ -57,8 +58,15 @@ typedef struct DisasContext { int condlabel; /* Thumb-2 condtional execution bits. */ int condexec_mask; - int condexec_cond; - int condexec_mask_prev; /* mask at start of instruction/block */ + /* Set to 1 iff currently translated instruction is IT instruction. + * This flag is then used to properly adjust condexec_mask field after + * instruction has been translated*/ + int is_it_insn; + /* Set to 1 iff condexec_mask should be updated to CPU's condexec_bits. + * This flag is set to 1 if condexec_mask field has changed as the result + * of an instruction translation, so it must be saved to CPU's condexec_bits + * field after translated instruction has been executed. */ + int save_condexec_mask; struct TranslationBlock *tb; int singlestep_enabled; int thumb; @@ -3524,16 +3532,11 @@ static void gen_rfe(DisasContext *s, TCGv pc, TCGv cpsr) static inline void gen_set_condexec (DisasContext *s) { - if (s->condexec_mask) { - uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); + if (s->save_condexec_mask) { TCGv tmp = new_tmp(); - tcg_gen_movi_i32(tmp, val); - store_cpu_field(tmp, condexec_bits); - } - else if (s->condexec_mask_prev != 0) { - TCGv tmp = new_tmp(); - tcg_gen_movi_i32(tmp, 0); + tcg_gen_movi_i32(tmp, s->condexec_mask); store_cpu_field(tmp, condexec_bits); + s->save_condexec_mask = 0; } } @@ -8180,10 +8183,12 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s) TCGv tmp2; TCGv addr; - if (s->condexec_mask) { - cond = s->condexec_cond; + if (itstate_is_in_it_block(s->condexec_mask)) { + /* We're translating an IT block instruction. Make it branch as + * requried by the current ITSTATE. */ + const uint32_t it_cond = itstate_cond(s->condexec_mask); s->condlabel = gen_new_label(); - gen_test_cc(cond ^ 1, s->condlabel); + gen_test_cc(it_cond ^ 1, s->condlabel); s->condjmp = 1; } @@ -8702,8 +8707,9 @@ static void disas_thumb_insn(CPUState *env, DisasContext *s) break; } /* If Then. */ - s->condexec_cond = (insn >> 4) & 0xe; - s->condexec_mask = insn & 0x1f; + s->condexec_mask = insn & 0xff; + /* Let the translator know that this was an IT instruction. */ + s->is_it_insn = 1; /* No actual code generated for this insn, just setup state. */ break; @@ -8879,9 +8885,11 @@ static inline void gen_intermediate_code_internal(CPUState *env, dc->singlestep_enabled = env->singlestep_enabled; dc->condjmp = 0; dc->thumb = env->thumb; - dc->condexec_mask = (env->condexec_bits & 0xf) << 1; - dc->condexec_mask_prev = dc->condexec_mask; - dc->condexec_cond = env->condexec_bits >> 4; + if (!search_pc) { + /* Store current ITSTATE value. */ + tb->itstate = env->condexec_bits; + } + dc->condexec_mask = tb->itstate; #if !defined(CONFIG_USER_ONLY) if (IS_M(env)) { dc->user = ((env->v7m.exception == 0) && (env->v7m.control & 1)); @@ -8916,6 +8924,9 @@ static inline void gen_intermediate_code_internal(CPUState *env, #endif do { + /* Clear IT related flags at the beginning of insn translation. */ + dc->save_condexec_mask = 0; + dc->is_it_insn = 0; #ifdef CONFIG_USER_ONLY /* Intercept jump to the magic kernel page. */ if (dc->pc >= 0xffff0000) { @@ -8975,13 +8986,13 @@ static inline void gen_intermediate_code_internal(CPUState *env, if (env->thumb) { disas_thumb_insn(env, dc); - dc->condexec_mask_prev = dc->condexec_mask; if (dc->condexec_mask) { - dc->condexec_cond = (dc->condexec_cond & 0xe) - | ((dc->condexec_mask >> 4) & 1); - dc->condexec_mask = (dc->condexec_mask << 1) & 0x1f; - if (dc->condexec_mask == 0) { - dc->condexec_cond = 0; + /* We just translated an IT-related instruction. We must save + * updated ITSTATE into CPU's condexec_bits field at the end + * this instruction translation. */ + dc->save_condexec_mask = 1; + if (!dc->is_it_insn) { + dc->condexec_mask = itstate_advance(dc->condexec_mask); } } } else { @@ -8997,6 +9008,11 @@ static inline void gen_intermediate_code_internal(CPUState *env, gen_set_label(dc->condlabel); dc->condjmp = 0; } + + /* Update CPU's condexec_bits after we've moved beyond executed + * command for both, "fall through" and "branch" cases. */ + gen_set_condexec(dc); + /* Translation stops when a conditional branch is encountered. * Otherwise the subsequent code could get translated several times. * Also stop translation when a page boundary is reached. This |