translate.c (7838B)
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 */ 23 TCGv cpu_gpr[32], cpu_pc; 24 static TCGv cpu_lladdr, cpu_llval; 25 TCGv_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 33 static inline int plus_1(DisasContext *ctx, int x) 34 { 35 return x + 1; 36 } 37 38 static 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 */ 47 static 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 52 void 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 59 static 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 71 static 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 95 static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) 96 { 97 } 98 99 static 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 */ 114 static 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 120 static 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 143 static 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 151 static 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 185 static 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 208 static 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 233 static 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 240 static 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 249 void 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 258 void 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 }