| /* |
| * 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 <stdarg.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <inttypes.h> |
| |
| #include "cpu.h" |
| #include "internals.h" |
| #include "disas/disas.h" |
| #include "tcg-op.h" |
| #include "qemu/log.h" |
| #include "qemu/bitops.h" |
| #include "arm_ldst.h" |
| |
| #include "exec/helper-proto.h" |
| #include "exec/helper-gen.h" |
| |
| #include "trace-tcg.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 0 |
| #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" |
| static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; |
| |
| #if defined(CONFIG_USER_ONLY) |
| #define IS_USER(s) 1 |
| #else |
| #define IS_USER(s) (s->user) |
| #endif |
| |
| TCGv_ptr cpu_env; |
| /* We reuse the same 64-bit temporaries for efficiency. */ |
| static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; |
| static TCGv_i32 cpu_R[16]; |
| static TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; |
| static TCGv_i64 cpu_exclusive_addr; |
| static TCGv_i64 cpu_exclusive_val; |
| #ifdef CONFIG_USER_ONLY |
| static TCGv_i64 cpu_exclusive_test; |
| static TCGv_i32 cpu_exclusive_info; |
| #endif |
| |
| /* FIXME: These should be removed. */ |
| static TCGv_i32 cpu_F0s, cpu_F1s; |
| static TCGv_i64 cpu_F0d, cpu_F1d; |
| |
| #include "exec/gen-icount.h" |
| |
| static const char *regnames[] = |
| { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
| "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" }; |
| |
| /* initialize TCG globals. */ |
| void arm_translate_init(void) |
| { |
| int i; |
| |
| cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); |
| |
| for (i = 0; i < 16; i++) { |
| cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0, |
| offsetof(CPUARMState, regs[i]), |
| regnames[i]); |
| } |
| cpu_CF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, CF), "CF"); |
| cpu_NF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, NF), "NF"); |
| cpu_VF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, VF), "VF"); |
| cpu_ZF = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUARMState, ZF), "ZF"); |
| |
| cpu_exclusive_addr = tcg_global_mem_new_i64(TCG_AREG0, |
| offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); |
| cpu_exclusive_val = tcg_global_mem_new_i64(TCG_AREG0, |
| offsetof(CPUARMState, exclusive_val), "exclusive_val"); |
| #ifdef CONFIG_USER_ONLY |
| cpu_exclusive_test = tcg_global_mem_new_i64(TCG_AREG0, |
| offsetof(CPUARMState, exclusive_test), "exclusive_test"); |
| cpu_exclusive_info = tcg_global_mem_new_i32(TCG_AREG0, |
| offsetof(CPUARMState, exclusive_info), "exclusive_info"); |
| #endif |
| |
| a64_translate_init(); |
| } |
| |
| 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)) |
| |
| /* 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) { |
| uint32_t addr; |
| /* normally, since we updated PC, we need only to add one insn */ |
| if (s->thumb) |
| addr = (long)s->pc + 2; |
| else |
| addr = (long)s->pc + 4; |
| tcg_gen_movi_i32(var, addr); |
| } 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; |
| } |
| |
| /* 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) { |
| tcg_gen_andi_i32(var, var, ~1); |
| s->is_jmp = DISAS_JUMP; |
| } |
| tcg_gen_mov_i32(cpu_R[reg], var); |
| tcg_temp_free_i32(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_exception(int excp, uint32_t syndrome) |
| { |
| TCGv_i32 tcg_excp = tcg_const_i32(excp); |
| TCGv_i32 tcg_syn = tcg_const_i32(syndrome); |
| |
| gen_helper_exception_with_syndrome(cpu_env, tcg_excp, tcg_syn); |
| tcg_temp_free_i32(tcg_syn); |
| tcg_temp_free_i32(tcg_excp); |
| } |
| |
| static void gen_ss_advance(DisasContext *s) |
| { |
| /* If the singlestep state is Active-not-pending, advance to |
| * Active-pending. |
| */ |
| if (s->ss_active) { |
| s->pstate_ss = 0; |
| gen_helper_clear_pstate_ss(cpu_env); |
| } |
| } |
| |
| 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_exception(EXCP_UDEF, syn_swstep(s->ss_same_el, 1, s->is_ldex)); |
| s->is_jmp = DISAS_EXC; |
| } |
| |
| 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 var) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_shri_i32(tmp, var, 8); |
| tcg_gen_andi_i32(tmp, tmp, 0x00ff00ff); |
| tcg_gen_shli_i32(var, var, 8); |
| tcg_gen_andi_i32(var, var, 0xff00ff00); |
| tcg_gen_or_i32(var, var, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| /* Byteswap low halfword and sign extend. */ |
| static void gen_revsh(TCGv_i32 var) |
| { |
| tcg_gen_ext16u_i32(var, var); |
| tcg_gen_bswap16_i32(var, var); |
| tcg_gen_ext16s_i32(var, var); |
| } |
| |
| /* Unsigned bitfield extract. */ |
| static void gen_ubfx(TCGv_i32 var, int shift, uint32_t mask) |
| { |
| if (shift) |
| tcg_gen_shri_i32(var, var, shift); |
| tcg_gen_andi_i32(var, var, mask); |
| } |
| |
| /* Signed bitfield extract. */ |
| static void gen_sbfx(TCGv_i32 var, int shift, int width) |
| { |
| uint32_t signbit; |
| |
| if (shift) |
| tcg_gen_sari_i32(var, var, shift); |
| if (shift + width < 32) { |
| signbit = 1u << (width - 1); |
| tcg_gen_andi_i32(var, var, (1u << width) - 1); |
| tcg_gen_xori_i32(var, var, signbit); |
| tcg_gen_subi_i32(var, var, signbit); |
| } |
| } |
| |
| /* Return (b << 32) + a. Mark inputs as dead */ |
| static TCGv_i64 gen_addq_msw(TCGv_i64 a, TCGv_i32 b) |
| { |
| TCGv_i64 tmp64 = tcg_temp_new_i64(); |
| |
| tcg_gen_extu_i32_i64(tmp64, b); |
| tcg_temp_free_i32(b); |
| tcg_gen_shli_i64(tmp64, tmp64, 32); |
| tcg_gen_add_i64(a, tmp64, a); |
| |
| tcg_temp_free_i64(tmp64); |
| return a; |
| } |
| |
| /* Return (b << 32) - a. Mark inputs as dead. */ |
| static TCGv_i64 gen_subq_msw(TCGv_i64 a, TCGv_i32 b) |
| { |
| TCGv_i64 tmp64 = tcg_temp_new_i64(); |
| |
| tcg_gen_extu_i32_i64(tmp64, b); |
| tcg_temp_free_i32(b); |
| tcg_gen_shli_i64(tmp64, tmp64, 32); |
| tcg_gen_sub_i64(a, tmp64, a); |
| |
| tcg_temp_free_i64(tmp64); |
| return a; |
| } |
| |
| /* 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) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_shri_i32(tmp, var, 16); |
| tcg_gen_shli_i32(var, var, 16); |
| tcg_gen_or_i32(var, var, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| /* 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 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(t0, t0, tmp); |
| tcg_temp_free_i32(tmp); |
| tcg_temp_free_i32(t1); |
| } |
| |
| /* Set CF to the top bit of var. */ |
| static void gen_set_CF_bit31(TCGv_i32 var) |
| { |
| tcg_gen_shri_i32(cpu_CF, var, 31); |
| } |
| |
| /* 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); |
| } |
| |
| /* T0 += T1 + CF. */ |
| static void gen_adc(TCGv_i32 t0, TCGv_i32 t1) |
| { |
| tcg_gen_add_i32(t0, t0, t1); |
| tcg_gen_add_i32(t0, t0, cpu_CF); |
| } |
| |
| /* 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 tcg_gen_abs_i32(TCGv_i32 dest, TCGv_i32 src) |
| { |
| TCGv_i32 c0 = tcg_const_i32(0); |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_neg_i32(tmp, src); |
| tcg_gen_movcond_i32(TCG_COND_GT, dest, src, c0, src, tmp); |
| tcg_temp_free_i32(c0); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| static void shifter_out_im(TCGv_i32 var, int shift) |
| { |
| if (shift == 0) { |
| tcg_gen_andi_i32(cpu_CF, var, 1); |
| } else { |
| tcg_gen_shri_i32(cpu_CF, var, shift); |
| if (shift != 31) { |
| tcg_gen_andi_i32(cpu_CF, cpu_CF, 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); |
| } |
| |
| #define PAS_OP(pfx) \ |
| switch (op2) { \ |
| case 0: gen_pas_helper(glue(pfx,add16)); break; \ |
| case 1: gen_pas_helper(glue(pfx,addsubx)); break; \ |
| case 2: gen_pas_helper(glue(pfx,subaddx)); break; \ |
| case 3: gen_pas_helper(glue(pfx,sub16)); break; \ |
| case 4: gen_pas_helper(glue(pfx,add8)); break; \ |
| case 7: gen_pas_helper(glue(pfx,sub8)); break; \ |
| } |
| static void gen_arm_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b) |
| { |
| TCGv_ptr tmp; |
| |
| switch (op1) { |
| #define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp) |
| case 1: |
| tmp = tcg_temp_new_ptr(); |
| tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUARMState, GE)); |
| PAS_OP(s) |
| tcg_temp_free_ptr(tmp); |
| break; |
| case 5: |
| tmp = tcg_temp_new_ptr(); |
| tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUARMState, GE)); |
| PAS_OP(u) |
| tcg_temp_free_ptr(tmp); |
| break; |
| #undef gen_pas_helper |
| #define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b) |
| case 2: |
| PAS_OP(q); |
| break; |
| case 3: |
| PAS_OP(sh); |
| break; |
| case 6: |
| PAS_OP(uq); |
| break; |
| case 7: |
| PAS_OP(uh); |
| break; |
| #undef gen_pas_helper |
| } |
| } |
| #undef PAS_OP |
| |
| /* For unknown reasons Arm and Thumb-2 use arbitrarily different encodings. */ |
| #define PAS_OP(pfx) \ |
| switch (op1) { \ |
| case 0: gen_pas_helper(glue(pfx,add8)); break; \ |
| case 1: gen_pas_helper(glue(pfx,add16)); break; \ |
| case 2: gen_pas_helper(glue(pfx,addsubx)); break; \ |
| case 4: gen_pas_helper(glue(pfx,sub8)); break; \ |
| case 5: gen_pas_helper(glue(pfx,sub16)); break; \ |
| case 6: gen_pas_helper(glue(pfx,subaddx)); break; \ |
| } |
| static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv_i32 a, TCGv_i32 b) |
| { |
| TCGv_ptr tmp; |
| |
| switch (op2) { |
| #define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp) |
| case 0: |
| tmp = tcg_temp_new_ptr(); |
| tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUARMState, GE)); |
| PAS_OP(s) |
| tcg_temp_free_ptr(tmp); |
| break; |
| case 4: |
| tmp = tcg_temp_new_ptr(); |
| tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUARMState, GE)); |
| PAS_OP(u) |
| tcg_temp_free_ptr(tmp); |
| break; |
| #undef gen_pas_helper |
| #define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b) |
| case 1: |
| PAS_OP(q); |
| break; |
| case 2: |
| PAS_OP(sh); |
| break; |
| case 5: |
| PAS_OP(uq); |
| break; |
| case 6: |
| PAS_OP(uh); |
| break; |
| #undef gen_pas_helper |
| } |
| } |
| #undef PAS_OP |
| |
| /* |
| * generate a conditional branch based on ARM condition code cc. |
| * This is common between ARM and Aarch64 targets. |
| */ |
| void arm_gen_test_cc(int cc, int label) |
| { |
| TCGv_i32 tmp; |
| int inv; |
| |
| switch (cc) { |
| case 0: /* eq: Z */ |
| tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); |
| break; |
| case 1: /* ne: !Z */ |
| tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label); |
| break; |
| case 2: /* cs: C */ |
| tcg_gen_brcondi_i32(TCG_COND_NE, cpu_CF, 0, label); |
| break; |
| case 3: /* cc: !C */ |
| tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label); |
| break; |
| case 4: /* mi: N */ |
| tcg_gen_brcondi_i32(TCG_COND_LT, cpu_NF, 0, label); |
| break; |
| case 5: /* pl: !N */ |
| tcg_gen_brcondi_i32(TCG_COND_GE, cpu_NF, 0, label); |
| break; |
| case 6: /* vs: V */ |
| tcg_gen_brcondi_i32(TCG_COND_LT, cpu_VF, 0, label); |
| break; |
| case 7: /* vc: !V */ |
| tcg_gen_brcondi_i32(TCG_COND_GE, cpu_VF, 0, label); |
| break; |
| case 8: /* hi: C && !Z */ |
| inv = gen_new_label(); |
| tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, inv); |
| tcg_gen_brcondi_i32(TCG_COND_NE, cpu_ZF, 0, label); |
| gen_set_label(inv); |
| break; |
| case 9: /* ls: !C || Z */ |
| tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_CF, 0, label); |
| tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); |
| break; |
| case 10: /* ge: N == V -> N ^ V == 0 */ |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); |
| tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); |
| tcg_temp_free_i32(tmp); |
| break; |
| case 11: /* lt: N != V -> N ^ V != 0 */ |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); |
| tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); |
| tcg_temp_free_i32(tmp); |
| break; |
| case 12: /* gt: !Z && N == V */ |
| inv = gen_new_label(); |
| tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, inv); |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); |
| tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); |
| tcg_temp_free_i32(tmp); |
| gen_set_label(inv); |
| break; |
| case 13: /* le: Z || N != V */ |
| tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ZF, 0, label); |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); |
| tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); |
| tcg_temp_free_i32(tmp); |
| break; |
| default: |
| fprintf(stderr, "Bad condition code 0x%x\n", cc); |
| abort(); |
| } |
| } |
| |
| static const uint8_t table_logic_cc[16] = { |
| 1, /* and */ |
| 1, /* xor */ |
| 0, /* sub */ |
| 0, /* rsb */ |
| 0, /* add */ |
| 0, /* adc */ |
| 0, /* sbc */ |
| 0, /* rsc */ |
| 1, /* andl */ |
| 1, /* xorl */ |
| 0, /* cmp */ |
| 0, /* cmn */ |
| 1, /* orr */ |
| 1, /* mov */ |
| 1, /* bic */ |
| 1, /* mvn */ |
| }; |
| |
| /* Set PC and Thumb state from an immediate address. */ |
| static inline void gen_bx_im(DisasContext *s, uint32_t addr) |
| { |
| TCGv_i32 tmp; |
| |
| s->is_jmp = DISAS_UPDATE; |
| if (s->thumb != (addr & 1)) { |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_movi_i32(tmp, addr & 1); |
| tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUARMState, thumb)); |
| tcg_temp_free_i32(tmp); |
| } |
| tcg_gen_movi_i32(cpu_R[15], addr & ~1); |
| } |
| |
| /* Set PC and Thumb state from var. var is marked as dead. */ |
| static inline void gen_bx(DisasContext *s, TCGv_i32 var) |
| { |
| s->is_jmp = DISAS_UPDATE; |
| tcg_gen_andi_i32(cpu_R[15], var, ~1); |
| tcg_gen_andi_i32(var, var, 1); |
| store_cpu_field(var, thumb); |
| } |
| |
| /* 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(s, var); |
| } else { |
| store_reg(s, reg, var); |
| } |
| } |
| |
| /* 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. |
| */ |
| #if TARGET_LONG_BITS == 32 |
| |
| #define DO_GEN_LD(SUFF, OPC) \ |
| static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ |
| { \ |
| tcg_gen_qemu_ld_i32(val, addr, index, OPC); \ |
| } |
| |
| #define DO_GEN_ST(SUFF, OPC) \ |
| static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ |
| { \ |
| tcg_gen_qemu_st_i32(val, addr, index, OPC); \ |
| } |
| |
| static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) |
| { |
| tcg_gen_qemu_ld_i64(val, addr, index, MO_TEQ); |
| } |
| |
| static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) |
| { |
| tcg_gen_qemu_st_i64(val, addr, index, MO_TEQ); |
| } |
| |
| #else |
| |
| #define DO_GEN_LD(SUFF, OPC) \ |
| static inline void gen_aa32_ld##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ |
| { \ |
| TCGv addr64 = tcg_temp_new(); \ |
| tcg_gen_extu_i32_i64(addr64, addr); \ |
| tcg_gen_qemu_ld_i32(val, addr64, index, OPC); \ |
| tcg_temp_free(addr64); \ |
| } |
| |
| #define DO_GEN_ST(SUFF, OPC) \ |
| static inline void gen_aa32_st##SUFF(TCGv_i32 val, TCGv_i32 addr, int index) \ |
| { \ |
| TCGv addr64 = tcg_temp_new(); \ |
| tcg_gen_extu_i32_i64(addr64, addr); \ |
| tcg_gen_qemu_st_i32(val, addr64, index, OPC); \ |
| tcg_temp_free(addr64); \ |
| } |
| |
| static inline void gen_aa32_ld64(TCGv_i64 val, TCGv_i32 addr, int index) |
| { |
| TCGv addr64 = tcg_temp_new(); |
| tcg_gen_extu_i32_i64(addr64, addr); |
| tcg_gen_qemu_ld_i64(val, addr64, index, MO_TEQ); |
| tcg_temp_free(addr64); |
| } |
| |
| static inline void gen_aa32_st64(TCGv_i64 val, TCGv_i32 addr, int index) |
| { |
| TCGv addr64 = tcg_temp_new(); |
| tcg_gen_extu_i32_i64(addr64, addr); |
| tcg_gen_qemu_st_i64(val, addr64, index, MO_TEQ); |
| tcg_temp_free(addr64); |
| } |
| |
| #endif |
| |
| DO_GEN_LD(8s, MO_SB) |
| DO_GEN_LD(8u, MO_UB) |
| DO_GEN_LD(16s, MO_TESW) |
| DO_GEN_LD(16u, MO_TEUW) |
| DO_GEN_LD(32u, MO_TEUL) |
| DO_GEN_ST(8, MO_UB) |
| DO_GEN_ST(16, MO_TEUW) |
| DO_GEN_ST(32, MO_TEUL) |
| |
| static inline void gen_set_pc_im(DisasContext *s, target_ulong val) |
| { |
| tcg_gen_movi_i32(cpu_R[15], val); |
| } |
| |
| 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 - 4); |
| 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->pc); |
| s->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 - 4); |
| 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->pc); |
| s->is_jmp = DISAS_SMC; |
| } |
| |
| 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 void gen_exception_internal_insn(DisasContext *s, int offset, int excp) |
| { |
| gen_set_condexec(s); |
| gen_set_pc_im(s, s->pc - offset); |
| gen_exception_internal(excp); |
| s->is_jmp = DISAS_JUMP; |
| } |
| |
| static void gen_exception_insn(DisasContext *s, int offset, int excp, int syn) |
| { |
| gen_set_condexec(s); |
| gen_set_pc_im(s, s->pc - offset); |
| gen_exception(excp, syn); |
| s->is_jmp = DISAS_JUMP; |
| } |
| |
| /* 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->pc & ~1); |
| s->is_jmp = DISAS_UPDATE; |
| } |
| |
| static inline void gen_add_data_offset(DisasContext *s, unsigned int insn, |
| TCGv_i32 var) |
| { |
| int val, rm, shift, shiftop; |
| TCGv_i32 offset; |
| |
| if (!(insn & (1 << 25))) { |
| /* immediate */ |
| val = insn & 0xfff; |
| if (!(insn & (1 << 23))) |
| val = -val; |
| if (val != 0) |
| tcg_gen_addi_i32(var, var, val); |
| } else { |
| /* shift/register */ |
| rm = (insn) & 0xf; |
| shift = (insn >> 7) & 0x1f; |
| shiftop = (insn >> 5) & 3; |
| offset = load_reg(s, rm); |
| gen_arm_shift_im(offset, shiftop, shift, 0); |
| if (!(insn & (1 << 23))) |
| tcg_gen_sub_i32(var, var, offset); |
| else |
| tcg_gen_add_i32(var, var, offset); |
| tcg_temp_free_i32(offset); |
| } |
| } |
| |
| static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn, |
| int extra, TCGv_i32 var) |
| { |
| int val, rm; |
| TCGv_i32 offset; |
| |
| if (insn & (1 << 22)) { |
| /* immediate */ |
| val = (insn & 0xf) | ((insn >> 4) & 0xf0); |
| if (!(insn & (1 << 23))) |
| val = -val; |
| val += extra; |
| if (val != 0) |
| tcg_gen_addi_i32(var, var, val); |
| } else { |
| /* register */ |
| if (extra) |
| tcg_gen_addi_i32(var, var, extra); |
| rm = (insn) & 0xf; |
| offset = load_reg(s, rm); |
| if (!(insn & (1 << 23))) |
| tcg_gen_sub_i32(var, var, offset); |
| else |
| tcg_gen_add_i32(var, var, offset); |
| tcg_temp_free_i32(offset); |
| } |
| } |
| |
| 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; |
| } |
| |
| #define VFP_OP2(name) \ |
| static inline void gen_vfp_##name(int dp) \ |
| { \ |
| TCGv_ptr fpst = get_fpstatus_ptr(0); \ |
| if (dp) { \ |
| gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, fpst); \ |
| } else { \ |
| gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, fpst); \ |
| } \ |
| tcg_temp_free_ptr(fpst); \ |
| } |
| |
| VFP_OP2(add) |
| VFP_OP2(sub) |
| VFP_OP2(mul) |
| VFP_OP2(div) |
| |
| #undef VFP_OP2 |
| |
| static inline void gen_vfp_F1_mul(int dp) |
| { |
| /* Like gen_vfp_mul() but put result in F1 */ |
| TCGv_ptr fpst = get_fpstatus_ptr(0); |
| if (dp) { |
| gen_helper_vfp_muld(cpu_F1d, cpu_F0d, cpu_F1d, fpst); |
| } else { |
| gen_helper_vfp_muls(cpu_F1s, cpu_F0s, cpu_F1s, fpst); |
| } |
| tcg_temp_free_ptr(fpst); |
| } |
| |
| static inline void gen_vfp_F1_neg(int dp) |
| { |
| /* Like gen_vfp_neg() but put result in F1 */ |
| if (dp) { |
| gen_helper_vfp_negd(cpu_F1d, cpu_F0d); |
| } else { |
| gen_helper_vfp_negs(cpu_F1s, cpu_F0s); |
| } |
| } |
| |
| static inline void gen_vfp_abs(int dp) |
| { |
| if (dp) |
| gen_helper_vfp_absd(cpu_F0d, cpu_F0d); |
| else |
| gen_helper_vfp_abss(cpu_F0s, cpu_F0s); |
| } |
| |
| static inline void gen_vfp_neg(int dp) |
| { |
| if (dp) |
| gen_helper_vfp_negd(cpu_F0d, cpu_F0d); |
| else |
| gen_helper_vfp_negs(cpu_F0s, cpu_F0s); |
| } |
| |
| static inline void gen_vfp_sqrt(int dp) |
| { |
| if (dp) |
| gen_helper_vfp_sqrtd(cpu_F0d, cpu_F0d, cpu_env); |
| else |
| gen_helper_vfp_sqrts(cpu_F0s, cpu_F0s, cpu_env); |
| } |
| |
| static inline void gen_vfp_cmp(int dp) |
| { |
| if (dp) |
| gen_helper_vfp_cmpd(cpu_F0d, cpu_F1d, cpu_env); |
| else |
| gen_helper_vfp_cmps(cpu_F0s, cpu_F1s, cpu_env); |
| } |
| |
| static inline void gen_vfp_cmpe(int dp) |
| { |
| if (dp) |
| gen_helper_vfp_cmped(cpu_F0d, cpu_F1d, cpu_env); |
| else |
| gen_helper_vfp_cmpes(cpu_F0s, cpu_F1s, cpu_env); |
| } |
| |
| static inline void gen_vfp_F1_ld0(int dp) |
| { |
| if (dp) |
| tcg_gen_movi_i64(cpu_F1d, 0); |
| else |
| tcg_gen_movi_i32(cpu_F1s, 0); |
| } |
| |
| #define VFP_GEN_ITOF(name) \ |
| static inline void gen_vfp_##name(int dp, int neon) \ |
| { \ |
| TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ |
| if (dp) { \ |
| gen_helper_vfp_##name##d(cpu_F0d, cpu_F0s, statusptr); \ |
| } else { \ |
| gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \ |
| } \ |
| tcg_temp_free_ptr(statusptr); \ |
| } |
| |
| VFP_GEN_ITOF(uito) |
| VFP_GEN_ITOF(sito) |
| #undef VFP_GEN_ITOF |
| |
| #define VFP_GEN_FTOI(name) \ |
| static inline void gen_vfp_##name(int dp, int neon) \ |
| { \ |
| TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ |
| if (dp) { \ |
| gen_helper_vfp_##name##d(cpu_F0s, cpu_F0d, statusptr); \ |
| } else { \ |
| gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, statusptr); \ |
| } \ |
| tcg_temp_free_ptr(statusptr); \ |
| } |
| |
| VFP_GEN_FTOI(toui) |
| VFP_GEN_FTOI(touiz) |
| VFP_GEN_FTOI(tosi) |
| VFP_GEN_FTOI(tosiz) |
| #undef VFP_GEN_FTOI |
| |
| #define VFP_GEN_FIX(name, round) \ |
| static inline void gen_vfp_##name(int dp, int shift, int neon) \ |
| { \ |
| TCGv_i32 tmp_shift = tcg_const_i32(shift); \ |
| TCGv_ptr statusptr = get_fpstatus_ptr(neon); \ |
| if (dp) { \ |
| gen_helper_vfp_##name##d##round(cpu_F0d, cpu_F0d, tmp_shift, \ |
| statusptr); \ |
| } else { \ |
| gen_helper_vfp_##name##s##round(cpu_F0s, cpu_F0s, tmp_shift, \ |
| statusptr); \ |
| } \ |
| tcg_temp_free_i32(tmp_shift); \ |
| tcg_temp_free_ptr(statusptr); \ |
| } |
| VFP_GEN_FIX(tosh, _round_to_zero) |
| VFP_GEN_FIX(tosl, _round_to_zero) |
| VFP_GEN_FIX(touh, _round_to_zero) |
| VFP_GEN_FIX(toul, _round_to_zero) |
| VFP_GEN_FIX(shto, ) |
| VFP_GEN_FIX(slto, ) |
| VFP_GEN_FIX(uhto, ) |
| VFP_GEN_FIX(ulto, ) |
| #undef VFP_GEN_FIX |
| |
| static inline void gen_vfp_ld(DisasContext *s, int dp, TCGv_i32 addr) |
| { |
| if (dp) { |
| gen_aa32_ld64(cpu_F0d, addr, get_mem_index(s)); |
| } else { |
| gen_aa32_ld32u(cpu_F0s, addr, get_mem_index(s)); |
| } |
| } |
| |
| static inline void gen_vfp_st(DisasContext *s, int dp, TCGv_i32 addr) |
| { |
| if (dp) { |
| gen_aa32_st64(cpu_F0d, addr, get_mem_index(s)); |
| } else { |
| gen_aa32_st32(cpu_F0s, addr, get_mem_index(s)); |
| } |
| } |
| |
| static inline long |
| vfp_reg_offset (int dp, int reg) |
| { |
| if (dp) |
| return offsetof(CPUARMState, vfp.regs[reg]); |
| else if (reg & 1) { |
| return offsetof(CPUARMState, vfp.regs[reg >> 1]) |
| + offsetof(CPU_DoubleU, l.upper); |
| } else { |
| return offsetof(CPUARMState, vfp.regs[reg >> 1]) |
| + offsetof(CPU_DoubleU, l.lower); |
| } |
| } |
| |
| /* 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); |
| } |
| |
| 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_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 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)); |
| } |
| |
| #define tcg_gen_ld_f32 tcg_gen_ld_i32 |
| #define tcg_gen_ld_f64 tcg_gen_ld_i64 |
| #define tcg_gen_st_f32 tcg_gen_st_i32 |
| #define tcg_gen_st_f64 tcg_gen_st_i64 |
| |
| static inline void gen_mov_F0_vreg(int dp, int reg) |
| { |
| if (dp) |
| tcg_gen_ld_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg)); |
| else |
| tcg_gen_ld_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg)); |
| } |
| |
| static inline void gen_mov_F1_vreg(int dp, int reg) |
| { |
| if (dp) |
| tcg_gen_ld_f64(cpu_F1d, cpu_env, vfp_reg_offset(dp, reg)); |
| else |
| tcg_gen_ld_f32(cpu_F1s, cpu_env, vfp_reg_offset(dp, reg)); |
| } |
| |
| static inline void gen_mov_vreg_F0(int dp, int reg) |
| { |
| if (dp) |
| tcg_gen_st_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg)); |
| else |
| tcg_gen_st_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg)); |
| } |
| |
| #define ARM_CP_RW_BIT (1 << 20) |
| |
| 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_trunc_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_trunc_i64_i32(cpu_R[rdlo], cpu_V0); |
| tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); |
| tcg_gen_trunc_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(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(cpu_M0, addr, get_mem_index(s)); |
| i = 0; |
| } else { /* WLDRW wRd */ |
| tmp = tcg_temp_new_i32(); |
| gen_aa32_ld32u(tmp, addr, get_mem_index(s)); |
| } |
| } else { |
| tmp = tcg_temp_new_i32(); |
| if (insn & (1 << 22)) { /* WLDRH */ |
| gen_aa32_ld16u(tmp, addr, get_mem_index(s)); |
| } else { /* WLDRB */ |
| gen_aa32_ld8u(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(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(cpu_M0, addr, get_mem_index(s)); |
| } else { /* WSTRW wRd */ |
| tcg_gen_trunc_i64_i32(tmp, cpu_M0); |
| gen_aa32_st32(tmp, addr, get_mem_index(s)); |
| } |
| } else { |
| if (insn & (1 << 22)) { /* WSTRH */ |
| tcg_gen_trunc_i64_i32(tmp, cpu_M0); |
| gen_aa32_st16(tmp, addr, get_mem_index(s)); |
| } else { /* WSTRB */ |
| tcg_gen_trunc_i64_i32(tmp, cpu_M0); |
| gen_aa32_st8(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: |
| TCGV_UNUSED_I32(tmp2); |
| TCGV_UNUSED_I32(tmp3); |
| } |
| 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_trunc_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_trunc_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_trunc_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_trunc_i64_i32(cpu_R[rdlo], cpu_V0); |
| tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); |
| tcg_gen_trunc_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 (arm_dc_feature(s, ARM_FEATURE_VFP3)) { \ |
| 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) |
| |
| /* Move between integer and VFP cores. */ |
| static TCGv_i32 gen_vfp_mrs(void) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| tcg_gen_mov_i32(tmp, cpu_F0s); |
| return tmp; |
| } |
| |
| static void gen_vfp_msr(TCGv_i32 tmp) |
| { |
| tcg_gen_mov_i32(cpu_F0s, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| static void gen_neon_dup_u8(TCGv_i32 var, int shift) |
| { |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| if (shift) |
| tcg_gen_shri_i32(var, var, shift); |
| tcg_gen_ext8u_i32(var, var); |
| tcg_gen_shli_i32(tmp, var, 8); |
| tcg_gen_or_i32(var, var, tmp); |
| tcg_gen_shli_i32(tmp, var, 16); |
| tcg_gen_or_i32(var, var, tmp); |
| tcg_temp_free_i32(tmp); |
| } |
| |
| 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 TCGv_i32 gen_load_and_replicate(DisasContext *s, TCGv_i32 addr, int size) |
| { |
| /* Load a single Neon element and replicate into a 32 bit TCG reg */ |
| TCGv_i32 tmp = tcg_temp_new_i32(); |
| switch (size) { |
| case 0: |
| gen_aa32_ld8u(tmp, addr, get_mem_index(s)); |
| gen_neon_dup_u8(tmp, 0); |
| break; |
| case 1: |
| gen_aa32_ld16u(tmp, addr, get_mem_index(s)); |
| gen_neon_dup_low16(tmp); |
| break; |
| case 2: |
| gen_aa32_ld32u(tmp, addr, get_mem_index(s)); |
| break; |
| default: /* Avoid compiler warnings. */ |
| abort(); |
| } |
| return tmp; |
| } |
| |
| static int handle_vsel(uint32_t insn, uint32_t rd, uint32_t rn, uint32_t rm, |
| uint32_t dp) |
| { |
| uint32_t cc = extract32(insn, 20, 2); |
| |
| if (dp) { |
| TCGv_i64 frn, frm, dest; |
| TCGv_i64 tmp, zero, zf, nf, vf; |
| |
| zero = tcg_const_i64(0); |
| |
| frn = tcg_temp_new_i64(); |
| frm = tcg_temp_new_i64(); |
| dest = tcg_temp_new_i64(); |
| |
| zf = tcg_temp_new_i64(); |
| nf = tcg_temp_new_i64(); |
| vf = tcg_temp_new_i64(); |
| |
| tcg_gen_extu_i32_i64(zf, cpu_ZF); |
| tcg_gen_ext_i32_i64(nf, cpu_NF); |
| tcg_gen_ext_i32_i64(vf, cpu_VF); |
| |
| tcg_gen_ld_f64(frn, cpu_env, vfp_reg_offset(dp, rn)); |
| tcg_gen_ld_f64(frm, cpu_env, vfp_reg_offset(dp, rm)); |
| switch (cc) { |
| case 0: /* eq: Z */ |
| tcg_gen_movcond_i64(TCG_COND_EQ, dest, zf, zero, |
| frn, frm); |
| break; |
| case 1: /* vs: V */ |
| tcg_gen_movcond_i64(TCG_COND_LT, dest, vf, zero, |
| frn, frm); |
| break; |
| case 2: /* ge: N == V -> N ^ V == 0 */ |
| tmp = tcg_temp_new_i64(); |
| tcg_gen_xor_i64(tmp, vf, nf); |
| tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, |
| frn, frm); |
| tcg_temp_free_i64(tmp); |
| break; |
| case 3: /* gt: !Z && N == V */ |
| tcg_gen_movcond_i64(TCG_COND_NE, dest, zf, zero, |
| frn, frm); |
| tmp = tcg_temp_new_i64(); |
| tcg_gen_xor_i64(tmp, vf, nf); |
| tcg_gen_movcond_i64(TCG_COND_GE, dest, tmp, zero, |
| dest, frm); |
| tcg_temp_free_i64(tmp); |
| break; |
| } |
| tcg_gen_st_f64(dest, cpu_env, vfp_reg_offset(dp, rd)); |
| tcg_temp_free_i64(frn); |
| tcg_temp_free_i64(frm); |
| tcg_temp_free_i64(dest); |
| |
| tcg_temp_free_i64(zf); |
| tcg_temp_free_i64(nf); |
| tcg_temp_free_i64(vf); |
| |
| tcg_temp_free_i64(zero); |
| } else { |
| TCGv_i32 frn, frm, dest; |
| TCGv_i32 tmp, zero; |
| |
| zero = tcg_const_i32(0); |
| |
| frn = tcg_temp_new_i32(); |
| frm = tcg_temp_new_i32(); |
| dest = tcg_temp_new_i32(); |
| tcg_gen_ld_f32(frn, cpu_env, vfp_reg_offset(dp, rn)); |
| tcg_gen_ld_f32(frm, cpu_env, vfp_reg_offset(dp, rm)); |
| switch (cc) { |
| case 0: /* eq: Z */ |
| tcg_gen_movcond_i32(TCG_COND_EQ, dest, cpu_ZF, zero, |
| frn, frm); |
| break; |
| case 1: /* vs: V */ |
| tcg_gen_movcond_i32(TCG_COND_LT, dest, cpu_VF, zero, |
| frn, frm); |
| break; |
| case 2: /* ge: N == V -> N ^ V == 0 */ |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); |
| tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, |
| frn, frm); |
| tcg_temp_free_i32(tmp); |
| break; |
| case 3: /* gt: !Z && N == V */ |
| tcg_gen_movcond_i32(TCG_COND_NE, dest, cpu_ZF, zero, |
| frn, frm); |
| tmp = tcg_temp_new_i32(); |
| tcg_gen_xor_i32(tmp, cpu_VF, cpu_NF); |
| tcg_gen_movcond_i32(TCG_COND_GE, dest, tmp, zero, |
| dest, frm); |
| tcg_temp_free_i32(tmp); |
| break; |
| } |
| tcg_gen_st_f32(dest, cpu_env, vfp_reg_offset(dp, rd)); |
| tcg_temp_free_i32(frn); |
| tcg_temp_free_i32(frm); |
| tcg_temp_free_i32(dest); |
| |
| tcg_temp_free_i32(zero); |
| } |
| |
| return 0; |
| } |
| |
| static int handle_vminmaxnm(uint32_t insn, uint32_t rd, uint32_t rn, |
| uint32_t rm |