| /* |
| * ARM translation |
| * |
| * Copyright (c) 2003 Fabrice Bellard |
| * Copyright (c) 2005-2007 CodeSourcery |
| * Copyright (c) 2007 OpenedHand, Ltd. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| #include "qemu/osdep.h" |
| |
| #include "cpu.h" |
| #include "internals.h" |
| #include "disas/disas.h" |
| #include "exec/exec-all.h" |
| #include "tcg/tcg-op.h" |
| #include "tcg/tcg-op-gvec.h" |
| #include "qemu/log.h" |
| #include "qemu/bitops.h" |
| #include "arm_ldst.h" |
| #include "hw/semihosting/semihost.h" |
| |
| #include "exec/helper-proto.h" |
| #include "exec/helper-gen.h" |
| |
| #include "trace-tcg.h" |
| #include "exec/log.h" |
| |
| |
| #define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) |
| #define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) |
| /* currently all emulated v5 cores are also v5TE, so don't bother */ |
| #define ENABLE_ARCH_5TE arm_dc_feature(s, ARM_FEATURE_V5) |
| #define ENABLE_ARCH_5J dc_isar_feature(aa32_jazelle, s) |
| #define ENABLE_ARCH_6 arm_dc_feature(s, ARM_FEATURE_V6) |
| #define ENABLE_ARCH_6K arm_dc_feature(s, ARM_FEATURE_V6K) |
| #define ENABLE_ARCH_6T2 arm_dc_feature(s, ARM_FEATURE_THUMB2) |
| #define ENABLE_ARCH_7 arm_dc_feature(s, ARM_FEATURE_V7) |
| #define ENABLE_ARCH_8 arm_dc_feature(s, ARM_FEATURE_V8) |
| |
| #define ARCH(x) do { if (!ENABLE_ARCH_##x) goto illegal_op; } while(0) |
| |
| #include "translate.h" |
| |
| #if defined(CONFIG_USER_ONLY) |
| #define IS_USER(s) 1 |
| #else |
| #define IS_USER(s) (s->user) |
| #endif |
| |
| /* We reuse the same 64-bit temporaries for efficiency. */ |
| static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; |
| static TCGv_i32 cpu_R[16]; |
| TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; |
| TCGv_i64 cpu_exclusive_addr; |
| TCGv_i64 cpu_exclusive_val; |
| |
| #include "exec/gen-icount.h" |
| |
| static const char * const regnames[] = |
| { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
| "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" }; |
| |
| /* Function prototypes for gen_ functions calling Neon helpers. */ |
| typedef void NeonGenThreeOpEnvFn(TCGv_i32, TCGv_env, TCGv_i32, |
| TCGv_i32, TCGv_i32); |
| /* Function prototypes for gen_ functions for fix point conversions */ |
| typedef void VFPGenFixPointFn(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); |
| |
| /* initialize TCG globals. */ |
| void arm_translate_init(void) |
| { |
| int i; |
| |
| for (i = 0; i < 16; i++) { |
| cpu_R[i] = tcg_global_mem_new_i32(cpu_env, |
| offsetof(CPUARMState, regs[i]), |
| regnames[i]); |
| } |
| cpu_CF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, CF), "CF"); |
| cpu_NF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, NF), "NF"); |
| cpu_VF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, VF), "VF"); |
| cpu_ZF = tcg_global_mem_new_i32(cpu_env, offsetof(CPUARMState, ZF), "ZF"); |
| |
| cpu_exclusive_addr = tcg_global_mem_new_i64(cpu_env, |
| offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); |
| cpu_exclusive_val = tcg_global_mem_new_i64(cpu_env, |
| offsetof(CPUARMState, exclusive_val), "exclusive_val"); |
| |
| a64_translate_init(); |
| } |
| |
| /* Flags for the disas_set_da_iss info argument: |
| * lower bits hold the Rt register number, higher bits are flags. |
| */ |
| typedef enum ISSInfo { |
| ISSNone = 0, |
| ISSRegMask = 0x1f, |
| ISSInvalid = (1 << 5), |
| ISSIsAcqRel = (1 << 6), |
| ISSIsWrite = (1 << 7), |
| ISSIs16Bit = (1 << 8), |
| } ISSInfo; |
| |
| /* Save the syndrome information for a Data Abort */ |
| static void disas_set_da_iss(DisasContext *s, MemOp memop, ISSInfo issinfo) |
| { |
| uint32_t syn; |
| int sas = memop & MO_SIZE; |
| bool sse = memop & MO_SIGN; |
| bool is_acqrel = issinfo & ISSIsAcqRel; |
| bool is_write = issinfo & ISSIsWrite; |
| bool is_16bit = issinfo & ISSIs16Bit; |
| int srt = issinfo & ISSRegMask; |
| |
| if (issinfo & ISSInvalid) { |
| /* Some callsites want to conditionally provide ISS info, |
| * eg "only if this was not a writeback" |
| */ |
| return; |
| } |
| |
| if (srt == 15) { |
| /* For AArch32, insns where the src/dest is R15 never generate |
| * ISS information. Catching that here saves checking at all |
| * the call sites. |
| */ |
| return; |
| } |
| |
| syn = syn_data_abort_with_iss(0, sas, sse, srt, 0, is_acqrel, |
| 0, 0, 0, is_write, 0, is_16bit); |
| disas_set_insn_syndrome(s, syn); |
| } |
| |
| static inline int get_a32_user_mem_index(DisasContext *s) |
| { |
| /* Return the core mmu_idx to use for A32/T32 "unprivileged load/store" |
| * insns: |
| * if PL2, UNPREDICTABLE (we choose to implement as if PL0) |
| * otherwise, access as if at PL0. |
| */ |
| switch (s->mmu_idx) { |
| case ARMMMUIdx_E2: /* this one is UNPREDICTABLE */ |
| case ARMMMUIdx_E10_0: |
| case ARMMMUIdx_E10_1: |
| case ARMMMUIdx_E10_1_PAN: |
| return arm_to_core_mmu_idx(ARMMMUIdx_E10_0); |
| case ARMMMUIdx_SE3: |
| case ARMMMUIdx_SE10_0: |
| case ARMMMUIdx_SE10_1: |
| case ARMMMUIdx_SE10_1_PAN: |
| return arm_to_core_mmu_idx(ARMMMUIdx_SE10_0); |
| case ARMMMUIdx_MUser: |
| case ARMMMUIdx_MPriv: |
| return arm_to_core_mmu_idx(ARMMMUIdx_MUser); |
| case ARMMMUIdx_MUserNegPri: |
| case ARMMMUIdx_MPrivNegPri: |
| return arm_to_core_mmu_idx(ARMMMUIdx_MUserNegPri); |
| case ARMMMUIdx_MSUser: |
| case ARMMMUIdx_MSPriv: |
| return arm_to_core_mmu_idx(ARMMMUIdx_MSUser); |
| case ARMMMUIdx_MSUserNegPri: |
| case ARMMMUIdx_MSPrivNegPri: |
| return arm_to_core_mmu_idx(ARMMMUIdx_MSUserNegPri); |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| static inline TCGv_i32 load_cpu_offset(int offset) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_ld_i32(tmp, cpu_env, offset); |
| return tmp; |
| } |
| |
| #define load_cpu_field(name) load_cpu_offset(offsetof(CPUARMState, name)) |
| |
| static inline void store_cpu_offset(TCGv_i32 var, int offset) |
| { |
| tcg_gen_st_i32(var, cpu_env, offset); |
| tcg_temp_free_i32(var); |
| } |
| |
| #define store_cpu_field(var, name) \ |
| store_cpu_offset(var, offsetof(CPUARMState, name)) |
| |
| /* The architectural value of PC. */ |
| static uint32_t read_pc(DisasContext *s) |
| { |
| return s->pc_curr + (s->thumb ? 4 : 8); |
| } |
| |
| /* Set a variable to the value of a CPU register. */ |
| static void load_reg_var(DisasContext *s, TCGv_i32 var, int reg) |
| { |
| if (reg == 15) { |
| tcg_gen_movi_i32(var, read_pc(s)); |
| } else { |
| tcg_gen_mov_i32(var, cpu_R[reg]); |
| } |
| } |
| |
| /* Create a new temporary and set it to the value of a CPU register. */ |
| static inline TCGv_i32 load_reg(DisasContext *s, int reg) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| load_reg_var(s, tmp, reg); |
| return tmp; |
| } |
| |
| /* |
| * Create a new temp, REG + OFS, except PC is ALIGN(PC, 4). |
| * This is used for load/store for which use of PC implies (literal), |
| * or ADD that implies ADR. |
| */ |
| static TCGv_i32 add_reg_for_lit(DisasContext *s, int reg, int ofs) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| |
| if (reg == 15) { |
| tcg_gen_movi_i32(tmp, (read_pc(s) & ~3) + ofs); |
| } else { |
| tcg_gen_addi_i32(tmp, cpu_R[reg], ofs); |
| } |
| return tmp; |
| } |
| |
| /* Set a CPU register. The source must be a temporary and will be |
| marked as dead. */ |
| static void store_reg(DisasContext *s, int reg, TCGv_i32 var) |
| { |
| if (reg == 15) { |
| /* In Thumb mode, we must ignore bit 0. |
| * In ARM mode, for ARMv4 and ARMv5, it is UNPREDICTABLE if bits [1:0] |
| * are not 0b00, but for ARMv6 and above, we must ignore bits [1:0]. |
| * We choose to ignore [1:0] in ARM mode for all architecture versions. |
| */ |
| tcg_gen_andi_i32(var, var, s->thumb ? ~1 : ~3); |
| s->base.is_jmp = DISAS_JUMP; |
| } |
| tcg_gen_mov_i32(cpu_R[reg], var); |
| tcg_temp_free_i32(var); |
| } |
| |
| /* |
| * Variant of store_reg which applies v8M stack-limit checks before updating |
| * SP. If the check fails this will result in an exception being taken. |
| * We disable the stack checks for CONFIG_USER_ONLY because we have |
| * no idea what the stack limits should be in that case. |
| * If stack checking is not being done this just acts like store_reg(). |
| */ |
| static void store_sp_checked(DisasContext *s, TCGv_i32 var) |
| { |
| #ifndef CONFIG_USER_ONLY |
| if (s->v8m_stackcheck) { |
| gen_helper_v8m_stackcheck(cpu_env, var); |
| } |
| #endif |
| store_reg(s, 13, var); |
| } |
| |
| /* Value extensions. */ |
| #define gen_uxtb(var) tcg_gen_ext8u_i32(var, var) |
| #define gen_uxth(var) tcg_gen_ext16u_i32(var, var) |
| #define gen_sxtb(var) tcg_gen_ext8s_i32(var, var) |
| #define gen_sxth(var) tcg_gen_ext16s_i32(var, var) |
| |
| #define gen_sxtb16(var) gen_helper_sxtb16(var, var) |
| #define gen_uxtb16(var) gen_helper_uxtb16(var, var) |
| |
| |
| static inline void gen_set_cpsr(TCGv_i32 var, uint32_t mask) |
| { |
| TCGv_i32 tmp_mask = tcg_const_i32(mask); |
| gen_helper_cpsr_write(cpu_env, var, tmp_mask); |
| tcg_temp_free_i32(tmp_mask); |
| } |
| /* Set NZCV flags from the high 4 bits of var. */ |
| #define gen_set_nzcv(var) gen_set_cpsr(var, CPSR_NZCV) |
| |
| static void gen_exception_internal(int excp) |
| { |
| TCGv_i32 tcg_excp = tcg_const_i32(excp); |
| |
| assert(excp_is_internal(excp)); |
| gen_helper_exception_internal(cpu_env, tcg_excp); |
| tcg_temp_free_i32(tcg_excp); |
| } |
| |
| static void gen_step_complete_exception(DisasContext *s) |
| { |
| /* We just completed step of an insn. Move from Active-not-pending |
| * to Active-pending, and then also take the swstep exception. |
| * This corresponds to making the (IMPDEF) choice to prioritize |
| * swstep exceptions over asynchronous exceptions taken to an exception |
| * level where debug is disabled. This choice has the advantage that |
| * we do not need to maintain internal state corresponding to the |
| * ISV/EX syndrome bits between completion of the step and generation |
| * of the exception, and our syndrome information is always correct. |
| */ |
| gen_ss_advance(s); |
| gen_swstep_exception(s, 1, s->is_ldex); |
| s->base.is_jmp = DISAS_NORETURN; |
| } |
| |
| static void gen_singlestep_exception(DisasContext *s) |
| { |
| /* Generate the right kind of exception for singlestep, which is |
| * either the architectural singlestep or EXCP_DEBUG for QEMU's |
| * gdb singlestepping. |
| */ |
| if (s->ss_active) { |
| gen_step_complete_exception(s); |
| } else { |
| gen_exception_internal(EXCP_DEBUG); |
| } |
| } |
| |
| static inline bool is_singlestepping(DisasContext *s) |
| { |
| /* Return true if we are singlestepping either because of |
| * architectural singlestep or QEMU gdbstub singlestep. This does |
| * not include the command line '-singlestep' mode which is rather |
| * misnamed as it only means "one instruction per TB" and doesn't |
| * affect the code we generate. |
| */ |
| return s->base.singlestep_enabled || s->ss_active; |
| } |
| |
| static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b) |
| { |
| TCGv_i32 tmp1 = tcg_temp_new_i32(); |
| TCGv_i32 tmp2 = tcg_temp_new_i32(); |
| tcg_gen_ext16s_i32(tmp1, a); |
| tcg_gen_ext16s_i32(tmp2, b); |
| tcg_gen_mul_i32(tmp1, tmp1, tmp2); |
| tcg_temp_free_i32(tmp2); |
| tcg_gen_sari_i32(a, a, 16); |
| tcg_gen_sari_i32(b, b, 16); |
| tcg_gen_mul_i32(b, b, a); |
| tcg_gen_mov_i32(a, tmp1); |
| tcg_temp_free_i32(tmp1); |
| } |
| |
| /* Byteswap each halfword. */ |
| static void gen_rev16(TCGv_i32 dest, TCGv_i32 var) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| TCGv_i32 mask = tcg_const_i32(0x00ff00ff); |
| tcg_gen_shri_i32(tmp, var, 8); |
| tcg_gen_and_i32(tmp, tmp, mask); |
| tcg_gen_and_i32(var, var, mask); |
| tcg_gen_shli_i32(var, var, 8); |
| tcg_gen_or_i32(dest, var, tmp); |
| tcg_temp_free_i32(mask); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| /* Byteswap low halfword and sign extend. */ |
| static void gen_revsh(TCGv_i32 dest, TCGv_i32 var) |
| { |
| tcg_gen_ext16u_i32(var, var); |
| tcg_gen_bswap16_i32(var, var); |
| tcg_gen_ext16s_i32(dest, var); |
| } |
| |
| /* 32x32->64 multiply. Marks inputs as dead. */ |
| static TCGv_i64 gen_mulu_i64_i32(TCGv_i32 a, TCGv_i32 b) |
| { |
| TCGv_i32 lo = tcg_temp_new_i32(); |
| TCGv_i32 hi = tcg_temp_new_i32(); |
| TCGv_i64 ret; |
| |
| tcg_gen_mulu2_i32(lo, hi, a, b); |
| tcg_temp_free_i32(a); |
| tcg_temp_free_i32(b); |
| |
| ret = tcg_temp_new_i64(); |
| tcg_gen_concat_i32_i64(ret, lo, hi); |
| tcg_temp_free_i32(lo); |
| tcg_temp_free_i32(hi); |
| |
| return ret; |
| } |
| |
| static TCGv_i64 gen_muls_i64_i32(TCGv_i32 a, TCGv_i32 b) |
| { |
| TCGv_i32 lo = tcg_temp_new_i32(); |
| TCGv_i32 hi = tcg_temp_new_i32(); |
| TCGv_i64 ret; |
| |
| tcg_gen_muls2_i32(lo, hi, a, b); |
| tcg_temp_free_i32(a); |
| tcg_temp_free_i32(b); |
| |
| ret = tcg_temp_new_i64(); |
| tcg_gen_concat_i32_i64(ret, lo, hi); |
| tcg_temp_free_i32(lo); |
| tcg_temp_free_i32(hi); |
| |
| return ret; |
| } |
| |
| /* Swap low and high halfwords. */ |
| static void gen_swap_half(TCGv_i32 var) |
| { |
| tcg_gen_rotri_i32(var, var, 16); |
| } |
| |
| /* Dual 16-bit add. Result placed in t0 and t1 is marked as dead. |
| tmp = (t0 ^ t1) & 0x8000; |
| t0 &= ~0x8000; |
| t1 &= ~0x8000; |
| t0 = (t0 + t1) ^ tmp; |
| */ |
| |
| static void gen_add16(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, t0, t1); |
| tcg_gen_andi_i32(tmp, tmp, 0x8000); |
| tcg_gen_andi_i32(t0, t0, ~0x8000); |
| tcg_gen_andi_i32(t1, t1, ~0x8000); |
| tcg_gen_add_i32(t0, t0, t1); |
| tcg_gen_xor_i32(dest, t0, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| /* Set N and Z flags from var. */ |
| static inline void gen_logic_CC(TCGv_i32 var) |
| { |
| tcg_gen_mov_i32(cpu_NF, var); |
| tcg_gen_mov_i32(cpu_ZF, var); |
| } |
| |
| /* dest = T0 + T1 + CF. */ |
| static void gen_add_carry(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| tcg_gen_add_i32(dest, t0, t1); |
| tcg_gen_add_i32(dest, dest, cpu_CF); |
| } |
| |
| /* dest = T0 - T1 + CF - 1. */ |
| static void gen_sub_carry(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| tcg_gen_sub_i32(dest, t0, t1); |
| tcg_gen_add_i32(dest, dest, cpu_CF); |
| tcg_gen_subi_i32(dest, dest, 1); |
| } |
| |
| /* dest = T0 + T1. Compute C, N, V and Z flags */ |
| static void gen_add_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_movi_i32(tmp, 0); |
| tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, t1, tmp); |
| tcg_gen_mov_i32(cpu_ZF, cpu_NF); |
| tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); |
| tcg_gen_xor_i32(tmp, t0, t1); |
| tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); |
| tcg_temp_free_i32(tmp); |
| tcg_gen_mov_i32(dest, cpu_NF); |
| } |
| |
| /* dest = T0 + T1 + CF. Compute C, N, V and Z flags */ |
| static void gen_adc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| if (TCG_TARGET_HAS_add2_i32) { |
| tcg_gen_movi_i32(tmp, 0); |
| tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, cpu_CF, tmp); |
| tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1, tmp); |
| } else { |
| TCGv_i64 q0 = tcg_temp_new_i64(); |
| TCGv_i64 q1 = tcg_temp_new_i64(); |
| tcg_gen_extu_i32_i64(q0, t0); |
| tcg_gen_extu_i32_i64(q1, t1); |
| tcg_gen_add_i64(q0, q0, q1); |
| tcg_gen_extu_i32_i64(q1, cpu_CF); |
| tcg_gen_add_i64(q0, q0, q1); |
| tcg_gen_extr_i64_i32(cpu_NF, cpu_CF, q0); |
| tcg_temp_free_i64(q0); |
| tcg_temp_free_i64(q1); |
| } |
| tcg_gen_mov_i32(cpu_ZF, cpu_NF); |
| tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); |
| tcg_gen_xor_i32(tmp, t0, t1); |
| tcg_gen_andc_i32(cpu_VF, cpu_VF, tmp); |
| tcg_temp_free_i32(tmp); |
| tcg_gen_mov_i32(dest, cpu_NF); |
| } |
| |
| /* dest = T0 - T1. Compute C, N, V and Z flags */ |
| static void gen_sub_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| TCGv_i32 tmp; |
| tcg_gen_sub_i32(cpu_NF, t0, t1); |
| tcg_gen_mov_i32(cpu_ZF, cpu_NF); |
| tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0, t1); |
| tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, t0, t1); |
| tcg_gen_and_i32(cpu_VF, cpu_VF, tmp); |
| tcg_temp_free_i32(tmp); |
| tcg_gen_mov_i32(dest, cpu_NF); |
| } |
| |
| /* dest = T0 + ~T1 + CF. Compute C, N, V and Z flags */ |
| static void gen_sbc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_not_i32(tmp, t1); |
| gen_adc_CC(dest, t0, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| #define GEN_SHIFT(name) \ |
| static void gen_##name(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) \ |
| { \ |
| TCGv_i32 tmp1, tmp2, tmp3; \ |
| tmp1 = tcg_temp_new_i32(); \ |
| tcg_gen_andi_i32(tmp1, t1, 0xff); \ |
| tmp2 = tcg_const_i32(0); \ |
| tmp3 = tcg_const_i32(0x1f); \ |
| tcg_gen_movcond_i32(TCG_COND_GTU, tmp2, tmp1, tmp3, tmp2, t0); \ |
| tcg_temp_free_i32(tmp3); \ |
| tcg_gen_andi_i32(tmp1, tmp1, 0x1f); \ |
| tcg_gen_##name##_i32(dest, tmp2, tmp1); \ |
| tcg_temp_free_i32(tmp2); \ |
| tcg_temp_free_i32(tmp1); \ |
| } |
| GEN_SHIFT(shl) |
| GEN_SHIFT(shr) |
| #undef GEN_SHIFT |
| |
| static void gen_sar(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) |
| { |
| TCGv_i32 tmp1, tmp2; |
| tmp1 = tcg_temp_new_i32(); |
| tcg_gen_andi_i32(tmp1, t1, 0xff); |
| tmp2 = tcg_const_i32(0x1f); |
| tcg_gen_movcond_i32(TCG_COND_GTU, tmp1, tmp1, tmp2, tmp2, tmp1); |
| tcg_temp_free_i32(tmp2); |
| tcg_gen_sar_i32(dest, t0, tmp1); |
| tcg_temp_free_i32(tmp1); |
| } |
| |
| static void shifter_out_im(TCGv_i32 var, int shift) |
| { |
| tcg_gen_extract_i32(cpu_CF, var, shift, 1); |
| } |
| |
| /* Shift by immediate. Includes special handling for shift == 0. */ |
| static inline void gen_arm_shift_im(TCGv_i32 var, int shiftop, |
| int shift, int flags) |
| { |
| switch (shiftop) { |
| case 0: /* LSL */ |
| if (shift != 0) { |
| if (flags) |
| shifter_out_im(var, 32 - shift); |
| tcg_gen_shli_i32(var, var, shift); |
| } |
| break; |
| case 1: /* LSR */ |
| if (shift == 0) { |
| if (flags) { |
| tcg_gen_shri_i32(cpu_CF, var, 31); |
| } |
| tcg_gen_movi_i32(var, 0); |
| } else { |
| if (flags) |
| shifter_out_im(var, shift - 1); |
| tcg_gen_shri_i32(var, var, shift); |
| } |
| break; |
| case 2: /* ASR */ |
| if (shift == 0) |
| shift = 32; |
| if (flags) |
| shifter_out_im(var, shift - 1); |
| if (shift == 32) |
| shift = 31; |
| tcg_gen_sari_i32(var, var, shift); |
| break; |
| case 3: /* ROR/RRX */ |
| if (shift != 0) { |
| if (flags) |
| shifter_out_im(var, shift - 1); |
| tcg_gen_rotri_i32(var, var, shift); break; |
| } else { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_shli_i32(tmp, cpu_CF, 31); |
| if (flags) |
| shifter_out_im(var, 0); |
| tcg_gen_shri_i32(var, var, 1); |
| tcg_gen_or_i32(var, var, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| } |
| }; |
| |
| static inline void gen_arm_shift_reg(TCGv_i32 var, int shiftop, |
| TCGv_i32 shift, int flags) |
| { |
| if (flags) { |
| switch (shiftop) { |
| case 0: gen_helper_shl_cc(var, cpu_env, var, shift); break; |
| case 1: gen_helper_shr_cc(var, cpu_env, var, shift); break; |
| case 2: gen_helper_sar_cc(var, cpu_env, var, shift); break; |
| case 3: gen_helper_ror_cc(var, cpu_env, var, shift); break; |
| } |
| } else { |
| switch (shiftop) { |
| case 0: |
| gen_shl(var, var, shift); |
| break; |
| case 1: |
| gen_shr(var, var, shift); |
| break; |
| case 2: |
| gen_sar(var, var, shift); |
| break; |
| case 3: tcg_gen_andi_i32(shift, shift, 0x1f); |
| tcg_gen_rotr_i32(var, var, shift); break; |
| } |
| } |
| tcg_temp_free_i32(shift); |
| } |
| |
| /* |
| * Generate a conditional based on ARM condition code cc. |
| * This is common between ARM and Aarch64 targets. |
| */ |
| void arm_test_cc(DisasCompare *cmp, int cc) |
| { |
| TCGv_i32 value; |
| TCGCond cond; |
| bool global = true; |
| |
| switch (cc) { |
| case 0: /* eq: Z */ |
| case 1: /* ne: !Z */ |
| cond = TCG_COND_EQ; |
| value = cpu_ZF; |
| break; |
| |
| case 2: /* cs: C */ |
| case 3: /* cc: !C */ |
| cond = TCG_COND_NE; |
| value = cpu_CF; |
| break; |
| |
| case 4: /* mi: N */ |
| case 5: /* pl: !N */ |
| cond = TCG_COND_LT; |
| value = cpu_NF; |
| break; |
| |
| case 6: /* vs: V */ |
| case 7: /* vc: !V */ |
| cond = TCG_COND_LT; |
| value = cpu_VF; |
| break; |
| |
| case 8: /* hi: C && !Z */ |
| case 9: /* ls: !C || Z -> !(C && !Z) */ |
| cond = TCG_COND_NE; |
| value = tcg_temp_new_i32(); |
| global = false; |
| /* CF is 1 for C, so -CF is an all-bits-set mask for C; |
| ZF is non-zero for !Z; so AND the two subexpressions. */ |
| tcg_gen_neg_i32(value, cpu_CF); |
| tcg_gen_and_i32(value, value, cpu_ZF); |
| break; |
| |
| case 10: /* ge: N == V -> N ^ V == 0 */ |
| case 11: /* lt: N != V -> N ^ V != 0 */ |
| /* Since we're only interested in the sign bit, == 0 is >= 0. */ |
| cond = TCG_COND_GE; |
| value = tcg_temp_new_i32(); |
| global = false; |
| tcg_gen_xor_i32(value, cpu_VF, cpu_NF); |
| break; |
| |
| case 12: /* gt: !Z && N == V */ |
| case 13: /* le: Z || N != V */ |
| cond = TCG_COND_NE; |
| value = tcg_temp_new_i32(); |
| global = false; |
| /* (N == V) is equal to the sign bit of ~(NF ^ VF). Propagate |
| * the sign bit then AND with ZF to yield the result. */ |
| tcg_gen_xor_i32(value, cpu_VF, cpu_NF); |
| tcg_gen_sari_i32(value, value, 31); |
| tcg_gen_andc_i32(value, cpu_ZF, value); |
| break; |
| |
| case 14: /* always */ |
| case 15: /* always */ |
| /* Use the ALWAYS condition, which will fold early. |
| * It doesn't matter what we use for the value. */ |
| cond = TCG_COND_ALWAYS; |
| value = cpu_ZF; |
| goto no_invert; |
| |
| default: |
| fprintf(stderr, "Bad condition code 0x%x\n", cc); |
| abort(); |
| } |
| |
| if (cc & 1) { |
| cond = tcg_invert_cond(cond); |
| } |
| |
| no_invert: |
| cmp->cond = cond; |
| cmp->value = value; |
| cmp->value_global = global; |
| } |
| |
| void arm_free_cc(DisasCompare *cmp) |
| { |
| if (!cmp->value_global) { |
| tcg_temp_free_i32(cmp->value); |
| } |
| } |
| |
| void arm_jump_cc(DisasCompare *cmp, TCGLabel *label) |
| { |
| tcg_gen_brcondi_i32(cmp->cond, cmp->value, 0, label); |
| } |
| |
| void arm_gen_test_cc(int cc, TCGLabel *label) |
| { |
| DisasCompare cmp; |
| arm_test_cc(&cmp, cc); |
| arm_jump_cc(&cmp, label); |
| arm_free_cc(&cmp); |
| } |
| |
| static inline void gen_set_condexec(DisasContext *s) |
| { |
| if (s->condexec_mask) { |
| uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_movi_i32(tmp, val); |
| store_cpu_field(tmp, condexec_bits); |
| } |
| } |
| |
| static inline void gen_set_pc_im(DisasContext *s, target_ulong val) |
| { |
| tcg_gen_movi_i32(cpu_R[15], val); |
| } |
| |
| /* Set PC and Thumb state from var. var is marked as dead. */ |
| static inline void gen_bx(DisasContext *s, TCGv_i32 var) |
| { |
| s->base.is_jmp = DISAS_JUMP; |
| tcg_gen_andi_i32(cpu_R[15], var, ~1); |
| tcg_gen_andi_i32(var, var, 1); |
| store_cpu_field(var, thumb); |
| } |
| |
| /* |
| * Set PC and Thumb state from var. var is marked as dead. |
| * For M-profile CPUs, include logic to detect exception-return |
| * branches and handle them. This is needed for Thumb POP/LDM to PC, LDR to PC, |
| * and BX reg, and no others, and happens only for code in Handler mode. |
| * The Security Extension also requires us to check for the FNC_RETURN |
| * which signals a function return from non-secure state; this can happen |
| * in both Handler and Thread mode. |
| * To avoid having to do multiple comparisons in inline generated code, |
| * we make the check we do here loose, so it will match for EXC_RETURN |
| * in Thread mode. For system emulation do_v7m_exception_exit() checks |
| * for these spurious cases and returns without doing anything (giving |
| * the same behaviour as for a branch to a non-magic address). |
| * |
| * In linux-user mode it is unclear what the right behaviour for an |
| * attempted FNC_RETURN should be, because in real hardware this will go |
| * directly to Secure code (ie not the Linux kernel) which will then treat |
| * the error in any way it chooses. For QEMU we opt to make the FNC_RETURN |
| * attempt behave the way it would on a CPU without the security extension, |
| * which is to say "like a normal branch". That means we can simply treat |
| * all branches as normal with no magic address behaviour. |
| */ |
| static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var) |
| { |
| /* Generate the same code here as for a simple bx, but flag via |
| * s->base.is_jmp that we need to do the rest of the work later. |
| */ |
| gen_bx(s, var); |
| #ifndef CONFIG_USER_ONLY |
| if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY) || |
| (s->v7m_handler_mode && arm_dc_feature(s, ARM_FEATURE_M))) { |
| s->base.is_jmp = DISAS_BX_EXCRET; |
| } |
| #endif |
| } |
| |
| static inline void gen_bx_excret_final_code(DisasContext *s) |
| { |
| /* Generate the code to finish possible exception return and end the TB */ |
| TCGLabel *excret_label = gen_new_label(); |
| uint32_t min_magic; |
| |
| if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY)) { |
| /* Covers FNC_RETURN and EXC_RETURN magic */ |
| min_magic = FNC_RETURN_MIN_MAGIC; |
| } else { |
| /* EXC_RETURN magic only */ |
| min_magic = EXC_RETURN_MIN_MAGIC; |
| } |
| |
| /* Is the new PC value in the magic range indicating exception return? */ |
| tcg_gen_brcondi_i32(TCG_COND_GEU, cpu_R[15], min_magic, excret_label); |
| /* No: end the TB as we would for a DISAS_JMP */ |
| if (is_singlestepping(s)) { |
| gen_singlestep_exception(s); |
| } else { |
| tcg_gen_exit_tb(NULL, 0); |
| } |
| gen_set_label(excret_label); |
| /* Yes: this is an exception return. |
| * At this point in runtime env->regs[15] and env->thumb will hold |
| * the exception-return magic number, which do_v7m_exception_exit() |
| * will read. Nothing else will be able to see those values because |
| * the cpu-exec main loop guarantees that we will always go straight |
| * from raising the exception to the exception-handling code. |
| * |
| * gen_ss_advance(s) does nothing on M profile currently but |
| * calling it is conceptually the right thing as we have executed |
| * this instruction (compare SWI, HVC, SMC handling). |
| */ |
| gen_ss_advance(s); |
| gen_exception_internal(EXCP_EXCEPTION_EXIT); |
| } |
| |
| static inline void gen_bxns(DisasContext *s, int rm) |
| { |
| TCGv_i32 var = load_reg(s, rm); |
| |
| /* The bxns helper may raise an EXCEPTION_EXIT exception, so in theory |
| * we need to sync state before calling it, but: |
| * - we don't need to do gen_set_pc_im() because the bxns helper will |
| * always set the PC itself |
| * - we don't need to do gen_set_condexec() because BXNS is UNPREDICTABLE |
| * unless it's outside an IT block or the last insn in an IT block, |
| * so we know that condexec == 0 (already set at the top of the TB) |
| * is correct in the non-UNPREDICTABLE cases, and we can choose |
| * "zeroes the IT bits" as our UNPREDICTABLE behaviour otherwise. |
| */ |
| gen_helper_v7m_bxns(cpu_env, var); |
| tcg_temp_free_i32(var); |
| s->base.is_jmp = DISAS_EXIT; |
| } |
| |
| static inline void gen_blxns(DisasContext *s, int rm) |
| { |
| TCGv_i32 var = load_reg(s, rm); |
| |
| /* We don't need to sync condexec state, for the same reason as bxns. |
| * We do however need to set the PC, because the blxns helper reads it. |
| * The blxns helper may throw an exception. |
| */ |
| gen_set_pc_im(s, s->base.pc_next); |
| gen_helper_v7m_blxns(cpu_env, var); |
| tcg_temp_free_i32(var); |
| s->base.is_jmp = DISAS_EXIT; |
| } |
| |
| /* Variant of store_reg which uses branch&exchange logic when storing |
| to r15 in ARM architecture v7 and above. The source must be a temporary |
| and will be marked as dead. */ |
| static inline void store_reg_bx(DisasContext *s, int reg, TCGv_i32 var) |
| { |
| if (reg == 15 && ENABLE_ARCH_7) { |
| gen_bx(s, var); |
| } else { |
| store_reg(s, reg, var); |
| } |
| } |
| |
| /* Variant of store_reg which uses branch&exchange logic when storing |
| * to r15 in ARM architecture v5T and above. This is used for storing |
| * the results of a LDR/LDM/POP into r15, and corresponds to the cases |
| * in the ARM ARM which use the LoadWritePC() pseudocode function. */ |
| static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) |
| { |
| if (reg == 15 && ENABLE_ARCH_5) { |
| gen_bx_excret(s, var); |
| } else { |
| store_reg(s, reg, var); |
| } |
| } |
| |
| #ifdef CONFIG_USER_ONLY |
| #define IS_USER_ONLY 1 |
| #else |
| #define IS_USER_ONLY 0 |
| #endif |
| |
| /* Abstractions of "generate code to do a guest load/store for |
| * AArch32", where a vaddr is always 32 bits (and is zero |
| * extended if we're a 64 bit core) and data is also |
| * 32 bits unless specifically doing a 64 bit access. |
| * These functions work like tcg_gen_qemu_{ld,st}* except |
| * that the address argument is TCGv_i32 rather than TCGv. |
| */ |
| |
| static inline TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, MemOp op) |
| { |
| TCGv addr = tcg_temp_new(); |
| tcg_gen_extu_i32_tl(addr, a32); |
| |
| /* Not needed for user-mode BE32, where we use MO_BE instead. */ |
| if (!IS_USER_ONLY && s->sctlr_b && (op & MO_SIZE) < MO_32) { |
| tcg_gen_xori_tl(addr, addr, 4 - (1 << (op & MO_SIZE))); |
| } |
| return addr; |
| } |
| |
| static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, |
| int index, MemOp opc) |
| { |
| TCGv addr; |
| |
| if (arm_dc_feature(s, ARM_FEATURE_M) && |
| !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { |
| opc |= MO_ALIGN; |
| } |
| |
| addr = gen_aa32_addr(s, a32, opc); |
| tcg_gen_qemu_ld_i32(val, addr, index, opc); |
| tcg_temp_free(addr); |
| } |
| |
| static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, |
| int index, MemOp opc) |
| { |
| TCGv addr; |
| |
| if (arm_dc_feature(s, ARM_FEATURE_M) && |
| !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { |
| opc |= MO_ALIGN; |
| } |
| |
| addr = gen_aa32_addr(s, a32, opc); |
| tcg_gen_qemu_st_i32(val, addr, index, opc); |
| tcg_temp_free(addr); |
| } |
| |
| #define DO_GEN_LD(SUFF, OPC) \ |
| static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ |
| TCGv_i32 a32, int index) \ |
| { \ |
| gen_aa32_ld_i32(s, val, a32, index, OPC | s->be_data); \ |
| } |
| |
| #define DO_GEN_ST(SUFF, OPC) \ |
| static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ |
| TCGv_i32 a32, int index) \ |
| { \ |
| gen_aa32_st_i32(s, val, a32, index, OPC | s->be_data); \ |
| } |
| |
| static inline void gen_aa32_frob64(DisasContext *s, TCGv_i64 val) |
| { |
| /* Not needed for user-mode BE32, where we use MO_BE instead. */ |
| if (!IS_USER_ONLY && s->sctlr_b) { |
| tcg_gen_rotri_i64(val, val, 32); |
| } |
| } |
| |
| static void gen_aa32_ld_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, |
| int index, MemOp opc) |
| { |
| TCGv addr = gen_aa32_addr(s, a32, opc); |
| tcg_gen_qemu_ld_i64(val, addr, index, opc); |
| gen_aa32_frob64(s, val); |
| tcg_temp_free(addr); |
| } |
| |
| static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val, |
| TCGv_i32 a32, int index) |
| { |
| gen_aa32_ld_i64(s, val, a32, index, MO_Q | s->be_data); |
| } |
| |
| static void gen_aa32_st_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, |
| int index, MemOp opc) |
| { |
| TCGv addr = gen_aa32_addr(s, a32, opc); |
| |
| /* Not needed for user-mode BE32, where we use MO_BE instead. */ |
| if (!IS_USER_ONLY && s->sctlr_b) { |
| TCGv_i64 tmp = tcg_temp_new_i64(); |
| tcg_gen_rotri_i64(tmp, val, 32); |
| tcg_gen_qemu_st_i64(tmp, addr, index, opc); |
| tcg_temp_free_i64(tmp); |
| } else { |
| tcg_gen_qemu_st_i64(val, addr, index, opc); |
| } |
| tcg_temp_free(addr); |
| } |
| |
| static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val, |
| TCGv_i32 a32, int index) |
| { |
| gen_aa32_st_i64(s, val, a32, index, MO_Q | s->be_data); |
| } |
| |
| DO_GEN_LD(8u, MO_UB) |
| DO_GEN_LD(16u, MO_UW) |
| DO_GEN_LD(32u, MO_UL) |
| DO_GEN_ST(8, MO_UB) |
| DO_GEN_ST(16, MO_UW) |
| DO_GEN_ST(32, MO_UL) |
| |
| static inline void gen_hvc(DisasContext *s, int imm16) |
| { |
| /* The pre HVC helper handles cases when HVC gets trapped |
| * as an undefined insn by runtime configuration (ie before |
| * the insn really executes). |
| */ |
| gen_set_pc_im(s, s->pc_curr); |
| gen_helper_pre_hvc(cpu_env); |
| /* Otherwise we will treat this as a real exception which |
| * happens after execution of the insn. (The distinction matters |
| * for the PC value reported to the exception handler and also |
| * for single stepping.) |
| */ |
| s->svc_imm = imm16; |
| gen_set_pc_im(s, s->base.pc_next); |
| s->base.is_jmp = DISAS_HVC; |
| } |
| |
| static inline void gen_smc(DisasContext *s) |
| { |
| /* As with HVC, we may take an exception either before or after |
| * the insn executes. |
| */ |
| TCGv_i32 tmp; |
| |
| gen_set_pc_im(s, s->pc_curr); |
| tmp = tcg_const_i32(syn_aa32_smc()); |
| gen_helper_pre_smc(cpu_env, tmp); |
| tcg_temp_free_i32(tmp); |
| gen_set_pc_im(s, s->base.pc_next); |
| s->base.is_jmp = DISAS_SMC; |
| } |
| |
| static void gen_exception_internal_insn(DisasContext *s, uint32_t pc, int excp) |
| { |
| gen_set_condexec(s); |
| gen_set_pc_im(s, pc); |
| gen_exception_internal(excp); |
| s->base.is_jmp = DISAS_NORETURN; |
| } |
| |
| static void gen_exception_insn(DisasContext *s, uint32_t pc, int excp, |
| int syn, uint32_t target_el) |
| { |
| gen_set_condexec(s); |
| gen_set_pc_im(s, pc); |
| gen_exception(excp, syn, target_el); |
| s->base.is_jmp = DISAS_NORETURN; |
| } |
| |
| static void gen_exception_bkpt_insn(DisasContext *s, uint32_t syn) |
| { |
| TCGv_i32 tcg_syn; |
| |
| gen_set_condexec(s); |
| gen_set_pc_im(s, s->pc_curr); |
| tcg_syn = tcg_const_i32(syn); |
| gen_helper_exception_bkpt_insn(cpu_env, tcg_syn); |
| tcg_temp_free_i32(tcg_syn); |
| s->base.is_jmp = DISAS_NORETURN; |
| } |
| |
| static void unallocated_encoding(DisasContext *s) |
| { |
| /* Unallocated and reserved encodings are uncategorized */ |
| gen_exception_insn(s, s->pc_curr, EXCP_UDEF, syn_uncategorized(), |
| default_exception_el(s)); |
| } |
| |
| /* Force a TB lookup after an instruction that changes the CPU state. */ |
| static inline void gen_lookup_tb(DisasContext *s) |
| { |
| tcg_gen_movi_i32(cpu_R[15], s->base.pc_next); |
| s->base.is_jmp = DISAS_EXIT; |
| } |
| |
| static inline void gen_hlt(DisasContext *s, int imm) |
| { |
| /* HLT. This has two purposes. |
| * Architecturally, it is an external halting debug instruction. |
| * Since QEMU doesn't implement external debug, we treat this as |
| * it is required for halting debug disabled: it will UNDEF. |
| * Secondly, "HLT 0x3C" is a T32 semihosting trap instruction, |
| * and "HLT 0xF000" is an A32 semihosting syscall. These traps |
| * must trigger semihosting even for ARMv7 and earlier, where |
| * HLT was an undefined encoding. |
| * In system mode, we don't allow userspace access to |
| * semihosting, to provide some semblance of security |
| * (and for consistency with our 32-bit semihosting). |
| */ |
| if (semihosting_enabled() && |
| #ifndef CONFIG_USER_ONLY |
| s->current_el != 0 && |
| #endif |
| (imm == (s->thumb ? 0x3c : 0xf000))) { |
| gen_exception_internal_insn(s, s->pc_curr, EXCP_SEMIHOST); |
| return; |
| } |
| |
| unallocated_encoding(s); |
| } |
| |
| static TCGv_ptr get_fpstatus_ptr(int neon) |
| { |
| TCGv_ptr statusptr = tcg_temp_new_ptr(); |
| int offset; |
| if (neon) { |
| offset = offsetof(CPUARMState, vfp.standard_fp_status); |
| } else { |
| offset = offsetof(CPUARMState, vfp.fp_status); |
| } |
| tcg_gen_addi_ptr(statusptr, cpu_env, offset); |
| return statusptr; |
| } |
| |
| static inline long vfp_reg_offset(bool dp, unsigned reg) |
| { |
| if (dp) { |
| return offsetof(CPUARMState, vfp.zregs[reg >> 1].d[reg & 1]); |
| } else { |
| long ofs = offsetof(CPUARMState, vfp.zregs[reg >> 2].d[(reg >> 1) & 1]); |
| if (reg & 1) { |
| ofs += offsetof(CPU_DoubleU, l.upper); |
| } else { |
| ofs += offsetof(CPU_DoubleU, l.lower); |
| } |
| return ofs; |
| } |
| } |
| |
| /* Return the offset of a 32-bit piece of a NEON register. |
| zero is the least significant end of the register. */ |
| static inline long |
| neon_reg_offset (int reg, int n) |
| { |
| int sreg; |
| sreg = reg * 2 + n; |
| return vfp_reg_offset(0, sreg); |
| } |
| |
| /* Return the offset of a 2**SIZE piece of a NEON register, at index ELE, |
| * where 0 is the least significant end of the register. |
| */ |
| static inline long |
| neon_element_offset(int reg, int element, MemOp size) |
| { |
| int element_size = 1 << size; |
| int ofs = element * element_size; |
| #ifdef HOST_WORDS_BIGENDIAN |
| /* Calculate the offset assuming fully little-endian, |
| * then XOR to account for the order of the 8-byte units. |
| */ |
| if (element_size < 8) { |
| ofs ^= 8 - element_size; |
| } |
| #endif |
| return neon_reg_offset(reg, 0) + ofs; |
| } |
| |
| static TCGv_i32 neon_load_reg(int reg, int pass) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_ld_i32(tmp, cpu_env, neon_reg_offset(reg, pass)); |
| return tmp; |
| } |
| |
| static void neon_load_element(TCGv_i32 var, int reg, int ele, MemOp mop) |
| { |
| long offset = neon_element_offset(reg, ele, mop & MO_SIZE); |
| |
| switch (mop) { |
| case MO_UB: |
| tcg_gen_ld8u_i32(var, cpu_env, offset); |
| break; |
| case MO_UW: |
| tcg_gen_ld16u_i32(var, cpu_env, offset); |
| break; |
| case MO_UL: |
| tcg_gen_ld_i32(var, cpu_env, offset); |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| static void neon_load_element64(TCGv_i64 var, int reg, int ele, MemOp mop) |
| { |
| long offset = neon_element_offset(reg, ele, mop & MO_SIZE); |
| |
| switch (mop) { |
| case MO_UB: |
| tcg_gen_ld8u_i64(var, cpu_env, offset); |
| break; |
| case MO_UW: |
| tcg_gen_ld16u_i64(var, cpu_env, offset); |
| break; |
| case MO_UL: |
| tcg_gen_ld32u_i64(var, cpu_env, offset); |
| break; |
| case MO_Q: |
| tcg_gen_ld_i64(var, cpu_env, offset); |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| static void neon_store_reg(int reg, int pass, TCGv_i32 var) |
| { |
| tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass)); |
| tcg_temp_free_i32(var); |
| } |
| |
| static void neon_store_element(int reg, int ele, MemOp size, TCGv_i32 var) |
| { |
| long offset = neon_element_offset(reg, ele, size); |
| |
| switch (size) { |
| case MO_8: |
| tcg_gen_st8_i32(var, cpu_env, offset); |
| break; |
| case MO_16: |
| tcg_gen_st16_i32(var, cpu_env, offset); |
| break; |
| case MO_32: |
| tcg_gen_st_i32(var, cpu_env, offset); |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var) |
| { |
| long offset = neon_element_offset(reg, ele, size); |
| |
| switch (size) { |
| case MO_8: |
| tcg_gen_st8_i64(var, cpu_env, offset); |
| break; |
| case MO_16: |
| tcg_gen_st16_i64(var, cpu_env, offset); |
| break; |
| case MO_32: |
| tcg_gen_st32_i64(var, cpu_env, offset); |
| break; |
| case MO_64: |
| tcg_gen_st_i64(var, cpu_env, offset); |
| break; |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| static inline void neon_load_reg64(TCGv_i64 var, int reg) |
| { |
| tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(1, reg)); |
| } |
| |
| static inline void neon_store_reg64(TCGv_i64 var, int reg) |
| { |
| tcg_gen_st_i64(var, cpu_env, vfp_reg_offset(1, reg)); |
| } |
| |
| static inline void neon_load_reg32(TCGv_i32 var, int reg) |
| { |
| tcg_gen_ld_i32(var, cpu_env, vfp_reg_offset(false, reg)); |
| } |
| |
| static inline void neon_store_reg32(TCGv_i32 var, int reg) |
| { |
| tcg_gen_st_i32(var, cpu_env, vfp_reg_offset(false, reg)); |
| } |
| |
| static TCGv_ptr vfp_reg_ptr(bool dp, int reg) |
| { |
| TCGv_ptr ret = tcg_temp_new_ptr(); |
| tcg_gen_addi_ptr(ret, cpu_env, vfp_reg_offset(dp, reg)); |
| return ret; |
| } |
| |
| #define ARM_CP_RW_BIT (1 << 20) |
| |
| /* Include the VFP decoder */ |
| #include "translate-vfp.inc.c" |
| |
| static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) |
| { |
| tcg_gen_ld_i64(var, cpu_env, offsetof(CPUARMState, iwmmxt.regs[reg])); |
| } |
| |
| static inline void iwmmxt_store_reg(TCGv_i64 var, int reg) |
| { |
| tcg_gen_st_i64(var, cpu_env, offsetof(CPUARMState, iwmmxt.regs[reg])); |
| } |
| |
| static inline TCGv_i32 iwmmxt_load_creg(int reg) |
| { |
| TCGv_i32 var = tcg_temp_new_i32(); |
| tcg_gen_ld_i32(var, cpu_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); |
| return var; |
| } |
| |
| static inline void iwmmxt_store_creg(int reg, TCGv_i32 var) |
| { |
| tcg_gen_st_i32(var, cpu_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); |
| tcg_temp_free_i32(var); |
| } |
| |
| static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) |
| { |
| iwmmxt_store_reg(cpu_M0, rn); |
| } |
| |
| static inline void gen_op_iwmmxt_movq_M0_wRn(int rn) |
| { |
| iwmmxt_load_reg(cpu_M0, rn); |
| } |
| |
| static inline void gen_op_iwmmxt_orq_M0_wRn(int rn) |
| { |
| iwmmxt_load_reg(cpu_V1, rn); |
| tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1); |
| } |
| |
| static inline void gen_op_iwmmxt_andq_M0_wRn(int rn) |
| { |
| iwmmxt_load_reg(cpu_V1, rn); |
| tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1); |
| } |
| |
| static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn) |
| { |
| iwmmxt_load_reg(cpu_V1, rn); |
| tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1); |
| } |
| |
| #define IWMMXT_OP(name) \ |
| static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ |
| { \ |
| iwmmxt_load_reg(cpu_V1, rn); \ |
| gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \ |
| } |
| |
| #define IWMMXT_OP_ENV(name) \ |
| static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ |
| { \ |
| iwmmxt_load_reg(cpu_V1, rn); \ |
| gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \ |
| } |
| |
| #define IWMMXT_OP_ENV_SIZE(name) \ |
| IWMMXT_OP_ENV(name##b) \ |
| IWMMXT_OP_ENV(name##w) \ |
| IWMMXT_OP_ENV(name##l) |
| |
| #define IWMMXT_OP_ENV1(name) \ |
| static inline void gen_op_iwmmxt_##name##_M0(void) \ |
| { \ |
| gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \ |
| } |
| |
| IWMMXT_OP(maddsq) |
| IWMMXT_OP(madduq) |
| IWMMXT_OP(sadb) |
| IWMMXT_OP(sadw) |
| IWMMXT_OP(mulslw) |
| IWMMXT_OP(mulshw) |
| IWMMXT_OP(mululw) |
| IWMMXT_OP(muluhw) |
| IWMMXT_OP(macsw) |
| IWMMXT_OP(macuw) |
| |
| IWMMXT_OP_ENV_SIZE(unpackl) |
| IWMMXT_OP_ENV_SIZE(unpackh) |
| |
| IWMMXT_OP_ENV1(unpacklub) |
| IWMMXT_OP_ENV1(unpackluw) |
| IWMMXT_OP_ENV1(unpacklul) |
| IWMMXT_OP_ENV1(unpackhub) |
| IWMMXT_OP_ENV1(unpackhuw) |
| IWMMXT_OP_ENV1(unpackhul) |
| IWMMXT_OP_ENV1(unpacklsb) |
| IWMMXT_OP_ENV1(unpacklsw) |
| IWMMXT_OP_ENV1(unpacklsl) |
| IWMMXT_OP_ENV1(unpackhsb) |
| IWMMXT_OP_ENV1(unpackhsw) |
| IWMMXT_OP_ENV1(unpackhsl) |
| |
| IWMMXT_OP_ENV_SIZE(cmpeq) |
| IWMMXT_OP_ENV_SIZE(cmpgtu) |
| IWMMXT_OP_ENV_SIZE(cmpgts) |
| |
| IWMMXT_OP_ENV_SIZE(mins) |
| IWMMXT_OP_ENV_SIZE(minu) |
| IWMMXT_OP_ENV_SIZE(maxs) |
| IWMMXT_OP_ENV_SIZE(maxu) |
| |
| IWMMXT_OP_ENV_SIZE(subn) |
| IWMMXT_OP_ENV_SIZE(addn) |
| IWMMXT_OP_ENV_SIZE(subu) |
| IWMMXT_OP_ENV_SIZE(addu) |
| IWMMXT_OP_ENV_SIZE(subs) |
| IWMMXT_OP_ENV_SIZE(adds) |
| |
| IWMMXT_OP_ENV(avgb0) |
| IWMMXT_OP_ENV(avgb1) |
| IWMMXT_OP_ENV(avgw0) |
| IWMMXT_OP_ENV(avgw1) |
| |
| IWMMXT_OP_ENV(packuw) |
| IWMMXT_OP_ENV(packul) |
| IWMMXT_OP_ENV(packuq) |
| IWMMXT_OP_ENV(packsw) |
| IWMMXT_OP_ENV(packsl) |
| IWMMXT_OP_ENV(packsq) |
| |
| static void gen_op_iwmmxt_set_mup(void) |
| { |
| TCGv_i32 tmp; |
| tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); |
| tcg_gen_ori_i32(tmp, tmp, 2); |
| store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); |
| } |
| |
| static void gen_op_iwmmxt_set_cup(void) |
| { |
| TCGv_i32 tmp; |
| tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); |
| tcg_gen_ori_i32(tmp, tmp, 1); |
| store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); |
| } |
| |
| static void gen_op_iwmmxt_setpsr_nz(void) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0); |
| store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]); |
| } |
| |
| static inline void gen_op_iwmmxt_addl_M0_wRn(int rn) |
| { |
| iwmmxt_load_reg(cpu_V1, rn); |
| tcg_gen_ext32u_i64(cpu_V1, cpu_V1); |
| tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); |
| } |
| |
| static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn, |
| TCGv_i32 dest) |
| { |
| int rd; |
| uint32_t offset; |
| TCGv_i32 tmp; |
| |
| rd = (insn >> 16) & 0xf; |
| tmp = load_reg(s, rd); |
| |
| offset = (insn & 0xff) << ((insn >> 7) & 2); |
| if (insn & (1 << 24)) { |
| /* Pre indexed */ |
| if (insn & (1 << 23)) |
| tcg_gen_addi_i32(tmp, tmp, offset); |
| else |
| tcg_gen_addi_i32(tmp, tmp, -offset); |
| tcg_gen_mov_i32(dest, tmp); |
| if (insn & (1 << 21)) |
| store_reg(s, rd, tmp); |
| else |
| tcg_temp_free_i32(tmp); |
| } else if (insn & (1 << 21)) { |
| /* Post indexed */ |
| tcg_gen_mov_i32(dest, tmp); |
| if (insn & (1 << 23)) |
| tcg_gen_addi_i32(tmp, tmp, offset); |
| else |
| tcg_gen_addi_i32(tmp, tmp, -offset); |
| store_reg(s, rd, tmp); |
| } else if (!(insn & (1 << 23))) |
| return 1; |
| return 0; |
| } |
| |
| static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) |
| { |
| int rd = (insn >> 0) & 0xf; |
| TCGv_i32 tmp; |
| |
| if (insn & (1 << 8)) { |
| if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3) { |
| return 1; |
| } else { |
| tmp = iwmmxt_load_creg(rd); |
| } |
| } else { |
| tmp = tcg_temp_new_i32(); |
| iwmmxt_load_reg(cpu_V0, rd); |
| tcg_gen_extrl_i64_i32(tmp, cpu_V0); |
| } |
| tcg_gen_andi_i32(tmp, tmp, mask); |
| tcg_gen_mov_i32(dest, tmp); |
| tcg_temp_free_i32(tmp); |
| return 0; |
| } |
| |
| /* Disassemble an iwMMXt instruction. Returns nonzero if an error occurred |
| (ie. an undefined instruction). */ |
| static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) |
| { |
| int rd, wrd; |
| int rdhi, rdlo, rd0, rd1, i; |
| TCGv_i32 addr; |
| TCGv_i32 tmp, tmp2, tmp3; |
| |
| if ((insn & 0x0e000e00) == 0x0c000000) { |
| if ((insn & 0x0fe00ff0) == 0x0c400000) { |
| wrd = insn & 0xf; |
| rdlo = (insn >> 12) & 0xf; |
| rdhi = (insn >> 16) & 0xf; |
| if (insn & ARM_CP_RW_BIT) { /* TMRRC */ |
| iwmmxt_load_reg(cpu_V0, wrd); |
| tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); |
| tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); |
| } else { /* TMCRR */ |
| tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); |
| iwmmxt_store_reg(cpu_V0, wrd); |
| gen_op_iwmmxt_set_mup(); |
| } |
| return 0; |
| } |
| |
| wrd = (insn >> 12) & 0xf; |
| addr = tcg_temp_new_i32(); |
| if (gen_iwmmxt_address(s, insn, addr)) { |
| tcg_temp_free_i32(addr); |
| return 1; |
| } |
| if (insn & ARM_CP_RW_BIT) { |
| if ((insn >> 28) == 0xf) { /* WLDRW wCx */ |
| tmp = tcg_temp_new_i32(); |
| gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); |
| iwmmxt_store_creg(wrd, tmp); |
| } else { |
| i = 1; |
| if (insn & (1 << 8)) { |
| if (insn & (1 << 22)) { /* WLDRD */ |
| gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); |
| i = 0; |
| } else { /* WLDRW wRd */ |
| tmp = tcg_temp_new_i32(); |
| gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); |
| } |
| } else { |
| tmp = tcg_temp_new_i32(); |
| if (insn & (1 << 22)) { /* WLDRH */ |
| gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); |
| } else { /* WLDRB */ |
| gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); |
| } |
| } |
| if (i) { |
| tcg_gen_extu_i32_i64(cpu_M0, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| } |
| } else { |
| if ((insn >> 28) == 0xf) { /* WSTRW wCx */ |
| tmp = iwmmxt_load_creg(wrd); |
| gen_aa32_st32(s, tmp, addr, get_mem_index(s)); |
| } else { |
| gen_op_iwmmxt_movq_M0_wRn(wrd); |
| tmp = tcg_temp_new_i32(); |
| if (insn & (1 << 8)) { |
| if (insn & (1 << 22)) { /* WSTRD */ |
| gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); |
| } else { /* WSTRW wRd */ |
| tcg_gen_extrl_i64_i32(tmp, cpu_M0); |
| gen_aa32_st32(s, tmp, addr, get_mem_index(s)); |
| } |
| } else { |
| if (insn & (1 << 22)) { /* WSTRH */ |
| tcg_gen_extrl_i64_i32(tmp, cpu_M0); |
| gen_aa32_st16(s, tmp, addr, get_mem_index(s)); |
| } else { /* WSTRB */ |
| tcg_gen_extrl_i64_i32(tmp, cpu_M0); |
| gen_aa32_st8(s, tmp, addr, get_mem_index(s)); |
| } |
| } |
| } |
| tcg_temp_free_i32(tmp); |
| } |
| tcg_temp_free_i32(addr); |
| return 0; |
| } |
| |
| if ((insn & 0x0f000000) != 0x0e000000) |
| return 1; |
| |
| switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { |
| case 0x000: /* WOR */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 0) & 0xf; |
| rd1 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| gen_op_iwmmxt_orq_M0_wRn(rd1); |
| gen_op_iwmmxt_setpsr_nz(); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x011: /* TMCR */ |
| if (insn & 0xf) |
| return 1; |
| rd = (insn >> 12) & 0xf; |
| wrd = (insn >> 16) & 0xf; |
| switch (wrd) { |
| case ARM_IWMMXT_wCID: |
| case ARM_IWMMXT_wCASF: |
| break; |
| case ARM_IWMMXT_wCon: |
| gen_op_iwmmxt_set_cup(); |
| /* Fall through. */ |
| case ARM_IWMMXT_wCSSF: |
| tmp = iwmmxt_load_creg(wrd); |
| tmp2 = load_reg(s, rd); |
| tcg_gen_andc_i32(tmp, tmp, tmp2); |
| tcg_temp_free_i32(tmp2); |
| iwmmxt_store_creg(wrd, tmp); |
| break; |
| case ARM_IWMMXT_wCGR0: |
| case ARM_IWMMXT_wCGR1: |
| case ARM_IWMMXT_wCGR2: |
| case ARM_IWMMXT_wCGR3: |
| gen_op_iwmmxt_set_cup(); |
| tmp = load_reg(s, rd); |
| iwmmxt_store_creg(wrd, tmp); |
| break; |
| default: |
| return 1; |
| } |
| break; |
| case 0x100: /* WXOR */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 0) & 0xf; |
| rd1 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| gen_op_iwmmxt_xorq_M0_wRn(rd1); |
| gen_op_iwmmxt_setpsr_nz(); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x111: /* TMRC */ |
| if (insn & 0xf) |
| return 1; |
| rd = (insn >> 12) & 0xf; |
| wrd = (insn >> 16) & 0xf; |
| tmp = iwmmxt_load_creg(wrd); |
| store_reg(s, rd, tmp); |
| break; |
| case 0x300: /* WANDN */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 0) & 0xf; |
| rd1 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tcg_gen_neg_i64(cpu_M0, cpu_M0); |
| gen_op_iwmmxt_andq_M0_wRn(rd1); |
| gen_op_iwmmxt_setpsr_nz(); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x200: /* WAND */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 0) & 0xf; |
| rd1 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| gen_op_iwmmxt_andq_M0_wRn(rd1); |
| gen_op_iwmmxt_setpsr_nz(); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x810: case 0xa10: /* WMADD */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 0) & 0xf; |
| rd1 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_maddsq_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_madduq_M0_wRn(rd1); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| gen_op_iwmmxt_unpacklb_M0_wRn(rd1); |
| break; |
| case 1: |
| gen_op_iwmmxt_unpacklw_M0_wRn(rd1); |
| break; |
| case 2: |
| gen_op_iwmmxt_unpackll_M0_wRn(rd1); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| gen_op_iwmmxt_unpackhb_M0_wRn(rd1); |
| break; |
| case 1: |
| gen_op_iwmmxt_unpackhw_M0_wRn(rd1); |
| break; |
| case 2: |
| gen_op_iwmmxt_unpackhl_M0_wRn(rd1); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| if (insn & (1 << 22)) |
| gen_op_iwmmxt_sadw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_sadb_M0_wRn(rd1); |
| if (!(insn & (1 << 20))) |
| gen_op_iwmmxt_addl_M0_wRn(wrd); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| if (insn & (1 << 21)) { |
| if (insn & (1 << 20)) |
| gen_op_iwmmxt_mulshw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_mulslw_M0_wRn(rd1); |
| } else { |
| if (insn & (1 << 20)) |
| gen_op_iwmmxt_muluhw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_mululw_M0_wRn(rd1); |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_macsw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_macuw_M0_wRn(rd1); |
| if (!(insn & (1 << 20))) { |
| iwmmxt_load_reg(cpu_V1, wrd); |
| tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| gen_op_iwmmxt_cmpeqb_M0_wRn(rd1); |
| break; |
| case 1: |
| gen_op_iwmmxt_cmpeqw_M0_wRn(rd1); |
| break; |
| case 2: |
| gen_op_iwmmxt_cmpeql_M0_wRn(rd1); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| if (insn & (1 << 22)) { |
| if (insn & (1 << 20)) |
| gen_op_iwmmxt_avgw1_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_avgw0_M0_wRn(rd1); |
| } else { |
| if (insn & (1 << 20)) |
| gen_op_iwmmxt_avgb1_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_avgb0_M0_wRn(rd1); |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = iwmmxt_load_creg(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3)); |
| tcg_gen_andi_i32(tmp, tmp, 7); |
| iwmmxt_load_reg(cpu_V1, rd1); |
| gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ |
| if (((insn >> 6) & 3) == 3) |
| return 1; |
| rd = (insn >> 12) & 0xf; |
| wrd = (insn >> 16) & 0xf; |
| tmp = load_reg(s, rd); |
| gen_op_iwmmxt_movq_M0_wRn(wrd); |
| switch ((insn >> 6) & 3) { |
| case 0: |
| tmp2 = tcg_const_i32(0xff); |
| tmp3 = tcg_const_i32((insn & 7) << 3); |
| break; |
| case 1: |
| tmp2 = tcg_const_i32(0xffff); |
| tmp3 = tcg_const_i32((insn & 3) << 4); |
| break; |
| case 2: |
| tmp2 = tcg_const_i32(0xffffffff); |
| tmp3 = tcg_const_i32((insn & 1) << 5); |
| break; |
| default: |
| tmp2 = NULL; |
| tmp3 = NULL; |
| } |
| gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3); |
| tcg_temp_free_i32(tmp3); |
| tcg_temp_free_i32(tmp2); |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ |
| rd = (insn >> 12) & 0xf; |
| wrd = (insn >> 16) & 0xf; |
| if (rd == 15 || ((insn >> 22) & 3) == 3) |
| return 1; |
| gen_op_iwmmxt_movq_M0_wRn(wrd); |
| tmp = tcg_temp_new_i32(); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3); |
| tcg_gen_extrl_i64_i32(tmp, cpu_M0); |
| if (insn & 8) { |
| tcg_gen_ext8s_i32(tmp, tmp); |
| } else { |
| tcg_gen_andi_i32(tmp, tmp, 0xff); |
| } |
| break; |
| case 1: |
| tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 3) << 4); |
| tcg_gen_extrl_i64_i32(tmp, cpu_M0); |
| if (insn & 8) { |
| tcg_gen_ext16s_i32(tmp, tmp); |
| } else { |
| tcg_gen_andi_i32(tmp, tmp, 0xffff); |
| } |
| break; |
| case 2: |
| tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 1) << 5); |
| tcg_gen_extrl_i64_i32(tmp, cpu_M0); |
| break; |
| } |
| store_reg(s, rd, tmp); |
| break; |
| case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ |
| if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) |
| return 1; |
| tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| tcg_gen_shri_i32(tmp, tmp, ((insn & 7) << 2) + 0); |
| break; |
| case 1: |
| tcg_gen_shri_i32(tmp, tmp, ((insn & 3) << 3) + 4); |
| break; |
| case 2: |
| tcg_gen_shri_i32(tmp, tmp, ((insn & 1) << 4) + 12); |
| break; |
| } |
| tcg_gen_shli_i32(tmp, tmp, 28); |
| gen_set_nzcv(tmp); |
| tcg_temp_free_i32(tmp); |
| break; |
| case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ |
| if (((insn >> 6) & 3) == 3) |
| return 1; |
| rd = (insn >> 12) & 0xf; |
| wrd = (insn >> 16) & 0xf; |
| tmp = load_reg(s, rd); |
| switch ((insn >> 6) & 3) { |
| case 0: |
| gen_helper_iwmmxt_bcstb(cpu_M0, tmp); |
| break; |
| case 1: |
| gen_helper_iwmmxt_bcstw(cpu_M0, tmp); |
| break; |
| case 2: |
| gen_helper_iwmmxt_bcstl(cpu_M0, tmp); |
| break; |
| } |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ |
| if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) |
| return 1; |
| tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); |
| tmp2 = tcg_temp_new_i32(); |
| tcg_gen_mov_i32(tmp2, tmp); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| for (i = 0; i < 7; i ++) { |
| tcg_gen_shli_i32(tmp2, tmp2, 4); |
| tcg_gen_and_i32(tmp, tmp, tmp2); |
| } |
| break; |
| case 1: |
| for (i = 0; i < 3; i ++) { |
| tcg_gen_shli_i32(tmp2, tmp2, 8); |
| tcg_gen_and_i32(tmp, tmp, tmp2); |
| } |
| break; |
| case 2: |
| tcg_gen_shli_i32(tmp2, tmp2, 16); |
| tcg_gen_and_i32(tmp, tmp, tmp2); |
| break; |
| } |
| gen_set_nzcv(tmp); |
| tcg_temp_free_i32(tmp2); |
| tcg_temp_free_i32(tmp); |
| break; |
| case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0); |
| break; |
| case 1: |
| gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0); |
| break; |
| case 2: |
| gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ |
| if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) |
| return 1; |
| tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); |
| tmp2 = tcg_temp_new_i32(); |
| tcg_gen_mov_i32(tmp2, tmp); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| for (i = 0; i < 7; i ++) { |
| tcg_gen_shli_i32(tmp2, tmp2, 4); |
| tcg_gen_or_i32(tmp, tmp, tmp2); |
| } |
| break; |
| case 1: |
| for (i = 0; i < 3; i ++) { |
| tcg_gen_shli_i32(tmp2, tmp2, 8); |
| tcg_gen_or_i32(tmp, tmp, tmp2); |
| } |
| break; |
| case 2: |
| tcg_gen_shli_i32(tmp2, tmp2, 16); |
| tcg_gen_or_i32(tmp, tmp, tmp2); |
| break; |
| } |
| gen_set_nzcv(tmp); |
| tcg_temp_free_i32(tmp2); |
| tcg_temp_free_i32(tmp); |
| break; |
| case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ |
| rd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) |
| return 1; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = tcg_temp_new_i32(); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| gen_helper_iwmmxt_msbb(tmp, cpu_M0); |
| break; |
| case 1: |
| gen_helper_iwmmxt_msbw(tmp, cpu_M0); |
| break; |
| case 2: |
| gen_helper_iwmmxt_msbl(tmp, cpu_M0); |
| break; |
| } |
| store_reg(s, rd, tmp); |
| break; |
| case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ |
| case 0x906: case 0xb06: case 0xd06: case 0xf06: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_cmpgtub_M0_wRn(rd1); |
| break; |
| case 1: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1); |
| break; |
| case 2: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_cmpgtul_M0_wRn(rd1); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ |
| case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_unpacklsb_M0(); |
| else |
| gen_op_iwmmxt_unpacklub_M0(); |
| break; |
| case 1: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_unpacklsw_M0(); |
| else |
| gen_op_iwmmxt_unpackluw_M0(); |
| break; |
| case 2: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_unpacklsl_M0(); |
| else |
| gen_op_iwmmxt_unpacklul_M0(); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ |
| case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_unpackhsb_M0(); |
| else |
| gen_op_iwmmxt_unpackhub_M0(); |
| break; |
| case 1: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_unpackhsw_M0(); |
| else |
| gen_op_iwmmxt_unpackhuw_M0(); |
| break; |
| case 2: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_unpackhsl_M0(); |
| else |
| gen_op_iwmmxt_unpackhul_M0(); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ |
| case 0x214: case 0x614: case 0xa14: case 0xe14: |
| if (((insn >> 22) & 3) == 0) |
| return 1; |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = tcg_temp_new_i32(); |
| if (gen_iwmmxt_shift(insn, 0xff, tmp)) { |
| tcg_temp_free_i32(tmp); |
| return 1; |
| } |
| switch ((insn >> 22) & 3) { |
| case 1: |
| gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 2: |
| gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 3: |
| gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| } |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ |
| case 0x014: case 0x414: case 0x814: case 0xc14: |
| if (((insn >> 22) & 3) == 0) |
| return 1; |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = tcg_temp_new_i32(); |
| if (gen_iwmmxt_shift(insn, 0xff, tmp)) { |
| tcg_temp_free_i32(tmp); |
| return 1; |
| } |
| switch ((insn >> 22) & 3) { |
| case 1: |
| gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 2: |
| gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 3: |
| gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| } |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ |
| case 0x114: case 0x514: case 0x914: case 0xd14: |
| if (((insn >> 22) & 3) == 0) |
| return 1; |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = tcg_temp_new_i32(); |
| if (gen_iwmmxt_shift(insn, 0xff, tmp)) { |
| tcg_temp_free_i32(tmp); |
| return 1; |
| } |
| switch ((insn >> 22) & 3) { |
| case 1: |
| gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 2: |
| gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 3: |
| gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| } |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ |
| case 0x314: case 0x714: case 0xb14: case 0xf14: |
| if (((insn >> 22) & 3) == 0) |
| return 1; |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = tcg_temp_new_i32(); |
| switch ((insn >> 22) & 3) { |
| case 1: |
| if (gen_iwmmxt_shift(insn, 0xf, tmp)) { |
| tcg_temp_free_i32(tmp); |
| return 1; |
| } |
| gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 2: |
| if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { |
| tcg_temp_free_i32(tmp); |
| return 1; |
| } |
| gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| case 3: |
| if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { |
| tcg_temp_free_i32(tmp); |
| return 1; |
| } |
| gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, tmp); |
| break; |
| } |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ |
| case 0x916: case 0xb16: case 0xd16: case 0xf16: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_minsb_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_minub_M0_wRn(rd1); |
| break; |
| case 1: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_minsw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_minuw_M0_wRn(rd1); |
| break; |
| case 2: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_minsl_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_minul_M0_wRn(rd1); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ |
| case 0x816: case 0xa16: case 0xc16: case 0xe16: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 0: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_maxsb_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_maxub_M0_wRn(rd1); |
| break; |
| case 1: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_maxsw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_maxuw_M0_wRn(rd1); |
| break; |
| case 2: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_maxsl_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_maxul_M0_wRn(rd1); |
| break; |
| case 3: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ |
| case 0x402: case 0x502: case 0x602: case 0x702: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = tcg_const_i32((insn >> 20) & 3); |
| iwmmxt_load_reg(cpu_V1, rd1); |
| gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ |
| case 0x41a: case 0x51a: case 0x61a: case 0x71a: |
| case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: |
| case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 20) & 0xf) { |
| case 0x0: |
| gen_op_iwmmxt_subnb_M0_wRn(rd1); |
| break; |
| case 0x1: |
| gen_op_iwmmxt_subub_M0_wRn(rd1); |
| break; |
| case 0x3: |
| gen_op_iwmmxt_subsb_M0_wRn(rd1); |
| break; |
| case 0x4: |
| gen_op_iwmmxt_subnw_M0_wRn(rd1); |
| break; |
| case 0x5: |
| gen_op_iwmmxt_subuw_M0_wRn(rd1); |
| break; |
| case 0x7: |
| gen_op_iwmmxt_subsw_M0_wRn(rd1); |
| break; |
| case 0x8: |
| gen_op_iwmmxt_subnl_M0_wRn(rd1); |
| break; |
| case 0x9: |
| gen_op_iwmmxt_subul_M0_wRn(rd1); |
| break; |
| case 0xb: |
| gen_op_iwmmxt_subsl_M0_wRn(rd1); |
| break; |
| default: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ |
| case 0x41e: case 0x51e: case 0x61e: case 0x71e: |
| case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: |
| case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| tmp = tcg_const_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); |
| gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, tmp); |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ |
| case 0x418: case 0x518: case 0x618: case 0x718: |
| case 0x818: case 0x918: case 0xa18: case 0xb18: |
| case 0xc18: case 0xd18: case 0xe18: case 0xf18: |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 20) & 0xf) { |
| case 0x0: |
| gen_op_iwmmxt_addnb_M0_wRn(rd1); |
| break; |
| case 0x1: |
| gen_op_iwmmxt_addub_M0_wRn(rd1); |
| break; |
| case 0x3: |
| gen_op_iwmmxt_addsb_M0_wRn(rd1); |
| break; |
| case 0x4: |
| gen_op_iwmmxt_addnw_M0_wRn(rd1); |
| break; |
| case 0x5: |
| gen_op_iwmmxt_adduw_M0_wRn(rd1); |
| break; |
| case 0x7: |
| gen_op_iwmmxt_addsw_M0_wRn(rd1); |
| break; |
| case 0x8: |
| gen_op_iwmmxt_addnl_M0_wRn(rd1); |
| break; |
| case 0x9: |
| gen_op_iwmmxt_addul_M0_wRn(rd1); |
| break; |
| case 0xb: |
| gen_op_iwmmxt_addsl_M0_wRn(rd1); |
| break; |
| default: |
| return 1; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ |
| case 0x408: case 0x508: case 0x608: case 0x708: |
| case 0x808: case 0x908: case 0xa08: case 0xb08: |
| case 0xc08: case 0xd08: case 0xe08: case 0xf08: |
| if (!(insn & (1 << 20)) || ((insn >> 22) & 3) == 0) |
| return 1; |
| wrd = (insn >> 12) & 0xf; |
| rd0 = (insn >> 16) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| gen_op_iwmmxt_movq_M0_wRn(rd0); |
| switch ((insn >> 22) & 3) { |
| case 1: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_packsw_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_packuw_M0_wRn(rd1); |
| break; |
| case 2: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_packsl_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_packul_M0_wRn(rd1); |
| break; |
| case 3: |
| if (insn & (1 << 21)) |
| gen_op_iwmmxt_packsq_M0_wRn(rd1); |
| else |
| gen_op_iwmmxt_packuq_M0_wRn(rd1); |
| break; |
| } |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| gen_op_iwmmxt_set_cup(); |
| break; |
| case 0x201: case 0x203: case 0x205: case 0x207: |
| case 0x209: case 0x20b: case 0x20d: case 0x20f: |
| case 0x211: case 0x213: case 0x215: case 0x217: |
| case 0x219: case 0x21b: case 0x21d: case 0x21f: |
| wrd = (insn >> 5) & 0xf; |
| rd0 = (insn >> 12) & 0xf; |
| rd1 = (insn >> 0) & 0xf; |
| if (rd0 == 0xf || rd1 == 0xf) |
| return 1; |
| gen_op_iwmmxt_movq_M0_wRn(wrd); |
| tmp = load_reg(s, rd0); |
| tmp2 = load_reg(s, rd1); |
| switch ((insn >> 16) & 0xf) { |
| case 0x0: /* TMIA */ |
| gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); |
| break; |
| case 0x8: /* TMIAPH */ |
| gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); |
| break; |
| case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ |
| if (insn & (1 << 16)) |
| tcg_gen_shri_i32(tmp, tmp, 16); |
| if (insn & (1 << 17)) |
| tcg_gen_shri_i32(tmp2, tmp2, 16); |
| gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); |
| break; |
| default: |
| tcg_temp_free_i32(tmp2); |
| tcg_temp_free_i32(tmp); |
| return 1; |
| } |
| tcg_temp_free_i32(tmp2); |
| tcg_temp_free_i32(tmp); |
| gen_op_iwmmxt_movq_wRn_M0(wrd); |
| gen_op_iwmmxt_set_mup(); |
| break; |
| default: |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Disassemble an XScale DSP instruction. Returns nonzero if an error occurred |
| (ie. an undefined instruction). */ |
| static int disas_dsp_insn(DisasContext *s, uint32_t insn) |
| { |
| int acc, rd0, rd1, rdhi, rdlo; |
| TCGv_i32 tmp, tmp2; |
| |
| if ((insn & 0x0ff00f10) == 0x0e200010) { |
| /* Multiply with Internal Accumulate Format */ |
| rd0 = (insn >> 12) & 0xf; |
| rd1 = insn & 0xf; |
| acc = (insn >> 5) & 7; |
| |
| if (acc != 0) |
| return 1; |
| |
| tmp = load_reg(s, rd0); |
| tmp2 = load_reg(s, rd1); |
| switch ((insn >> 16) & 0xf) { |
| case 0x0: /* MIA */ |
| gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); |
| break; |
| case 0x8: /* MIAPH */ |
| gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); |
| break; |
| case 0xc: /* MIABB */ |
| case 0xd: /* MIABT */ |
| case 0xe: /* MIATB */ |
| case 0xf: /* MIATT */ |
| if (insn & (1 << 16)) |
| tcg_gen_shri_i32(tmp, tmp, 16); |
| if (insn & (1 << 17)) |
| tcg_gen_shri_i32(tmp2, tmp2, 16); |
| gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); |
| break; |
| default: |
| return 1; |
| } |
| tcg_temp_free_i32(tmp2); |
| tcg_temp_free_i32(tmp); |
| |
| gen_op_iwmmxt_movq_wRn_M0(acc); |
| return 0; |
| } |
| |
| if ((insn & 0x0fe00ff8) == 0x0c400000) { |
| /* Internal Accumulator Access Format */ |
| rdhi = (insn >> 16) & 0xf; |
| rdlo = (insn >> 12) & 0xf; |
| acc = insn & 7; |
| |
| if (acc != 0) |
| return 1; |
| |
| if (insn & ARM_CP_RW_BIT) { /* MRA */ |
| iwmmxt_load_reg(cpu_V0, acc); |
| tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); |
| tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); |
| tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); |
| } else { /* MAR */ |
| tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); |
| iwmmxt_store_reg(cpu_V0, acc); |
| } |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| #define VFP_REG_SHR(x, n) (((n) > 0) ? (x) >> (n) : (x) << -(n)) |
| #define VFP_SREG(insn, bigbit, smallbit) \ |
| ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1)) |
| #define VFP_DREG(reg, insn, bigbit, smallbit) do { \ |
| if (dc_isar_feature(aa32_simd_r32, s)) { \ |
| reg = (((insn) >> (bigbit)) & 0x0f) \ |
| | (((insn) >> ((smallbit) - 4)) & 0x10); \ |
| } else { \ |
| if (insn & (1 << (smallbit))) \ |
| return 1; \ |
| reg = ((insn) >> (bigbit)) & 0x0f; \ |
| }} while (0) |
| |
| #define VFP_SREG_D(insn) VFP_SREG(insn, 12, 22) |
| #define VFP_DREG_D(reg, insn) VFP_DREG(reg, insn, 12, 22) |
| #define VFP_SREG_N(insn) VFP_SREG(insn, 16, 7) |
| #define VFP_DREG_N(reg, insn) VFP_DREG(reg, insn, 16, 7) |
| #define VFP_SREG_M(insn) VFP_SREG(insn, 0, 5) |
| #define VFP_DREG_M(reg, insn) VFP_DREG(reg, insn, 0, 5) |
| |
| static void gen_neon_dup_low16(TCGv_i32 var) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_ext16u_i32(var, var); |
| tcg_gen_shli_i32(tmp, var, 16); |
| tcg_gen_or_i32(var, var, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| static void gen_neon_dup_high16(TCGv_i32 var) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_andi_i32(var, var, 0xffff0000); |
| tcg_gen_shri_i32(tmp, var, 16); |
| tcg_gen_or_i32(var, var, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| static inline bool use_goto_tb(DisasContext *s, target_ulong dest) |
| { |
| #ifndef CONFIG_USER_ONLY |
| return (s->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) || |
| ((s->base.pc_next - 1) & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); |
| #else |
| return true; |
| #endif |
| } |
| |
| static void gen_goto_ptr(void) |
| { |
| tcg_gen_lookup_and_goto_ptr(); |
| } |
| |
| /* This will end the TB but doesn't guarantee we'll return to |
| * cpu_loop_exec. Any live exit_requests will be processed as we |
| * enter the next TB. |
| */ |
| static void gen_goto_tb(DisasContext *s, int n, target_ulong dest) |
| { |
| if (use_goto_tb(s, dest)) { |
| tcg_gen_goto_tb(n); |
| gen_set_pc_im(s, dest); |
| tcg_gen_exit_tb(s->base.tb, n); |
| } else { |
| gen_set_pc_im(s, dest); |
| gen_goto_ptr(); |
| } |
| s->base.is_jmp = DISAS_NORETURN; |
| } |
| |
| static inline void gen_jmp (DisasContext *s, uint32_t dest) |
| { |
| if (unlikely(is_singlestepping(s))) { |
| /* An indirect jump so that we still trigger the debug exception. */ |
| gen_set_pc_im(s, dest); |
| s->base.is_jmp = DISAS_JUMP; |
| } else { |
| gen_goto_tb(s, 0, dest); |
| } |
| } |
| |
| static inline void gen_mulxy(TCGv_i32 t0, TCGv_i32 t1, int x, int y) |
| { |
| if (x) |
| tcg_gen_sari_i32(t0, t0, 16); |
| else |
| gen_sxth(t0); |
| if (y) |
| tcg_gen_sari_i32(t1, t1, 16); |
| else |
| gen_sxth(t1); |
| tcg_gen_mul_i32(t0, t0, t1); |
| } |
| |
| /* Return the mask of PSR bits set by a MSR instruction. */ |
| static uint32_t msr_mask(DisasContext *s, int flags, int spsr) |
| { |
| uint32_t mask = 0; |
| |
| if (flags & (1 << 0)) { |
| mask |= 0xff; |
| } |
| if (flags & (1 << 1)) { |
| mask |= 0xff00; |
| } |
| if (flags & (1 << 2)) { |
| mask |= 0xff0000; |
| } |
| if (flags & (1 << 3)) { |
| mask |= 0xff000000; |
| } |
| |
| /* Mask out undefined and reserved bits. */ |
| mask &= aarch32_cpsr_valid_mask(s->features, s->isar); |
| |
| /* Mask out execution state. */ |
| if (!spsr) { |
| mask &= ~CPSR_EXEC; |
| } |
| |
| /* Mask out privileged bits. */ |
| if (IS_USER(s)) { |
| mask &= CPSR_USER; |
| } |
| return mask; |
| } |
| |
| /* Returns nonzero if access to the PSR is not permitted. Marks t0 as dead. */ |
| static int gen_set_psr(DisasContext *s, uint32_t mask, int spsr, TCGv_i32 t0) |
| { |
| TCGv_i32 tmp; |
| if (spsr) { |
| /* ??? This is also undefined in system mode. */ |
| if (IS_USER(s)) |
| return 1; |
| |
| tmp = load_cpu_field(spsr); |
| tcg_gen_andi_i32(tmp, tmp, ~mask); |
| tcg_gen_andi_i32(t0, t0, mask); |
| tcg_gen_or_i32(tmp, tmp, t0); |
| store_cpu_field(tmp, spsr); |
| } else { |
| gen_set_cpsr(t0, mask); |
| } |
| tcg_temp_free_i32(t0); |
| gen_lookup_tb(s); |
| return 0; |
| } |
| |
| /* Returns nonzero if access to the PSR is not permitted. */ |
| static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val) |
| { |
| TCGv_i32 tmp; |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_movi_i32(tmp, val); |
| return gen_set_psr(s, mask, spsr, tmp); |
| } |
| |
| static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, |
| int *tgtmode, int *regno) |
| { |
| /* Decode the r and sysm fields of MSR/MRS banked accesses into |
| * the target mode and register number, and identify the various |
| * unpredictable cases. |
| * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if: |
| * + executed in user mode |
| * + using R15 as the src/dest register |
| * + accessing an unimplemented register |
| * + accessing a register that's inaccessible at current PL/security state* |
| * + accessing a register that you could access with a different insn |
| * We choose to UNDEF in all these cases. |
| * Since we don't know which of the various AArch32 modes we are in |
| * we have to defer some checks to runtime. |
| * Accesses to Monitor mode registers from Secure EL1 (which implies |
| * that EL3 is AArch64) must trap to EL3. |
| * |
| * If the access checks fail this function will emit code to take |
| * an exception and return false. Otherwise it will return true, |
| * and set *tgtmode and *regno appropriately. |
| */ |
| int exc_target = default_exception_el(s); |
| |
| /* These instructions are present only in ARMv8, or in ARMv7 with the |
| * Virtualization Extensions. |
| */ |
| if (!arm_dc_feature(s, ARM_FEATURE_V8) && |
| !arm_dc_feature(s, ARM_FEATURE_EL2)) { |
| goto undef; |
| } |
| |
| if (IS_USER(s) || rn == 15) { |
| goto undef; |
| } |
| |
| /* The table in the v8 ARM ARM section F5.2.3 describes the encoding |
| * of registers into (r, sysm). |
| */ |
| if (r) { |
| /* SPSRs for other modes */ |
| switch (sysm) { |
| case 0xe: /* SPSR_fiq */ |
| *tgtmode = ARM_CPU_MODE_FIQ; |
| break; |
| case 0x10: /* SPSR_irq */ |
| *tgtmode = ARM_CPU_MODE_IRQ; |
| break; |
| case 0x12: /* SPSR_svc */ |
| *tgtmode = ARM_CPU_MODE_SVC; |
| break; |
| case 0x14: /* SPSR_abt */ |
| *tgtmode = ARM_CPU_MODE_ABT; |
| break; |
| case 0x16: /* SPSR_und */ |
| *tgtmode = ARM_CPU_MODE_UND; |
| break; |
| case 0x1c: /* SPSR_mon */ |
| *tgtmode = ARM_CPU_MODE_MON; |
| break; |
| case 0x1e: /* SPSR_hyp */ |
| *tgtmode = ARM_CPU_MODE_HYP; |
| break; |
| default: /* unallocated */ |
| goto undef; |
| } |
| /* We arbitrarily assign SPSR a register number of 16. */ |
| *regno = 16; |
| } else { |
| /* general purpose registers for other modes */ |
| switch (sysm) { |
| case 0x0 ... 0x6: /* 0b00xxx : r8_usr ... r14_usr */ |
| *tgtmode = ARM_CPU_MODE_USR; |
| *regno = sysm + 8; |
| break; |
| case 0x8 ... 0xe: /* 0b01xxx : r8_fiq ... r14_fiq */ |
| *tgtmode = ARM_CPU_MODE_FIQ; |
| *regno = sysm; |
| break; |
| case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */ |
| *tgtmode = ARM_CPU_MODE_IRQ; |
|