qemu/target/loongarch/translate.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * LoongArch emulation for QEMU - main translation routines.
   4 *
   5 * Copyright (c) 2021 Loongson Technology Corporation Limited
   6 */
   7
   8#include "qemu/osdep.h"
   9#include "cpu.h"
  10#include "tcg/tcg-op.h"
  11#include "exec/translator.h"
  12#include "exec/helper-proto.h"
  13#include "exec/helper-gen.h"
  14
  15#include "exec/translator.h"
  16#include "exec/log.h"
  17#include "qemu/qemu-print.h"
  18#include "fpu/softfloat.h"
  19#include "translate.h"
  20#include "internals.h"
  21
  22/* Global register indices */
  23TCGv cpu_gpr[32], cpu_pc;
  24static TCGv cpu_lladdr, cpu_llval;
  25TCGv_i64 cpu_fpr[32];
  26
  27#include "exec/gen-icount.h"
  28
  29#define DISAS_STOP        DISAS_TARGET_0
  30#define DISAS_EXIT        DISAS_TARGET_1
  31#define DISAS_EXIT_UPDATE DISAS_TARGET_2
  32
  33static inline int plus_1(DisasContext *ctx, int x)
  34{
  35    return x + 1;
  36}
  37
  38static inline int shl_2(DisasContext *ctx, int x)
  39{
  40    return x << 2;
  41}
  42
  43/*
  44 * LoongArch the upper 32 bits are undefined ("can be any value").
  45 * QEMU chooses to nanbox, because it is most likely to show guest bugs early.
  46 */
  47static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in)
  48{
  49    tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32));
  50}
  51
  52void generate_exception(DisasContext *ctx, int excp)
  53{
  54    tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
  55    gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp));
  56    ctx->base.is_jmp = DISAS_NORETURN;
  57}
  58
  59static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
  60{
  61    if (translator_use_goto_tb(&ctx->base, dest)) {
  62        tcg_gen_goto_tb(n);
  63        tcg_gen_movi_tl(cpu_pc, dest);
  64        tcg_gen_exit_tb(ctx->base.tb, n);
  65    } else {
  66        tcg_gen_movi_tl(cpu_pc, dest);
  67        tcg_gen_lookup_and_goto_ptr();
  68    }
  69}
  70
  71static void loongarch_tr_init_disas_context(DisasContextBase *dcbase,
  72                                            CPUState *cs)
  73{
  74    int64_t bound;
  75    DisasContext *ctx = container_of(dcbase, DisasContext, base);
  76
  77    ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK;
  78    ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK;
  79    if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) {
  80        ctx->mem_idx = ctx->plv;
  81    } else {
  82        ctx->mem_idx = MMU_IDX_DA;
  83    }
  84
  85    /* Bound the number of insns to execute to those left on the page.  */
  86    bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
  87    ctx->base.max_insns = MIN(ctx->base.max_insns, bound);
  88
  89    ctx->ntemp = 0;
  90    memset(ctx->temp, 0, sizeof(ctx->temp));
  91
  92    ctx->zero = tcg_constant_tl(0);
  93}
  94
  95static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
  96{
  97}
  98
  99static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
 100{
 101    DisasContext *ctx = container_of(dcbase, DisasContext, base);
 102
 103    tcg_gen_insn_start(ctx->base.pc_next);
 104}
 105
 106/*
 107 * Wrappers for getting reg values.
 108 *
 109 * The $zero register does not have cpu_gpr[0] allocated -- we supply the
 110 * constant zero as a source, and an uninitialized sink as destination.
 111 *
 112 * Further, we may provide an extension for word operations.
 113 */
 114static TCGv temp_new(DisasContext *ctx)
 115{
 116    assert(ctx->ntemp < ARRAY_SIZE(ctx->temp));
 117    return ctx->temp[ctx->ntemp++] = tcg_temp_new();
 118}
 119
 120static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext)
 121{
 122    TCGv t;
 123
 124    if (reg_num == 0) {
 125        return ctx->zero;
 126    }
 127
 128    switch (src_ext) {
 129    case EXT_NONE:
 130        return cpu_gpr[reg_num];
 131    case EXT_SIGN:
 132        t = temp_new(ctx);
 133        tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]);
 134        return t;
 135    case EXT_ZERO:
 136        t = temp_new(ctx);
 137        tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]);
 138        return t;
 139    }
 140    g_assert_not_reached();
 141}
 142
 143static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext)
 144{
 145    if (reg_num == 0 || dst_ext) {
 146        return temp_new(ctx);
 147    }
 148    return cpu_gpr[reg_num];
 149}
 150
 151static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext)
 152{
 153    if (reg_num != 0) {
 154        switch (dst_ext) {
 155        case EXT_NONE:
 156            tcg_gen_mov_tl(cpu_gpr[reg_num], t);
 157            break;
 158        case EXT_SIGN:
 159            tcg_gen_ext32s_tl(cpu_gpr[reg_num], t);
 160            break;
 161        case EXT_ZERO:
 162            tcg_gen_ext32u_tl(cpu_gpr[reg_num], t);
 163            break;
 164        default:
 165            g_assert_not_reached();
 166        }
 167    }
 168}
 169
 170#include "decode-insns.c.inc"
 171#include "insn_trans/trans_arith.c.inc"
 172#include "insn_trans/trans_shift.c.inc"
 173#include "insn_trans/trans_bit.c.inc"
 174#include "insn_trans/trans_memory.c.inc"
 175#include "insn_trans/trans_atomic.c.inc"
 176#include "insn_trans/trans_extra.c.inc"
 177#include "insn_trans/trans_farith.c.inc"
 178#include "insn_trans/trans_fcmp.c.inc"
 179#include "insn_trans/trans_fcnv.c.inc"
 180#include "insn_trans/trans_fmov.c.inc"
 181#include "insn_trans/trans_fmemory.c.inc"
 182#include "insn_trans/trans_branch.c.inc"
 183#include "insn_trans/trans_privileged.c.inc"
 184
 185static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
 186{
 187    CPULoongArchState *env = cs->env_ptr;
 188    DisasContext *ctx = container_of(dcbase, DisasContext, base);
 189
 190    ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
 191
 192    if (!decode(ctx, ctx->opcode)) {
 193        qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. "
 194                      TARGET_FMT_lx ": 0x%x\n",
 195                      ctx->base.pc_next, ctx->opcode);
 196        generate_exception(ctx, EXCCODE_INE);
 197    }
 198
 199    for (int i = ctx->ntemp - 1; i >= 0; --i) {
 200        tcg_temp_free(ctx->temp[i]);
 201        ctx->temp[i] = NULL;
 202    }
 203    ctx->ntemp = 0;
 204
 205    ctx->base.pc_next += 4;
 206}
 207
 208static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
 209{
 210    DisasContext *ctx = container_of(dcbase, DisasContext, base);
 211
 212    switch (ctx->base.is_jmp) {
 213    case DISAS_STOP:
 214        tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
 215        tcg_gen_lookup_and_goto_ptr();
 216        break;
 217    case DISAS_TOO_MANY:
 218        gen_goto_tb(ctx, 0, ctx->base.pc_next);
 219        break;
 220    case DISAS_NORETURN:
 221        break;
 222    case DISAS_EXIT_UPDATE:
 223        tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
 224        QEMU_FALLTHROUGH;
 225    case DISAS_EXIT:
 226        tcg_gen_exit_tb(NULL, 0);
 227        break;
 228    default:
 229        g_assert_not_reached();
 230    }
 231}
 232
 233static void loongarch_tr_disas_log(const DisasContextBase *dcbase,
 234                                   CPUState *cpu, FILE *logfile)
 235{
 236    qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first));
 237    target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size);
 238}
 239
 240static const TranslatorOps loongarch_tr_ops = {
 241    .init_disas_context = loongarch_tr_init_disas_context,
 242    .tb_start           = loongarch_tr_tb_start,
 243    .insn_start         = loongarch_tr_insn_start,
 244    .translate_insn     = loongarch_tr_translate_insn,
 245    .tb_stop            = loongarch_tr_tb_stop,
 246    .disas_log          = loongarch_tr_disas_log,
 247};
 248
 249void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns,
 250                           target_ulong pc, void *host_pc)
 251{
 252    DisasContext ctx;
 253
 254    translator_loop(cs, tb, max_insns, pc, host_pc,
 255                    &loongarch_tr_ops, &ctx.base);
 256}
 257
 258void loongarch_translate_init(void)
 259{
 260    int i;
 261
 262    cpu_gpr[0] = NULL;
 263    for (i = 1; i < 32; i++) {
 264        cpu_gpr[i] = tcg_global_mem_new(cpu_env,
 265                                        offsetof(CPULoongArchState, gpr[i]),
 266                                        regnames[i]);
 267    }
 268
 269    for (i = 0; i < 32; i++) {
 270        int off = offsetof(CPULoongArchState, fpr[i]);
 271        cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, off, fregnames[i]);
 272    }
 273
 274    cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc");
 275    cpu_lladdr = tcg_global_mem_new(cpu_env,
 276                    offsetof(CPULoongArchState, lladdr), "lladdr");
 277    cpu_llval = tcg_global_mem_new(cpu_env,
 278                    offsetof(CPULoongArchState, llval), "llval");
 279}
 280