/* * MIPS Loongson 64-bit translation routines * * Copyright (c) 2004-2005 Jocelyn Mayer * Copyright (c) 2006 Marius Groeger (FPU operations) * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) * Copyright (c) 2011 Richard Henderson * Copyright (c) 2021 Philippe Mathieu-Daudé * * This code is licensed under the GNU GPLv2 and later. */ #include "qemu/osdep.h" #include "translate.h" /* Include the auto-generated decoder. */ #include "decode-godson2.c.inc" #include "decode-loong-ext.c.inc" /* * Word or double-word Fixed-point instructions. * --------------------------------------------- * * Fixed-point multiplies and divisions write only * one result into general-purpose registers. */ static bool gen_lext_DIV_G(DisasContext *s, int rd, int rs, int rt, bool is_double) { TCGv t0, t1; TCGLabel *l1, *l2, *l3; if (rd == 0) { /* Treat as NOP. */ return true; } t0 = tcg_temp_new(); t1 = tcg_temp_new(); l1 = gen_new_label(); l2 = gen_new_label(); l3 = gen_new_label(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); if (!is_double) { tcg_gen_ext32s_tl(t0, t0); tcg_gen_ext32s_tl(t1, t1); } tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); tcg_gen_movi_tl(cpu_gpr[rd], 0); tcg_gen_br(l3); gen_set_label(l1); tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2); tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); tcg_gen_mov_tl(cpu_gpr[rd], t0); tcg_gen_br(l3); gen_set_label(l2); tcg_gen_div_tl(cpu_gpr[rd], t0, t1); if (!is_double) { tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); } gen_set_label(l3); return true; } static bool trans_DIV_G(DisasContext *s, arg_muldiv *a) { return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, false); } static bool trans_DDIV_G(DisasContext *s, arg_muldiv *a) { return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, true); } static bool gen_lext_DIVU_G(DisasContext *s, int rd, int rs, int rt, bool is_double) { TCGv t0, t1; TCGLabel *l1, *l2; if (rd == 0) { /* Treat as NOP. */ return true; } t0 = tcg_temp_new(); t1 = tcg_temp_new(); l1 = gen_new_label(); l2 = gen_new_label(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); if (!is_double) { tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); } tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); tcg_gen_movi_tl(cpu_gpr[rd], 0); tcg_gen_br(l2); gen_set_label(l1); tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); if (!is_double) { tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); } gen_set_label(l2); return true; } static bool trans_DIVU_G(DisasContext *s, arg_muldiv *a) { return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, false); } static bool trans_DDIVU_G(DisasContext *s, arg_muldiv *a) { return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, true); } static bool gen_lext_MOD_G(DisasContext *s, int rd, int rs, int rt, bool is_double) { TCGv t0, t1; TCGLabel *l1, *l2, *l3; if (rd == 0) { /* Treat as NOP. */ return true; } t0 = tcg_temp_new(); t1 = tcg_temp_new(); l1 = gen_new_label(); l2 = gen_new_label(); l3 = gen_new_label(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); if (!is_double) { tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); } tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2); tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); gen_set_label(l1); tcg_gen_movi_tl(cpu_gpr[rd], 0); tcg_gen_br(l3); gen_set_label(l2); tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); if (!is_double) { tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); } gen_set_label(l3); return true; } static bool trans_MOD_G(DisasContext *s, arg_muldiv *a) { return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, false); } static bool trans_DMOD_G(DisasContext *s, arg_muldiv *a) { return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, true); } static bool gen_lext_MODU_G(DisasContext *s, int rd, int rs, int rt, bool is_double) { TCGv t0, t1; TCGLabel *l1, *l2; if (rd == 0) { /* Treat as NOP. */ return true; } t0 = tcg_temp_new(); t1 = tcg_temp_new(); l1 = gen_new_label(); l2 = gen_new_label(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); if (!is_double) { tcg_gen_ext32u_tl(t0, t0); tcg_gen_ext32u_tl(t1, t1); } tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); tcg_gen_movi_tl(cpu_gpr[rd], 0); tcg_gen_br(l2); gen_set_label(l1); tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); if (!is_double) { tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); } gen_set_label(l2); return true; } static bool trans_MODU_G(DisasContext *s, arg_muldiv *a) { return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, false); } static bool trans_DMODU_G(DisasContext *s, arg_muldiv *a) { return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, true); } static bool gen_lext_MULT_G(DisasContext *s, int rd, int rs, int rt, bool is_double) { TCGv t0, t1; if (rd == 0) { /* Treat as NOP. */ return true; } t0 = tcg_temp_new(); t1 = tcg_temp_new(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); if (!is_double) { tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); } return true; } static bool trans_MULTu_G(DisasContext *s, arg_muldiv *a) { return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, false); } static bool trans_DMULTu_G(DisasContext *s, arg_muldiv *a) { return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, true); } bool decode_ext_loongson(DisasContext *ctx, uint32_t insn) { if (!decode_64bit_enabled(ctx)) { return false; } if ((ctx->insn_flags & INSN_LOONGSON2E) && decode_godson2(ctx, ctx->opcode)) { return true; } if ((ctx->insn_flags & ASE_LEXT) && decode_loong_ext(ctx, ctx->opcode)) { return true; } return false; }