blob: bd5d5cb5768a455663d7bcdbf40a3c5602daac43 [file] [log] [blame]
/*
* 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-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"
#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 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"
#if defined(CONFIG_USER_ONLY)
#define IS_USER(s) 1
#else
#define IS_USER(s) (s->user)
#endif
TCGv_env 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];
TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF;
TCGv_i64 cpu_exclusive_addr;
TCGv_i64 cpu_exclusive_val;
#ifdef CONFIG_USER_ONLY
TCGv_i64 cpu_exclusive_test;
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");
tcg_ctx.tcg_env = cpu_env;
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");
#ifdef CONFIG_USER_ONLY
cpu_exclusive_test = tcg_global_mem_new_i64(cpu_env,
offsetof(CPUARMState, exclusive_test), "exclusive_test");
cpu_exclusive_info = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUARMState, exclusive_info), "exclusive_info");
#endif
a64_translate_init();
}
static inline ARMMMUIdx get_a32_user_mem_index(DisasContext *s)
{
/* Return the 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_S1E2: /* this one is UNPREDICTABLE */
case ARMMMUIdx_S12NSE0:
case ARMMMUIdx_S12NSE1:
return ARMMMUIdx_S12NSE0;
case ARMMMUIdx_S1E3:
case ARMMMUIdx_S1SE0:
case ARMMMUIdx_S1SE1:
return ARMMMUIdx_S1SE0;
case ARMMMUIdx_S2NS:
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))
/* 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, uint32_t target_el)
{
TCGv_i32 tcg_excp = tcg_const_i32(excp);
TCGv_i32 tcg_syn = tcg_const_i32(syndrome);
TCGv_i32 tcg_el = tcg_const_i32(target_el);
gen_helper_exception_with_syndrome(cpu_env, tcg_excp,
tcg_syn, tcg_el);
tcg_temp_free_i32(tcg_el);
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),
default_exception_el(s));
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 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 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_JUMP;
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_JUMP;
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);
}
}
#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.
*/
#if TARGET_LONG_BITS == 32
#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \
static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \
TCGv_i32 addr, int index) \
{ \
TCGMemOp opc = (OPC) | s->be_data; \
/* Not needed for user-mode BE32, where we use MO_BE instead. */ \
if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \
TCGv addr_be = tcg_temp_new(); \
tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \
tcg_gen_qemu_ld_i32(val, addr_be, index, opc); \
tcg_temp_free(addr_be); \
return; \
} \
tcg_gen_qemu_ld_i32(val, addr, index, opc); \
}
#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \
static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \
TCGv_i32 addr, int index) \
{ \
TCGMemOp opc = (OPC) | s->be_data; \
/* Not needed for user-mode BE32, where we use MO_BE instead. */ \
if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \
TCGv addr_be = tcg_temp_new(); \
tcg_gen_xori_i32(addr_be, addr, BE32_XOR); \
tcg_gen_qemu_st_i32(val, addr_be, index, opc); \
tcg_temp_free(addr_be); \
return; \
} \
tcg_gen_qemu_st_i32(val, addr, index, opc); \
}
static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val,
TCGv_i32 addr, int index)
{
TCGMemOp opc = MO_Q | s->be_data;
tcg_gen_qemu_ld_i64(val, addr, index, opc);
/* 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 inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val,
TCGv_i32 addr, int index)
{
TCGMemOp opc = MO_Q | s->be_data;
/* 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);
return;
}
tcg_gen_qemu_st_i64(val, addr, index, opc);
}
#else
#define DO_GEN_LD(SUFF, OPC, BE32_XOR) \
static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \
TCGv_i32 addr, int index) \
{ \
TCGMemOp opc = (OPC) | s->be_data; \
TCGv addr64 = tcg_temp_new(); \
tcg_gen_extu_i32_i64(addr64, addr); \
/* Not needed for user-mode BE32, where we use MO_BE instead. */ \
if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \
tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \
} \
tcg_gen_qemu_ld_i32(val, addr64, index, opc); \
tcg_temp_free(addr64); \
}
#define DO_GEN_ST(SUFF, OPC, BE32_XOR) \
static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \
TCGv_i32 addr, int index) \
{ \
TCGMemOp opc = (OPC) | s->be_data; \
TCGv addr64 = tcg_temp_new(); \
tcg_gen_extu_i32_i64(addr64, addr); \
/* Not needed for user-mode BE32, where we use MO_BE instead. */ \
if (!IS_USER_ONLY && s->sctlr_b && BE32_XOR) { \
tcg_gen_xori_i64(addr64, addr64, BE32_XOR); \
} \
tcg_gen_qemu_st_i32(val, addr64, index, opc); \
tcg_temp_free(addr64); \
}
static inline void gen_aa32_ld64(DisasContext *s, TCGv_i64 val,
TCGv_i32 addr, int index)
{
TCGMemOp opc = MO_Q | s->be_data;
TCGv addr64 = tcg_temp_new();
tcg_gen_extu_i32_i64(addr64, addr);
tcg_gen_qemu_ld_i64(val, addr64, index, opc);
/* 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);
}
tcg_temp_free(addr64);
}
static inline void gen_aa32_st64(DisasContext *s, TCGv_i64 val,
TCGv_i32 addr, int index)
{
TCGMemOp opc = MO_Q | s->be_data;
TCGv addr64 = tcg_temp_new();
tcg_gen_extu_i32_i64(addr64, addr);
/* Not needed for user-mode BE32, where we use MO_BE instead. */
if (!IS_USER_ONLY && s->sctlr_b) {
TCGv tmp = tcg_temp_new();
tcg_gen_rotri_i64(tmp, val, 32);
tcg_gen_qemu_st_i64(tmp, addr64, index, opc);
tcg_temp_free(tmp);
} else {
tcg_gen_qemu_st_i64(val, addr64, index, opc);
}
tcg_temp_free(addr64);
}
#endif
DO_GEN_LD(8s, MO_SB, 3)
DO_GEN_LD(8u, MO_UB, 3)
DO_GEN_LD(16s, MO_SW, 2)
DO_GEN_LD(16u, MO_UW, 2)
DO_GEN_LD(32u, MO_UL, 0)
/* 'a' variants include an alignment check */
DO_GEN_LD(16ua, MO_UW | MO_ALIGN, 2)
DO_GEN_LD(32ua, MO_UL | MO_ALIGN, 0)
DO_GEN_ST(8, MO_UB, 3)
DO_GEN_ST(16, MO_UW, 2)
DO_GEN_ST(32, MO_UL, 0)
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, uint32_t target_el)
{
gen_set_condexec(s);
gen_set_pc_im(s, s->pc - offset);
gen_exception(excp, syn, target_el);
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_JUMP;
}
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(s, cpu_F0d, addr, get_mem_index(s));
} else {
gen_aa32_ld32u(s, 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(s, cpu_F0d, addr, get_mem_index(s));
} else {
gen_aa32_st32(s, 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_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_shri_i64(cpu_V0, cpu_V0, 32);
tcg_gen_extrl_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:
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_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_shri_i64(cpu_V0, cpu_V0, 32);
tcg_gen_extrl_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