qemu/target/openrisc/translate.c
Stafford Horne 08f021de3a target/openrisc: Allow fpcsr access in user mode
As per OpenRISC spec 1.4 FPCSR can be read and written in user mode.

Update mtspr and mfspr helpers to support this by moving the is_user
check into the helper.

Link: https://raw.githubusercontent.com/openrisc/doc/master/openrisc-arch-1.4-rev0.pdf
Signed-off-by: Stafford Horne <shorne@gmail.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
2023-05-11 15:40:03 +01:00

1682 lines
43 KiB
C

/*
* OpenRISC translation
*
* Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
* Feng Gao <gf91597@gmail.com>
*
* 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.1 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 "exec/exec-all.h"
#include "disas/disas.h"
#include "tcg/tcg-op.h"
#include "qemu/log.h"
#include "qemu/bitops.h"
#include "qemu/qemu-print.h"
#include "exec/cpu_ldst.h"
#include "exec/translator.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
#include "exec/gen-icount.h"
#include "exec/log.h"
/* is_jmp field values */
#define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */
#define DISAS_JUMP DISAS_TARGET_1 /* exit via jmp_pc/jmp_pc_imm */
typedef struct DisasContext {
DisasContextBase base;
uint32_t mem_idx;
uint32_t tb_flags;
uint32_t delayed_branch;
uint32_t cpucfgr;
uint32_t avr;
/* If not -1, jmp_pc contains this value and so is a direct jump. */
target_ulong jmp_pc_imm;
/* The temporary corresponding to register 0 for this compilation. */
TCGv R0;
/* The constant zero. */
TCGv zero;
} DisasContext;
static inline bool is_user(DisasContext *dc)
{
#ifdef CONFIG_USER_ONLY
return true;
#else
return !(dc->tb_flags & TB_FLAGS_SM);
#endif
}
/* Include the auto-generated decoder. */
#include "decode-insns.c.inc"
static TCGv cpu_sr;
static TCGv cpu_regs[32];
static TCGv cpu_pc;
static TCGv jmp_pc; /* l.jr/l.jalr temp pc */
static TCGv cpu_ppc;
static TCGv cpu_sr_f; /* bf/bnf, F flag taken */
static TCGv cpu_sr_cy; /* carry (unsigned overflow) */
static TCGv cpu_sr_ov; /* signed overflow */
static TCGv cpu_lock_addr;
static TCGv cpu_lock_value;
static TCGv_i32 fpcsr;
static TCGv_i64 cpu_mac; /* MACHI:MACLO */
static TCGv_i32 cpu_dflag;
void openrisc_translate_init(void)
{
static const char * const regnames[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
};
int i;
cpu_sr = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, sr), "sr");
cpu_dflag = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUOpenRISCState, dflag),
"dflag");
cpu_pc = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, pc), "pc");
cpu_ppc = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, ppc), "ppc");
jmp_pc = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, jmp_pc), "jmp_pc");
cpu_sr_f = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, sr_f), "sr_f");
cpu_sr_cy = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, sr_cy), "sr_cy");
cpu_sr_ov = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, sr_ov), "sr_ov");
cpu_lock_addr = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, lock_addr),
"lock_addr");
cpu_lock_value = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState, lock_value),
"lock_value");
fpcsr = tcg_global_mem_new_i32(cpu_env,
offsetof(CPUOpenRISCState, fpcsr),
"fpcsr");
cpu_mac = tcg_global_mem_new_i64(cpu_env,
offsetof(CPUOpenRISCState, mac),
"mac");
for (i = 0; i < 32; i++) {
cpu_regs[i] = tcg_global_mem_new(cpu_env,
offsetof(CPUOpenRISCState,
shadow_gpr[0][i]),
regnames[i]);
}
}
static void gen_exception(DisasContext *dc, unsigned int excp)
{
gen_helper_exception(cpu_env, tcg_constant_i32(excp));
}
static void gen_illegal_exception(DisasContext *dc)
{
tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
gen_exception(dc, EXCP_ILLEGAL);
dc->base.is_jmp = DISAS_NORETURN;
}
static bool check_v1_3(DisasContext *dc)
{
return dc->avr >= 0x01030000;
}
static bool check_of32s(DisasContext *dc)
{
return dc->cpucfgr & CPUCFGR_OF32S;
}
static bool check_of64a32s(DisasContext *dc)
{
return dc->cpucfgr & CPUCFGR_OF64A32S;
}
static TCGv cpu_R(DisasContext *dc, int reg)
{
if (reg == 0) {
return dc->R0;
} else {
return cpu_regs[reg];
}
}
/*
* We're about to write to REG. On the off-chance that the user is
* writing to R0, re-instate the architectural register.
*/
static void check_r0_write(DisasContext *dc, int reg)
{
if (unlikely(reg == 0)) {
dc->R0 = cpu_regs[0];
}
}
static void gen_ove_cy(DisasContext *dc)
{
if (dc->tb_flags & SR_OVE) {
gen_helper_ove_cy(cpu_env);
}
}
static void gen_ove_ov(DisasContext *dc)
{
if (dc->tb_flags & SR_OVE) {
gen_helper_ove_ov(cpu_env);
}
}
static void gen_ove_cyov(DisasContext *dc)
{
if (dc->tb_flags & SR_OVE) {
gen_helper_ove_cyov(cpu_env);
}
}
static void gen_add(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb)
{
TCGv t0 = tcg_temp_new();
TCGv res = tcg_temp_new();
tcg_gen_add2_tl(res, cpu_sr_cy, srca, dc->zero, srcb, dc->zero);
tcg_gen_xor_tl(cpu_sr_ov, srca, srcb);
tcg_gen_xor_tl(t0, res, srcb);
tcg_gen_andc_tl(cpu_sr_ov, t0, cpu_sr_ov);
tcg_gen_mov_tl(dest, res);
gen_ove_cyov(dc);
}
static void gen_addc(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb)
{
TCGv t0 = tcg_temp_new();
TCGv res = tcg_temp_new();
tcg_gen_add2_tl(res, cpu_sr_cy, srca, dc->zero, cpu_sr_cy, dc->zero);
tcg_gen_add2_tl(res, cpu_sr_cy, res, cpu_sr_cy, srcb, dc->zero);
tcg_gen_xor_tl(cpu_sr_ov, srca, srcb);
tcg_gen_xor_tl(t0, res, srcb);
tcg_gen_andc_tl(cpu_sr_ov, t0, cpu_sr_ov);
tcg_gen_mov_tl(dest, res);
gen_ove_cyov(dc);
}
static void gen_sub(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb)
{
TCGv res = tcg_temp_new();
tcg_gen_sub_tl(res, srca, srcb);
tcg_gen_xor_tl(cpu_sr_cy, srca, srcb);
tcg_gen_xor_tl(cpu_sr_ov, res, srcb);
tcg_gen_and_tl(cpu_sr_ov, cpu_sr_ov, cpu_sr_cy);
tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_cy, srca, srcb);
tcg_gen_mov_tl(dest, res);
gen_ove_cyov(dc);
}
static void gen_mul(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb)
{
TCGv t0 = tcg_temp_new();
tcg_gen_muls2_tl(dest, cpu_sr_ov, srca, srcb);
tcg_gen_sari_tl(t0, dest, TARGET_LONG_BITS - 1);
tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_ov, cpu_sr_ov, t0);
tcg_gen_neg_tl(cpu_sr_ov, cpu_sr_ov);
gen_ove_ov(dc);
}
static void gen_mulu(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb)
{
tcg_gen_muls2_tl(dest, cpu_sr_cy, srca, srcb);
tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_cy, cpu_sr_cy, 0);
gen_ove_cy(dc);
}
static void gen_div(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb)
{
TCGv t0 = tcg_temp_new();
tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_ov, srcb, 0);
/* The result of divide-by-zero is undefined.
Supress the host-side exception by dividing by 1. */
tcg_gen_or_tl(t0, srcb, cpu_sr_ov);
tcg_gen_div_tl(dest, srca, t0);
tcg_gen_neg_tl(cpu_sr_ov, cpu_sr_ov);
gen_ove_ov(dc);
}
static void gen_divu(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb)
{
TCGv t0 = tcg_temp_new();
tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_cy, srcb, 0);
/* The result of divide-by-zero is undefined.
Supress the host-side exception by dividing by 1. */
tcg_gen_or_tl(t0, srcb, cpu_sr_cy);
tcg_gen_divu_tl(dest, srca, t0);
gen_ove_cy(dc);
}
static void gen_muld(DisasContext *dc, TCGv srca, TCGv srcb)
{
TCGv_i64 t1 = tcg_temp_new_i64();
TCGv_i64 t2 = tcg_temp_new_i64();
tcg_gen_ext_tl_i64(t1, srca);
tcg_gen_ext_tl_i64(t2, srcb);
if (TARGET_LONG_BITS == 32) {
tcg_gen_mul_i64(cpu_mac, t1, t2);
tcg_gen_movi_tl(cpu_sr_ov, 0);
} else {
TCGv_i64 high = tcg_temp_new_i64();
tcg_gen_muls2_i64(cpu_mac, high, t1, t2);
tcg_gen_sari_i64(t1, cpu_mac, 63);
tcg_gen_setcond_i64(TCG_COND_NE, t1, t1, high);
tcg_gen_trunc_i64_tl(cpu_sr_ov, t1);
tcg_gen_neg_tl(cpu_sr_ov, cpu_sr_ov);
gen_ove_ov(dc);
}
}
static void gen_muldu(DisasContext *dc, TCGv srca, TCGv srcb)
{
TCGv_i64 t1 = tcg_temp_new_i64();
TCGv_i64 t2 = tcg_temp_new_i64();
tcg_gen_extu_tl_i64(t1, srca);
tcg_gen_extu_tl_i64(t2, srcb);
if (TARGET_LONG_BITS == 32) {
tcg_gen_mul_i64(cpu_mac, t1, t2);
tcg_gen_movi_tl(cpu_sr_cy, 0);
} else {
TCGv_i64 high = tcg_temp_new_i64();
tcg_gen_mulu2_i64(cpu_mac, high, t1, t2);
tcg_gen_setcondi_i64(TCG_COND_NE, high, high, 0);
tcg_gen_trunc_i64_tl(cpu_sr_cy, high);
gen_ove_cy(dc);
}
}
static void gen_mac(DisasContext *dc, TCGv srca, TCGv srcb)
{
TCGv_i64 t1 = tcg_temp_new_i64();
TCGv_i64 t2 = tcg_temp_new_i64();
tcg_gen_ext_tl_i64(t1, srca);
tcg_gen_ext_tl_i64(t2, srcb);
tcg_gen_mul_i64(t1, t1, t2);
/* Note that overflow is only computed during addition stage. */
tcg_gen_xor_i64(t2, cpu_mac, t1);
tcg_gen_add_i64(cpu_mac, cpu_mac, t1);
tcg_gen_xor_i64(t1, t1, cpu_mac);
tcg_gen_andc_i64(t1, t1, t2);
#if TARGET_LONG_BITS == 32
tcg_gen_extrh_i64_i32(cpu_sr_ov, t1);
#else
tcg_gen_mov_i64(cpu_sr_ov, t1);
#endif
gen_ove_ov(dc);
}
static void gen_macu(DisasContext *dc, TCGv srca, TCGv srcb)
{
TCGv_i64 t1 = tcg_temp_new_i64();
TCGv_i64 t2 = tcg_temp_new_i64();
tcg_gen_extu_tl_i64(t1, srca);
tcg_gen_extu_tl_i64(t2, srcb);
tcg_gen_mul_i64(t1, t1, t2);
/* Note that overflow is only computed during addition stage. */
tcg_gen_add_i64(cpu_mac, cpu_mac, t1);
tcg_gen_setcond_i64(TCG_COND_LTU, t1, cpu_mac, t1);
tcg_gen_trunc_i64_tl(cpu_sr_cy, t1);
gen_ove_cy(dc);
}
static void gen_msb(DisasContext *dc, TCGv srca, TCGv srcb)
{
TCGv_i64 t1 = tcg_temp_new_i64();
TCGv_i64 t2 = tcg_temp_new_i64();
tcg_gen_ext_tl_i64(t1, srca);
tcg_gen_ext_tl_i64(t2, srcb);
tcg_gen_mul_i64(t1, t1, t2);
/* Note that overflow is only computed during subtraction stage. */
tcg_gen_xor_i64(t2, cpu_mac, t1);
tcg_gen_sub_i64(cpu_mac, cpu_mac, t1);
tcg_gen_xor_i64(t1, t1, cpu_mac);
tcg_gen_and_i64(t1, t1, t2);
#if TARGET_LONG_BITS == 32
tcg_gen_extrh_i64_i32(cpu_sr_ov, t1);
#else
tcg_gen_mov_i64(cpu_sr_ov, t1);
#endif
gen_ove_ov(dc);
}
static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb)
{
TCGv_i64 t1 = tcg_temp_new_i64();
TCGv_i64 t2 = tcg_temp_new_i64();
tcg_gen_extu_tl_i64(t1, srca);
tcg_gen_extu_tl_i64(t2, srcb);
tcg_gen_mul_i64(t1, t1, t2);
/* Note that overflow is only computed during subtraction stage. */
tcg_gen_setcond_i64(TCG_COND_LTU, t2, cpu_mac, t1);
tcg_gen_sub_i64(cpu_mac, cpu_mac, t1);
tcg_gen_trunc_i64_tl(cpu_sr_cy, t2);
gen_ove_cy(dc);
}
static bool trans_l_add(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
gen_add(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_addc(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
gen_addc(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sub(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
gen_sub(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_and(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_and_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_or(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_or_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_xor(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_xor_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sll(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_shl_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_srl(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_shr_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sra(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_sar_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_ror(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_rotr_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_exths(DisasContext *dc, arg_da *a)
{
check_r0_write(dc, a->d);
tcg_gen_ext16s_tl(cpu_R(dc, a->d), cpu_R(dc, a->a));
return true;
}
static bool trans_l_extbs(DisasContext *dc, arg_da *a)
{
check_r0_write(dc, a->d);
tcg_gen_ext8s_tl(cpu_R(dc, a->d), cpu_R(dc, a->a));
return true;
}
static bool trans_l_exthz(DisasContext *dc, arg_da *a)
{
check_r0_write(dc, a->d);
tcg_gen_ext16u_tl(cpu_R(dc, a->d), cpu_R(dc, a->a));
return true;
}
static bool trans_l_extbz(DisasContext *dc, arg_da *a)
{
check_r0_write(dc, a->d);
tcg_gen_ext8u_tl(cpu_R(dc, a->d), cpu_R(dc, a->a));
return true;
}
static bool trans_l_cmov(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
tcg_gen_movcond_tl(TCG_COND_NE, cpu_R(dc, a->d), cpu_sr_f, dc->zero,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_ff1(DisasContext *dc, arg_da *a)
{
check_r0_write(dc, a->d);
tcg_gen_ctzi_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), -1);
tcg_gen_addi_tl(cpu_R(dc, a->d), cpu_R(dc, a->d), 1);
return true;
}
static bool trans_l_fl1(DisasContext *dc, arg_da *a)
{
check_r0_write(dc, a->d);
tcg_gen_clzi_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), TARGET_LONG_BITS);
tcg_gen_subfi_tl(cpu_R(dc, a->d), TARGET_LONG_BITS, cpu_R(dc, a->d));
return true;
}
static bool trans_l_mul(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
gen_mul(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_mulu(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
gen_mulu(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_div(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
gen_div(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_divu(DisasContext *dc, arg_dab *a)
{
check_r0_write(dc, a->d);
gen_divu(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_muld(DisasContext *dc, arg_ab *a)
{
gen_muld(dc, cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_muldu(DisasContext *dc, arg_ab *a)
{
gen_muldu(dc, cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_j(DisasContext *dc, arg_l_j *a)
{
target_ulong tmp_pc = dc->base.pc_next + a->n * 4;
tcg_gen_movi_tl(jmp_pc, tmp_pc);
dc->jmp_pc_imm = tmp_pc;
dc->delayed_branch = 2;
return true;
}
static bool trans_l_jal(DisasContext *dc, arg_l_jal *a)
{
target_ulong tmp_pc = dc->base.pc_next + a->n * 4;
target_ulong ret_pc = dc->base.pc_next + 8;
tcg_gen_movi_tl(cpu_regs[9], ret_pc);
/* Optimize jal being used to load the PC for PIC. */
if (tmp_pc != ret_pc) {
tcg_gen_movi_tl(jmp_pc, tmp_pc);
dc->jmp_pc_imm = tmp_pc;
dc->delayed_branch = 2;
}
return true;
}
static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond)
{
target_ulong tmp_pc = dc->base.pc_next + a->n * 4;
TCGv t_next = tcg_constant_tl(dc->base.pc_next + 8);
TCGv t_true = tcg_constant_tl(tmp_pc);
tcg_gen_movcond_tl(cond, jmp_pc, cpu_sr_f, dc->zero, t_true, t_next);
dc->delayed_branch = 2;
}
static bool trans_l_bf(DisasContext *dc, arg_l_bf *a)
{
do_bf(dc, a, TCG_COND_NE);
return true;
}
static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a)
{
do_bf(dc, a, TCG_COND_EQ);
return true;
}
static bool trans_l_jr(DisasContext *dc, arg_l_jr *a)
{
tcg_gen_mov_tl(jmp_pc, cpu_R(dc, a->b));
dc->delayed_branch = 2;
return true;
}
static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a)
{
tcg_gen_mov_tl(jmp_pc, cpu_R(dc, a->b));
tcg_gen_movi_tl(cpu_regs[9], dc->base.pc_next + 8);
dc->delayed_branch = 2;
return true;
}
static bool trans_l_lwa(DisasContext *dc, arg_load *a)
{
TCGv ea;
check_r0_write(dc, a->d);
ea = tcg_temp_new();
tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i);
tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, MO_TEUL);
tcg_gen_mov_tl(cpu_lock_addr, ea);
tcg_gen_mov_tl(cpu_lock_value, cpu_R(dc, a->d));
return true;
}
static void do_load(DisasContext *dc, arg_load *a, MemOp mop)
{
TCGv ea;
check_r0_write(dc, a->d);
ea = tcg_temp_new();
tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i);
tcg_gen_qemu_ld_tl(cpu_R(dc, a->d), ea, dc->mem_idx, mop);
}
static bool trans_l_lwz(DisasContext *dc, arg_load *a)
{
do_load(dc, a, MO_TEUL);
return true;
}
static bool trans_l_lws(DisasContext *dc, arg_load *a)
{
do_load(dc, a, MO_TESL);
return true;
}
static bool trans_l_lbz(DisasContext *dc, arg_load *a)
{
do_load(dc, a, MO_UB);
return true;
}
static bool trans_l_lbs(DisasContext *dc, arg_load *a)
{
do_load(dc, a, MO_SB);
return true;
}
static bool trans_l_lhz(DisasContext *dc, arg_load *a)
{
do_load(dc, a, MO_TEUW);
return true;
}
static bool trans_l_lhs(DisasContext *dc, arg_load *a)
{
do_load(dc, a, MO_TESW);
return true;
}
static bool trans_l_swa(DisasContext *dc, arg_store *a)
{
TCGv ea, val;
TCGLabel *lab_fail, *lab_done;
ea = tcg_temp_new();
tcg_gen_addi_tl(ea, cpu_R(dc, a->a), a->i);
lab_fail = gen_new_label();
lab_done = gen_new_label();
tcg_gen_brcond_tl(TCG_COND_NE, ea, cpu_lock_addr, lab_fail);
val = tcg_temp_new();
tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value,
cpu_R(dc, a->b), dc->mem_idx, MO_TEUL);
tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value);
tcg_gen_br(lab_done);
gen_set_label(lab_fail);
tcg_gen_movi_tl(cpu_sr_f, 0);
gen_set_label(lab_done);
tcg_gen_movi_tl(cpu_lock_addr, -1);
return true;
}
static void do_store(DisasContext *dc, arg_store *a, MemOp mop)
{
TCGv t0 = tcg_temp_new();
tcg_gen_addi_tl(t0, cpu_R(dc, a->a), a->i);
tcg_gen_qemu_st_tl(cpu_R(dc, a->b), t0, dc->mem_idx, mop);
}
static bool trans_l_sw(DisasContext *dc, arg_store *a)
{
do_store(dc, a, MO_TEUL);
return true;
}
static bool trans_l_sb(DisasContext *dc, arg_store *a)
{
do_store(dc, a, MO_UB);
return true;
}
static bool trans_l_sh(DisasContext *dc, arg_store *a)
{
do_store(dc, a, MO_TEUW);
return true;
}
static bool trans_l_nop(DisasContext *dc, arg_l_nop *a)
{
return true;
}
static bool trans_l_adrp(DisasContext *dc, arg_l_adrp *a)
{
if (!check_v1_3(dc)) {
return false;
}
check_r0_write(dc, a->d);
tcg_gen_movi_i32(cpu_R(dc, a->d),
(dc->base.pc_next & TARGET_PAGE_MASK) +
((target_long)a->i << TARGET_PAGE_BITS));
return true;
}
static bool trans_l_addi(DisasContext *dc, arg_rri *a)
{
check_r0_write(dc, a->d);
gen_add(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_tl(a->i));
return true;
}
static bool trans_l_addic(DisasContext *dc, arg_rri *a)
{
check_r0_write(dc, a->d);
gen_addc(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_tl(a->i));
return true;
}
static bool trans_l_muli(DisasContext *dc, arg_rri *a)
{
check_r0_write(dc, a->d);
gen_mul(dc, cpu_R(dc, a->d), cpu_R(dc, a->a), tcg_constant_tl(a->i));
return true;
}
static bool trans_l_maci(DisasContext *dc, arg_l_maci *a)
{
gen_mac(dc, cpu_R(dc, a->a), tcg_constant_tl(a->i));
return true;
}
static bool trans_l_andi(DisasContext *dc, arg_rrk *a)
{
check_r0_write(dc, a->d);
tcg_gen_andi_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), a->k);
return true;
}
static bool trans_l_ori(DisasContext *dc, arg_rrk *a)
{
check_r0_write(dc, a->d);
tcg_gen_ori_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), a->k);
return true;
}
static bool trans_l_xori(DisasContext *dc, arg_rri *a)
{
check_r0_write(dc, a->d);
tcg_gen_xori_tl(cpu_R(dc, a->d), cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a)
{
TCGv spr = tcg_temp_new();
check_r0_write(dc, a->d);
if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) {
gen_io_start();
if (dc->delayed_branch) {
tcg_gen_mov_tl(cpu_pc, jmp_pc);
tcg_gen_discard_tl(jmp_pc);
} else {
tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4);
}
dc->base.is_jmp = DISAS_EXIT;
}
tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k);
gen_helper_mfspr(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->d), spr);
return true;
}
static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a)
{
TCGv spr = tcg_temp_new();
if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) {
gen_io_start();
}
/*
* For SR, we will need to exit the TB to recognize the new
* exception state. For NPC, in theory this counts as a branch
* (although the SPR only exists for use by an ICE). Save all
* of the cpu state first, allowing it to be overwritten.
*/
if (dc->delayed_branch) {
tcg_gen_mov_tl(cpu_pc, jmp_pc);
tcg_gen_discard_tl(jmp_pc);
} else {
tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4);
}
dc->base.is_jmp = DISAS_EXIT;
tcg_gen_ori_tl(spr, cpu_R(dc, a->a), a->k);
gen_helper_mtspr(cpu_env, spr, cpu_R(dc, a->b));
return true;
}
static bool trans_l_mac(DisasContext *dc, arg_ab *a)
{
gen_mac(dc, cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_msb(DisasContext *dc, arg_ab *a)
{
gen_msb(dc, cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_macu(DisasContext *dc, arg_ab *a)
{
gen_macu(dc, cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_msbu(DisasContext *dc, arg_ab *a)
{
gen_msbu(dc, cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_slli(DisasContext *dc, arg_dal *a)
{
check_r0_write(dc, a->d);
tcg_gen_shli_tl(cpu_R(dc, a->d), cpu_R(dc, a->a),
a->l & (TARGET_LONG_BITS - 1));
return true;
}
static bool trans_l_srli(DisasContext *dc, arg_dal *a)
{
check_r0_write(dc, a->d);
tcg_gen_shri_tl(cpu_R(dc, a->d), cpu_R(dc, a->a),
a->l & (TARGET_LONG_BITS - 1));
return true;
}
static bool trans_l_srai(DisasContext *dc, arg_dal *a)
{
check_r0_write(dc, a->d);
tcg_gen_sari_tl(cpu_R(dc, a->d), cpu_R(dc, a->a),
a->l & (TARGET_LONG_BITS - 1));
return true;
}
static bool trans_l_rori(DisasContext *dc, arg_dal *a)
{
check_r0_write(dc, a->d);
tcg_gen_rotri_tl(cpu_R(dc, a->d), cpu_R(dc, a->a),
a->l & (TARGET_LONG_BITS - 1));
return true;
}
static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a)
{
check_r0_write(dc, a->d);
tcg_gen_movi_tl(cpu_R(dc, a->d), a->k << 16);
return true;
}
static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a)
{
check_r0_write(dc, a->d);
tcg_gen_trunc_i64_tl(cpu_R(dc, a->d), cpu_mac);
tcg_gen_movi_i64(cpu_mac, 0);
return true;
}
static bool trans_l_sfeq(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfne(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfltu(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfleu(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfgts(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfges(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sflts(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f,
cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfles(DisasContext *dc, arg_ab *a)
{
tcg_gen_setcond_tl(TCG_COND_LE,
cpu_sr_f, cpu_R(dc, a->a), cpu_R(dc, a->b));
return true;
}
static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfnei(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfltui(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfleui(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sflesi(DisasContext *dc, arg_ai *a)
{
tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R(dc, a->a), a->i);
return true;
}
static bool trans_l_sys(DisasContext *dc, arg_l_sys *a)
{
tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
gen_exception(dc, EXCP_SYSCALL);
dc->base.is_jmp = DISAS_NORETURN;
return true;
}
static bool trans_l_trap(DisasContext *dc, arg_l_trap *a)
{
tcg_gen_movi_tl(cpu_pc, dc->base.pc_next);
gen_exception(dc, EXCP_TRAP);
dc->base.is_jmp = DISAS_NORETURN;
return true;
}
static bool trans_l_msync(DisasContext *dc, arg_l_msync *a)
{
tcg_gen_mb(TCG_MO_ALL);
return true;
}
static bool trans_l_psync(DisasContext *dc, arg_l_psync *a)
{
return true;
}
static bool trans_l_csync(DisasContext *dc, arg_l_csync *a)
{
return true;
}
static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a)
{
if (is_user(dc)) {
gen_illegal_exception(dc);
} else {
gen_helper_rfe(cpu_env);
dc->base.is_jmp = DISAS_EXIT;
}
return true;
}
static bool do_fp2(DisasContext *dc, arg_da *a,
void (*fn)(TCGv, TCGv_env, TCGv))
{
if (!check_of32s(dc)) {
return false;
}
check_r0_write(dc, a->d);
fn(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->a));
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool do_fp3(DisasContext *dc, arg_dab *a,
void (*fn)(TCGv, TCGv_env, TCGv, TCGv))
{
if (!check_of32s(dc)) {
return false;
}
check_r0_write(dc, a->d);
fn(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->a), cpu_R(dc, a->b));
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool do_fpcmp(DisasContext *dc, arg_ab *a,
void (*fn)(TCGv, TCGv_env, TCGv, TCGv),
bool inv, bool swap)
{
if (!check_of32s(dc)) {
return false;
}
if (swap) {
fn(cpu_sr_f, cpu_env, cpu_R(dc, a->b), cpu_R(dc, a->a));
} else {
fn(cpu_sr_f, cpu_env, cpu_R(dc, a->a), cpu_R(dc, a->b));
}
if (inv) {
tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1);
}
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool trans_lf_add_s(DisasContext *dc, arg_dab *a)
{
return do_fp3(dc, a, gen_helper_float_add_s);
}
static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a)
{
return do_fp3(dc, a, gen_helper_float_sub_s);
}
static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a)
{
return do_fp3(dc, a, gen_helper_float_mul_s);
}
static bool trans_lf_div_s(DisasContext *dc, arg_dab *a)
{
return do_fp3(dc, a, gen_helper_float_div_s);
}
static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a)
{
return do_fp3(dc, a, gen_helper_float_rem_s);
return true;
}
static bool trans_lf_itof_s(DisasContext *dc, arg_da *a)
{
return do_fp2(dc, a, gen_helper_itofs);
}
static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a)
{
return do_fp2(dc, a, gen_helper_ftois);
}
static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a)
{
if (!check_of32s(dc)) {
return false;
}
check_r0_write(dc, a->d);
gen_helper_float_madd_s(cpu_R(dc, a->d), cpu_env, cpu_R(dc, a->d),
cpu_R(dc, a->a), cpu_R(dc, a->b));
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a)
{
return do_fpcmp(dc, a, gen_helper_float_eq_s, false, false);
}
static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a)
{
return do_fpcmp(dc, a, gen_helper_float_eq_s, true, false);
}
static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a)
{
return do_fpcmp(dc, a, gen_helper_float_lt_s, false, true);
}
static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a)
{
return do_fpcmp(dc, a, gen_helper_float_le_s, false, true);
}
static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a)
{
return do_fpcmp(dc, a, gen_helper_float_lt_s, false, false);
}
static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a)
{
return do_fpcmp(dc, a, gen_helper_float_le_s, false, false);
}
static bool trans_lf_sfueq_s(DisasContext *dc, arg_ab *a)
{
if (!check_v1_3(dc)) {
return false;
}
return do_fpcmp(dc, a, gen_helper_float_ueq_s, false, false);
}
static bool trans_lf_sfult_s(DisasContext *dc, arg_ab *a)
{
if (!check_v1_3(dc)) {
return false;
}
return do_fpcmp(dc, a, gen_helper_float_ult_s, false, false);
}
static bool trans_lf_sfugt_s(DisasContext *dc, arg_ab *a)
{
if (!check_v1_3(dc)) {
return false;
}
return do_fpcmp(dc, a, gen_helper_float_ult_s, false, true);
}
static bool trans_lf_sfule_s(DisasContext *dc, arg_ab *a)
{
if (!check_v1_3(dc)) {
return false;
}
return do_fpcmp(dc, a, gen_helper_float_ule_s, false, false);
}
static bool trans_lf_sfuge_s(DisasContext *dc, arg_ab *a)
{
if (!check_v1_3(dc)) {
return false;
}
return do_fpcmp(dc, a, gen_helper_float_ule_s, false, true);
}
static bool trans_lf_sfun_s(DisasContext *dc, arg_ab *a)
{
if (!check_v1_3(dc)) {
return false;
}
return do_fpcmp(dc, a, gen_helper_float_un_s, false, false);
}
static bool check_pair(DisasContext *dc, int r, int p)
{
return r + 1 + p < 32;
}
static void load_pair(DisasContext *dc, TCGv_i64 t, int r, int p)
{
tcg_gen_concat_i32_i64(t, cpu_R(dc, r + 1 + p), cpu_R(dc, r));
}
static void save_pair(DisasContext *dc, TCGv_i64 t, int r, int p)
{
tcg_gen_extr_i64_i32(cpu_R(dc, r + 1 + p), cpu_R(dc, r), t);
}
static bool do_dp3(DisasContext *dc, arg_dab_pair *a,
void (*fn)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64))
{
TCGv_i64 t0, t1;
if (!check_of64a32s(dc) ||
!check_pair(dc, a->a, a->ap) ||
!check_pair(dc, a->b, a->bp) ||
!check_pair(dc, a->d, a->dp)) {
return false;
}
check_r0_write(dc, a->d);
t0 = tcg_temp_new_i64();
t1 = tcg_temp_new_i64();
load_pair(dc, t0, a->a, a->ap);
load_pair(dc, t1, a->b, a->bp);
fn(t0, cpu_env, t0, t1);
save_pair(dc, t0, a->d, a->dp);
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool do_dp2(DisasContext *dc, arg_da_pair *a,
void (*fn)(TCGv_i64, TCGv_env, TCGv_i64))
{
TCGv_i64 t0;
if (!check_of64a32s(dc) ||
!check_pair(dc, a->a, a->ap) ||
!check_pair(dc, a->d, a->dp)) {
return false;
}
check_r0_write(dc, a->d);
t0 = tcg_temp_new_i64();
load_pair(dc, t0, a->a, a->ap);
fn(t0, cpu_env, t0);
save_pair(dc, t0, a->d, a->dp);
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool do_dpcmp(DisasContext *dc, arg_ab_pair *a,
void (*fn)(TCGv, TCGv_env, TCGv_i64, TCGv_i64),
bool inv, bool swap)
{
TCGv_i64 t0, t1;
if (!check_of64a32s(dc) ||
!check_pair(dc, a->a, a->ap) ||
!check_pair(dc, a->b, a->bp)) {
return false;
}
t0 = tcg_temp_new_i64();
t1 = tcg_temp_new_i64();
load_pair(dc, t0, a->a, a->ap);
load_pair(dc, t1, a->b, a->bp);
if (swap) {
fn(cpu_sr_f, cpu_env, t1, t0);
} else {
fn(cpu_sr_f, cpu_env, t0, t1);
}
if (inv) {
tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1);
}
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool trans_lf_add_d(DisasContext *dc, arg_dab_pair *a)
{
return do_dp3(dc, a, gen_helper_float_add_d);
}
static bool trans_lf_sub_d(DisasContext *dc, arg_dab_pair *a)
{
return do_dp3(dc, a, gen_helper_float_sub_d);
}
static bool trans_lf_mul_d(DisasContext *dc, arg_dab_pair *a)
{
return do_dp3(dc, a, gen_helper_float_mul_d);
}
static bool trans_lf_div_d(DisasContext *dc, arg_dab_pair *a)
{
return do_dp3(dc, a, gen_helper_float_div_d);
}
static bool trans_lf_rem_d(DisasContext *dc, arg_dab_pair *a)
{
return do_dp3(dc, a, gen_helper_float_rem_d);
}
static bool trans_lf_itof_d(DisasContext *dc, arg_da_pair *a)
{
return do_dp2(dc, a, gen_helper_itofd);
}
static bool trans_lf_ftoi_d(DisasContext *dc, arg_da_pair *a)
{
return do_dp2(dc, a, gen_helper_ftoid);
}
static bool trans_lf_stod_d(DisasContext *dc, arg_lf_stod_d *a)
{
TCGv_i64 t0;
if (!check_of64a32s(dc) ||
!check_pair(dc, a->d, a->dp)) {
return false;
}
check_r0_write(dc, a->d);
t0 = tcg_temp_new_i64();
gen_helper_stod(t0, cpu_env, cpu_R(dc, a->a));
save_pair(dc, t0, a->d, a->dp);
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool trans_lf_dtos_d(DisasContext *dc, arg_lf_dtos_d *a)
{
TCGv_i64 t0;
if (!check_of64a32s(dc) ||
!check_pair(dc, a->a, a->ap)) {
return false;
}
check_r0_write(dc, a->d);
t0 = tcg_temp_new_i64();
load_pair(dc, t0, a->a, a->ap);
gen_helper_dtos(cpu_R(dc, a->d), cpu_env, t0);
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool trans_lf_madd_d(DisasContext *dc, arg_dab_pair *a)
{
TCGv_i64 t0, t1, t2;
if (!check_of64a32s(dc) ||
!check_pair(dc, a->a, a->ap) ||
!check_pair(dc, a->b, a->bp) ||
!check_pair(dc, a->d, a->dp)) {
return false;
}
check_r0_write(dc, a->d);
t0 = tcg_temp_new_i64();
t1 = tcg_temp_new_i64();
t2 = tcg_temp_new_i64();
load_pair(dc, t0, a->d, a->dp);
load_pair(dc, t1, a->a, a->ap);
load_pair(dc, t2, a->b, a->bp);
gen_helper_float_madd_d(t0, cpu_env, t0, t1, t2);
save_pair(dc, t0, a->d, a->dp);
gen_helper_update_fpcsr(cpu_env);
return true;
}
static bool trans_lf_sfeq_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_eq_d, false, false);
}
static bool trans_lf_sfne_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_eq_d, true, false);
}
static bool trans_lf_sfgt_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_lt_d, false, true);
}
static bool trans_lf_sfge_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_le_d, false, true);
}
static bool trans_lf_sflt_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_lt_d, false, false);
}
static bool trans_lf_sfle_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_le_d, false, false);
}
static bool trans_lf_sfueq_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_ueq_d, false, false);
}
static bool trans_lf_sfule_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_ule_d, false, false);
}
static bool trans_lf_sfuge_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_ule_d, false, true);
}
static bool trans_lf_sfult_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_ult_d, false, false);
}
static bool trans_lf_sfugt_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_ult_d, false, true);
}
static bool trans_lf_sfun_d(DisasContext *dc, arg_ab_pair *a)
{
return do_dpcmp(dc, a, gen_helper_float_un_d, false, false);
}
static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs)
{
DisasContext *dc = container_of(dcb, DisasContext, base);
CPUOpenRISCState *env = cs->env_ptr;
int bound;
dc->mem_idx = cpu_mmu_index(env, false);
dc->tb_flags = dc->base.tb->flags;
dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0;
dc->cpucfgr = env->cpucfgr;
dc->avr = env->avr;
dc->jmp_pc_imm = -1;
bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
dc->base.max_insns = MIN(dc->base.max_insns, bound);
}
static void openrisc_tr_tb_start(DisasContextBase *db, CPUState *cs)
{
DisasContext *dc = container_of(db, DisasContext, base);
/* Allow the TCG optimizer to see that R0 == 0,
when it's true, which is the common case. */
dc->zero = tcg_constant_tl(0);
if (dc->tb_flags & TB_FLAGS_R0_0) {
dc->R0 = dc->zero;
} else {
dc->R0 = cpu_regs[0];
}
}
static void openrisc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
tcg_gen_insn_start(dc->base.pc_next, (dc->delayed_branch ? 1 : 0)
| (dc->base.num_insns > 1 ? 2 : 0));
}
static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
uint32_t insn = translator_ldl(&cpu->env, &dc->base, dc->base.pc_next);
if (!decode(dc, insn)) {
gen_illegal_exception(dc);
}
dc->base.pc_next += 4;
/* When exiting the delay slot normally, exit via jmp_pc.
* For DISAS_NORETURN, we have raised an exception and already exited.
* For DISAS_EXIT, we found l.rfe in a delay slot. There's nothing
* in the manual saying this is illegal, but it surely it should.
* At least or1ksim overrides pcnext and ignores the branch.
*/
if (dc->delayed_branch
&& --dc->delayed_branch == 0
&& dc->base.is_jmp == DISAS_NEXT) {
dc->base.is_jmp = DISAS_JUMP;
}
}
static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *dc = container_of(dcbase, DisasContext, base);
target_ulong jmp_dest;
/* If we have already exited the TB, nothing following has effect. */
if (dc->base.is_jmp == DISAS_NORETURN) {
return;
}
/* Adjust the delayed branch state for the next TB. */
if ((dc->tb_flags & TB_FLAGS_DFLAG ? 1 : 0) != (dc->delayed_branch != 0)) {
tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0);
}
/* For DISAS_TOO_MANY, jump to the next insn. */
jmp_dest = dc->base.pc_next;
tcg_gen_movi_tl(cpu_ppc, jmp_dest - 4);
switch (dc->base.is_jmp) {
case DISAS_JUMP:
jmp_dest = dc->jmp_pc_imm;
if (jmp_dest == -1) {
/* The jump destination is indirect/computed; use jmp_pc. */
tcg_gen_mov_tl(cpu_pc, jmp_pc);
tcg_gen_discard_tl(jmp_pc);
tcg_gen_lookup_and_goto_ptr();
break;
}
/* The jump destination is direct; use jmp_pc_imm.
However, we will have stored into jmp_pc as well;
we know now that it wasn't needed. */
tcg_gen_discard_tl(jmp_pc);
/* fallthru */
case DISAS_TOO_MANY:
if (translator_use_goto_tb(&dc->base, jmp_dest)) {
tcg_gen_goto_tb(0);
tcg_gen_movi_tl(cpu_pc, jmp_dest);
tcg_gen_exit_tb(dc->base.tb, 0);
break;
}
tcg_gen_movi_tl(cpu_pc, jmp_dest);
tcg_gen_lookup_and_goto_ptr();
break;
case DISAS_EXIT:
tcg_gen_exit_tb(NULL, 0);
break;
default:
g_assert_not_reached();
}
}
static void openrisc_tr_disas_log(const DisasContextBase *dcbase,
CPUState *cs, FILE *logfile)
{
DisasContext *s = container_of(dcbase, DisasContext, base);
fprintf(logfile, "IN: %s\n", lookup_symbol(s->base.pc_first));
target_disas(logfile, cs, s->base.pc_first, s->base.tb->size);
}
static const TranslatorOps openrisc_tr_ops = {
.init_disas_context = openrisc_tr_init_disas_context,
.tb_start = openrisc_tr_tb_start,
.insn_start = openrisc_tr_insn_start,
.translate_insn = openrisc_tr_translate_insn,
.tb_stop = openrisc_tr_tb_stop,
.disas_log = openrisc_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
target_ulong pc, void *host_pc)
{
DisasContext ctx;
translator_loop(cs, tb, max_insns, pc, host_pc,
&openrisc_tr_ops, &ctx.base);
}
void openrisc_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
OpenRISCCPU *cpu = OPENRISC_CPU(cs);
CPUOpenRISCState *env = &cpu->env;
int i;
qemu_fprintf(f, "PC=%08x\n", env->pc);
for (i = 0; i < 32; ++i) {
qemu_fprintf(f, "R%02d=%08x%c", i, cpu_get_gpr(env, i),
(i % 4) == 3 ? '\n' : ' ');
}
}