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