]>
Commit | Line | Data |
---|---|---|
55c2a12c MC |
1 | /* |
2 | * RISC-V emulation for qemu: main translation routines. | |
3 | * | |
4 | * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2 or later, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along with | |
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include "qemu/osdep.h" | |
20 | #include "qemu/log.h" | |
21 | #include "cpu.h" | |
dcb32f1d | 22 | #include "tcg/tcg-op.h" |
55c2a12c MC |
23 | #include "disas/disas.h" |
24 | #include "exec/cpu_ldst.h" | |
25 | #include "exec/exec-all.h" | |
26 | #include "exec/helper-proto.h" | |
27 | #include "exec/helper-gen.h" | |
28 | ||
b2e32021 | 29 | #include "exec/translator.h" |
55c2a12c | 30 | #include "exec/log.h" |
7d7fb116 | 31 | #include "semihosting/semihost.h" |
55c2a12c MC |
32 | |
33 | #include "instmap.h" | |
75804f71 | 34 | #include "internals.h" |
55c2a12c MC |
35 | |
36 | /* global register indices */ | |
2b547084 | 37 | static TCGv cpu_gpr[32], cpu_gprh[32], cpu_pc, cpu_vl, cpu_vstart; |
55c2a12c MC |
38 | static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ |
39 | static TCGv load_res; | |
40 | static TCGv load_val; | |
0774a7a1 | 41 | /* globals for PM CSRs */ |
0cff460d LZ |
42 | static TCGv pm_mask; |
43 | static TCGv pm_base; | |
55c2a12c MC |
44 | |
45 | #include "exec/gen-icount.h" | |
46 | ||
ecda15d1 RH |
47 | /* |
48 | * If an operation is being performed on less than TARGET_LONG_BITS, | |
49 | * it may require the inputs to be sign- or zero-extended; which will | |
50 | * depend on the exact operation being performed. | |
51 | */ | |
52 | typedef enum { | |
53 | EXT_NONE, | |
54 | EXT_SIGN, | |
55 | EXT_ZERO, | |
56 | } DisasExtend; | |
57 | ||
55c2a12c | 58 | typedef struct DisasContext { |
0114db1c EC |
59 | DisasContextBase base; |
60 | /* pc_succ_insn points to the instruction following base.pc_next */ | |
61 | target_ulong pc_succ_insn; | |
d75377bf | 62 | target_ulong priv_ver; |
a2f827ff | 63 | RISCVMXL misa_mxl_max; |
e91a7227 RH |
64 | RISCVMXL xl; |
65 | uint32_t misa_ext; | |
45b4dc8b | 66 | uint32_t opcode; |
83a71719 | 67 | uint32_t mstatus_fs; |
8e1ee1fb | 68 | uint32_t mstatus_vs; |
a88f0402 | 69 | uint32_t mstatus_hs_fs; |
8e1ee1fb | 70 | uint32_t mstatus_hs_vs; |
55c2a12c | 71 | uint32_t mem_idx; |
55c2a12c MC |
72 | /* Remember the rounding mode encoded in the previous fp instruction, |
73 | which we have already installed into env->fp_status. Or -1 for | |
74 | no previous fp instruction. Note that we exit the TB when writing | |
75 | to any system register, which includes CSR_FRM, so we do not have | |
76 | to reset this known value. */ | |
77 | int frm; | |
7667cafd | 78 | RISCVMXL ol; |
fb3f3730 | 79 | bool virt_inst_excp; |
ecda15d1 | 80 | bool virt_enabled; |
3b91323e | 81 | const RISCVCPUConfig *cfg_ptr; |
743077b3 | 82 | bool hlsx; |
2b7168fc LZ |
83 | /* vector extension */ |
84 | bool vill; | |
33f1beaf FC |
85 | /* |
86 | * Encode LMUL to lmul as follows: | |
87 | * LMUL vlmul lmul | |
88 | * 1 000 0 | |
89 | * 2 001 1 | |
90 | * 4 010 2 | |
91 | * 8 011 3 | |
92 | * - 100 - | |
93 | * 1/8 101 -3 | |
94 | * 1/4 110 -2 | |
95 | * 1/2 111 -1 | |
96 | */ | |
97 | int8_t lmul; | |
2b7168fc | 98 | uint8_t sew; |
f1eed927 | 99 | uint8_t vta; |
355d5584 | 100 | uint8_t vma; |
752614ca | 101 | bool cfg_vta_all_1s; |
f714361e | 102 | target_ulong vstart; |
2b7168fc | 103 | bool vl_eq_vlmax; |
ecda15d1 | 104 | uint8_t ntemp; |
a10b9d93 | 105 | CPUState *cs; |
ecda15d1 RH |
106 | TCGv zero; |
107 | /* Space for 3 operands plus 1 extra for address computation. */ | |
108 | TCGv temp[4]; | |
e1a29bbd WL |
109 | /* Space for 4 operands(1 dest and <=3 src) for float point computation */ |
110 | TCGv_i64 ftemp[4]; | |
111 | uint8_t nftemp; | |
0774a7a1 | 112 | /* PointerMasking extension */ |
4208dc7e LZ |
113 | bool pm_mask_enabled; |
114 | bool pm_base_enabled; | |
2c9d7471 LZ |
115 | /* Use icount trigger for native debug */ |
116 | bool itrigger; | |
3ceeb19a RH |
117 | /* FRM is known to contain a valid value. */ |
118 | bool frm_valid; | |
62cf0245 AP |
119 | /* TCG of the current insn_start */ |
120 | TCGOp *insn_start; | |
55c2a12c MC |
121 | } DisasContext; |
122 | ||
db9f3fd6 MC |
123 | static inline bool has_ext(DisasContext *ctx, uint32_t ext) |
124 | { | |
e91a7227 | 125 | return ctx->misa_ext & ext; |
d36a86d0 RH |
126 | } |
127 | ||
5e199b6b PT |
128 | static bool always_true_p(DisasContext *ctx __attribute__((__unused__))) |
129 | { | |
130 | return true; | |
131 | } | |
132 | ||
49a7f3aa CM |
133 | static bool has_xthead_p(DisasContext *ctx __attribute__((__unused__))) |
134 | { | |
426c0491 | 135 | return ctx->cfg_ptr->ext_xtheadba || ctx->cfg_ptr->ext_xtheadbb || |
fa134585 | 136 | ctx->cfg_ptr->ext_xtheadbs || ctx->cfg_ptr->ext_xtheadcmo || |
d4d90115 | 137 | ctx->cfg_ptr->ext_xtheadcondmov || |
578086ba CM |
138 | ctx->cfg_ptr->ext_xtheadfmemidx || ctx->cfg_ptr->ext_xtheadfmv || |
139 | ctx->cfg_ptr->ext_xtheadmac || ctx->cfg_ptr->ext_xtheadmemidx || | |
140 | ctx->cfg_ptr->ext_xtheadmempair || ctx->cfg_ptr->ext_xtheadsync; | |
49a7f3aa CM |
141 | } |
142 | ||
0d429bd2 PT |
143 | #define MATERIALISE_EXT_PREDICATE(ext) \ |
144 | static bool has_ ## ext ## _p(DisasContext *ctx) \ | |
145 | { \ | |
146 | return ctx->cfg_ptr->ext_ ## ext ; \ | |
147 | } | |
148 | ||
149 | MATERIALISE_EXT_PREDICATE(XVentanaCondOps); | |
150 | ||
4fd7455b | 151 | #ifdef TARGET_RISCV32 |
905b9fcd | 152 | #define get_xl(ctx) MXL_RV32 |
4fd7455b | 153 | #elif defined(CONFIG_USER_ONLY) |
905b9fcd | 154 | #define get_xl(ctx) MXL_RV64 |
4fd7455b | 155 | #else |
905b9fcd RH |
156 | #define get_xl(ctx) ((ctx)->xl) |
157 | #endif | |
158 | ||
159 | /* The word size for this machine mode. */ | |
160 | static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) | |
4fd7455b | 161 | { |
905b9fcd | 162 | return 16 << get_xl(ctx); |
4fd7455b | 163 | } |
4fd7455b | 164 | |
7667cafd RH |
165 | /* The operation length, as opposed to the xlen. */ |
166 | #ifdef TARGET_RISCV32 | |
167 | #define get_ol(ctx) MXL_RV32 | |
168 | #else | |
169 | #define get_ol(ctx) ((ctx)->ol) | |
170 | #endif | |
171 | ||
172 | static inline int get_olen(DisasContext *ctx) | |
89c88309 | 173 | { |
7667cafd | 174 | return 16 << get_ol(ctx); |
89c88309 RH |
175 | } |
176 | ||
a2f827ff FP |
177 | /* The maximum register length */ |
178 | #ifdef TARGET_RISCV32 | |
179 | #define get_xl_max(ctx) MXL_RV32 | |
180 | #else | |
181 | #define get_xl_max(ctx) ((ctx)->misa_mxl_max) | |
182 | #endif | |
183 | ||
d36a86d0 RH |
184 | /* |
185 | * RISC-V requires NaN-boxing of narrower width floating point values. | |
186 | * This applies when a 32-bit value is assigned to a 64-bit FP register. | |
187 | * For consistency and simplicity, we nanbox results even when the RVD | |
188 | * extension is not present. | |
189 | */ | |
190 | static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) | |
191 | { | |
192 | tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); | |
ffe70e4d RH |
193 | } |
194 | ||
915f77b2 KC |
195 | static void gen_nanbox_h(TCGv_i64 out, TCGv_i64 in) |
196 | { | |
197 | tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(16, 48)); | |
198 | } | |
199 | ||
ffe70e4d RH |
200 | /* |
201 | * A narrow n-bit operation, where n < FLEN, checks that input operands | |
202 | * are correctly Nan-boxed, i.e., all upper FLEN - n bits are 1. | |
203 | * If so, the least-significant bits of the input are used, otherwise the | |
204 | * input value is treated as an n-bit canonical NaN (v2.2 section 9.2). | |
205 | * | |
206 | * Here, the result is always nan-boxed, even the canonical nan. | |
207 | */ | |
7b03c8e5 KC |
208 | static void gen_check_nanbox_h(TCGv_i64 out, TCGv_i64 in) |
209 | { | |
210 | TCGv_i64 t_max = tcg_const_i64(0xffffffffffff0000ull); | |
211 | TCGv_i64 t_nan = tcg_const_i64(0xffffffffffff7e00ull); | |
212 | ||
213 | tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan); | |
214 | tcg_temp_free_i64(t_max); | |
215 | tcg_temp_free_i64(t_nan); | |
216 | } | |
217 | ||
ffe70e4d RH |
218 | static void gen_check_nanbox_s(TCGv_i64 out, TCGv_i64 in) |
219 | { | |
05b80ed0 RH |
220 | TCGv_i64 t_max = tcg_constant_i64(0xffffffff00000000ull); |
221 | TCGv_i64 t_nan = tcg_constant_i64(0xffffffff7fc00000ull); | |
ffe70e4d RH |
222 | |
223 | tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan); | |
db9f3fd6 MC |
224 | } |
225 | ||
a9814e3e RH |
226 | static void decode_save_opc(DisasContext *ctx) |
227 | { | |
228 | assert(ctx->insn_start != NULL); | |
229 | tcg_set_insn_start_param(ctx->insn_start, 1, ctx->opcode); | |
230 | ctx->insn_start = NULL; | |
231 | } | |
232 | ||
40f0c204 LZ |
233 | static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) |
234 | { | |
235 | if (get_xl(ctx) == MXL_RV32) { | |
236 | dest = (int32_t)dest; | |
237 | } | |
238 | tcg_gen_movi_tl(cpu_pc, dest); | |
239 | } | |
240 | ||
241 | static void gen_set_pc(DisasContext *ctx, TCGv dest) | |
242 | { | |
243 | if (get_xl(ctx) == MXL_RV32) { | |
244 | tcg_gen_ext32s_tl(cpu_pc, dest); | |
245 | } else { | |
246 | tcg_gen_mov_tl(cpu_pc, dest); | |
247 | } | |
248 | } | |
249 | ||
55c2a12c MC |
250 | static void generate_exception(DisasContext *ctx, int excp) |
251 | { | |
40f0c204 | 252 | gen_set_pc_imm(ctx, ctx->base.pc_next); |
05b80ed0 | 253 | gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); |
0114db1c | 254 | ctx->base.is_jmp = DISAS_NORETURN; |
55c2a12c MC |
255 | } |
256 | ||
55c2a12c MC |
257 | static void gen_exception_illegal(DisasContext *ctx) |
258 | { | |
b97028b8 RH |
259 | tcg_gen_st_i32(tcg_constant_i32(ctx->opcode), cpu_env, |
260 | offsetof(CPURISCVState, bins)); | |
fb3f3730 MC |
261 | if (ctx->virt_inst_excp) { |
262 | generate_exception(ctx, RISCV_EXCP_VIRT_INSTRUCTION_FAULT); | |
263 | } else { | |
264 | generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); | |
265 | } | |
55c2a12c MC |
266 | } |
267 | ||
268 | static void gen_exception_inst_addr_mis(DisasContext *ctx) | |
269 | { | |
5dacdbae RH |
270 | tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); |
271 | generate_exception(ctx, RISCV_EXCP_INST_ADDR_MIS); | |
55c2a12c MC |
272 | } |
273 | ||
2c9d7471 LZ |
274 | static void lookup_and_goto_ptr(DisasContext *ctx) |
275 | { | |
276 | #ifndef CONFIG_USER_ONLY | |
277 | if (ctx->itrigger) { | |
278 | gen_helper_itrigger_match(cpu_env); | |
279 | } | |
280 | #endif | |
281 | tcg_gen_lookup_and_goto_ptr(); | |
282 | } | |
283 | ||
284 | static void exit_tb(DisasContext *ctx) | |
285 | { | |
286 | #ifndef CONFIG_USER_ONLY | |
287 | if (ctx->itrigger) { | |
288 | gen_helper_itrigger_match(cpu_env); | |
289 | } | |
290 | #endif | |
291 | tcg_gen_exit_tb(NULL, 0); | |
292 | } | |
293 | ||
55c2a12c MC |
294 | static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) |
295 | { | |
2c9d7471 LZ |
296 | /* |
297 | * Under itrigger, instruction executes one by one like singlestep, | |
298 | * direct block chain benefits will be small. | |
299 | */ | |
300 | if (translator_use_goto_tb(&ctx->base, dest) && !ctx->itrigger) { | |
55c2a12c | 301 | tcg_gen_goto_tb(n); |
40f0c204 | 302 | gen_set_pc_imm(ctx, dest); |
07ea28b4 | 303 | tcg_gen_exit_tb(ctx->base.tb, n); |
55c2a12c | 304 | } else { |
40f0c204 | 305 | gen_set_pc_imm(ctx, dest); |
2c9d7471 | 306 | lookup_and_goto_ptr(ctx); |
55c2a12c MC |
307 | } |
308 | } | |
309 | ||
ecda15d1 RH |
310 | /* |
311 | * Wrappers for getting reg values. | |
312 | * | |
313 | * The $zero register does not have cpu_gpr[0] allocated -- we supply the | |
314 | * constant zero as a source, and an uninitialized sink as destination. | |
315 | * | |
316 | * Further, we may provide an extension for word operations. | |
55c2a12c | 317 | */ |
ecda15d1 RH |
318 | static TCGv temp_new(DisasContext *ctx) |
319 | { | |
320 | assert(ctx->ntemp < ARRAY_SIZE(ctx->temp)); | |
321 | return ctx->temp[ctx->ntemp++] = tcg_temp_new(); | |
322 | } | |
323 | ||
324 | static TCGv get_gpr(DisasContext *ctx, int reg_num, DisasExtend ext) | |
55c2a12c | 325 | { |
ecda15d1 RH |
326 | TCGv t; |
327 | ||
55c2a12c | 328 | if (reg_num == 0) { |
ecda15d1 | 329 | return ctx->zero; |
55c2a12c | 330 | } |
ecda15d1 | 331 | |
7667cafd RH |
332 | switch (get_ol(ctx)) { |
333 | case MXL_RV32: | |
334 | switch (ext) { | |
335 | case EXT_NONE: | |
336 | break; | |
337 | case EXT_SIGN: | |
338 | t = temp_new(ctx); | |
339 | tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); | |
340 | return t; | |
341 | case EXT_ZERO: | |
342 | t = temp_new(ctx); | |
343 | tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); | |
344 | return t; | |
345 | default: | |
346 | g_assert_not_reached(); | |
347 | } | |
348 | break; | |
349 | case MXL_RV64: | |
a2f827ff | 350 | case MXL_RV128: |
7667cafd RH |
351 | break; |
352 | default: | |
353 | g_assert_not_reached(); | |
ecda15d1 | 354 | } |
7667cafd | 355 | return cpu_gpr[reg_num]; |
55c2a12c MC |
356 | } |
357 | ||
a2f827ff FP |
358 | static TCGv get_gprh(DisasContext *ctx, int reg_num) |
359 | { | |
360 | assert(get_xl(ctx) == MXL_RV128); | |
361 | if (reg_num == 0) { | |
362 | return ctx->zero; | |
363 | } | |
364 | return cpu_gprh[reg_num]; | |
365 | } | |
366 | ||
191d1daf | 367 | static TCGv dest_gpr(DisasContext *ctx, int reg_num) |
ecda15d1 | 368 | { |
7667cafd | 369 | if (reg_num == 0 || get_olen(ctx) < TARGET_LONG_BITS) { |
ecda15d1 RH |
370 | return temp_new(ctx); |
371 | } | |
372 | return cpu_gpr[reg_num]; | |
373 | } | |
374 | ||
a2f827ff FP |
375 | static TCGv dest_gprh(DisasContext *ctx, int reg_num) |
376 | { | |
377 | if (reg_num == 0) { | |
378 | return temp_new(ctx); | |
379 | } | |
380 | return cpu_gprh[reg_num]; | |
381 | } | |
382 | ||
ecda15d1 | 383 | static void gen_set_gpr(DisasContext *ctx, int reg_num, TCGv t) |
55c2a12c | 384 | { |
ecda15d1 | 385 | if (reg_num != 0) { |
7667cafd RH |
386 | switch (get_ol(ctx)) { |
387 | case MXL_RV32: | |
ecda15d1 | 388 | tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); |
7667cafd RH |
389 | break; |
390 | case MXL_RV64: | |
a2f827ff | 391 | case MXL_RV128: |
ecda15d1 | 392 | tcg_gen_mov_tl(cpu_gpr[reg_num], t); |
7667cafd RH |
393 | break; |
394 | default: | |
395 | g_assert_not_reached(); | |
ecda15d1 | 396 | } |
a2f827ff FP |
397 | |
398 | if (get_xl_max(ctx) == MXL_RV128) { | |
399 | tcg_gen_sari_tl(cpu_gprh[reg_num], cpu_gpr[reg_num], 63); | |
400 | } | |
401 | } | |
402 | } | |
403 | ||
57c108b8 FP |
404 | static void gen_set_gpri(DisasContext *ctx, int reg_num, target_long imm) |
405 | { | |
406 | if (reg_num != 0) { | |
407 | switch (get_ol(ctx)) { | |
408 | case MXL_RV32: | |
409 | tcg_gen_movi_tl(cpu_gpr[reg_num], (int32_t)imm); | |
410 | break; | |
411 | case MXL_RV64: | |
412 | case MXL_RV128: | |
413 | tcg_gen_movi_tl(cpu_gpr[reg_num], imm); | |
414 | break; | |
415 | default: | |
416 | g_assert_not_reached(); | |
417 | } | |
418 | ||
419 | if (get_xl_max(ctx) == MXL_RV128) { | |
420 | tcg_gen_movi_tl(cpu_gprh[reg_num], -(imm < 0)); | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
a2f827ff FP |
425 | static void gen_set_gpr128(DisasContext *ctx, int reg_num, TCGv rl, TCGv rh) |
426 | { | |
427 | assert(get_ol(ctx) == MXL_RV128); | |
428 | if (reg_num != 0) { | |
429 | tcg_gen_mov_tl(cpu_gpr[reg_num], rl); | |
430 | tcg_gen_mov_tl(cpu_gprh[reg_num], rh); | |
55c2a12c MC |
431 | } |
432 | } | |
433 | ||
e1a29bbd WL |
434 | static TCGv_i64 ftemp_new(DisasContext *ctx) |
435 | { | |
436 | assert(ctx->nftemp < ARRAY_SIZE(ctx->ftemp)); | |
437 | return ctx->ftemp[ctx->nftemp++] = tcg_temp_new_i64(); | |
438 | } | |
439 | ||
440 | static TCGv_i64 get_fpr_hs(DisasContext *ctx, int reg_num) | |
441 | { | |
442 | if (!ctx->cfg_ptr->ext_zfinx) { | |
443 | return cpu_fpr[reg_num]; | |
444 | } | |
445 | ||
446 | if (reg_num == 0) { | |
447 | return tcg_constant_i64(0); | |
448 | } | |
449 | switch (get_xl(ctx)) { | |
450 | case MXL_RV32: | |
451 | #ifdef TARGET_RISCV32 | |
452 | { | |
453 | TCGv_i64 t = ftemp_new(ctx); | |
454 | tcg_gen_ext_i32_i64(t, cpu_gpr[reg_num]); | |
455 | return t; | |
456 | } | |
457 | #else | |
458 | /* fall through */ | |
459 | case MXL_RV64: | |
460 | return cpu_gpr[reg_num]; | |
461 | #endif | |
462 | default: | |
463 | g_assert_not_reached(); | |
464 | } | |
465 | } | |
466 | ||
026e73fa WL |
467 | static TCGv_i64 get_fpr_d(DisasContext *ctx, int reg_num) |
468 | { | |
469 | if (!ctx->cfg_ptr->ext_zfinx) { | |
470 | return cpu_fpr[reg_num]; | |
471 | } | |
472 | ||
473 | if (reg_num == 0) { | |
474 | return tcg_constant_i64(0); | |
475 | } | |
476 | switch (get_xl(ctx)) { | |
477 | case MXL_RV32: | |
478 | { | |
479 | TCGv_i64 t = ftemp_new(ctx); | |
480 | tcg_gen_concat_tl_i64(t, cpu_gpr[reg_num], cpu_gpr[reg_num + 1]); | |
481 | return t; | |
482 | } | |
483 | #ifdef TARGET_RISCV64 | |
484 | case MXL_RV64: | |
485 | return cpu_gpr[reg_num]; | |
486 | #endif | |
487 | default: | |
488 | g_assert_not_reached(); | |
489 | } | |
490 | } | |
491 | ||
e1a29bbd WL |
492 | static TCGv_i64 dest_fpr(DisasContext *ctx, int reg_num) |
493 | { | |
494 | if (!ctx->cfg_ptr->ext_zfinx) { | |
495 | return cpu_fpr[reg_num]; | |
496 | } | |
497 | ||
498 | if (reg_num == 0) { | |
499 | return ftemp_new(ctx); | |
500 | } | |
501 | ||
502 | switch (get_xl(ctx)) { | |
503 | case MXL_RV32: | |
504 | return ftemp_new(ctx); | |
505 | #ifdef TARGET_RISCV64 | |
506 | case MXL_RV64: | |
507 | return cpu_gpr[reg_num]; | |
508 | #endif | |
509 | default: | |
510 | g_assert_not_reached(); | |
511 | } | |
512 | } | |
513 | ||
514 | /* assume t is nanboxing (for normal) or sign-extended (for zfinx) */ | |
515 | static void gen_set_fpr_hs(DisasContext *ctx, int reg_num, TCGv_i64 t) | |
516 | { | |
517 | if (!ctx->cfg_ptr->ext_zfinx) { | |
518 | tcg_gen_mov_i64(cpu_fpr[reg_num], t); | |
519 | return; | |
520 | } | |
521 | if (reg_num != 0) { | |
522 | switch (get_xl(ctx)) { | |
523 | case MXL_RV32: | |
524 | #ifdef TARGET_RISCV32 | |
525 | tcg_gen_extrl_i64_i32(cpu_gpr[reg_num], t); | |
526 | break; | |
527 | #else | |
528 | /* fall through */ | |
529 | case MXL_RV64: | |
530 | tcg_gen_mov_i64(cpu_gpr[reg_num], t); | |
531 | break; | |
532 | #endif | |
533 | default: | |
534 | g_assert_not_reached(); | |
535 | } | |
536 | } | |
537 | } | |
538 | ||
026e73fa WL |
539 | static void gen_set_fpr_d(DisasContext *ctx, int reg_num, TCGv_i64 t) |
540 | { | |
541 | if (!ctx->cfg_ptr->ext_zfinx) { | |
542 | tcg_gen_mov_i64(cpu_fpr[reg_num], t); | |
543 | return; | |
544 | } | |
545 | ||
546 | if (reg_num != 0) { | |
547 | switch (get_xl(ctx)) { | |
548 | case MXL_RV32: | |
549 | #ifdef TARGET_RISCV32 | |
550 | tcg_gen_extr_i64_i32(cpu_gpr[reg_num], cpu_gpr[reg_num + 1], t); | |
551 | break; | |
552 | #else | |
553 | tcg_gen_ext32s_i64(cpu_gpr[reg_num], t); | |
554 | tcg_gen_sari_i64(cpu_gpr[reg_num + 1], t, 32); | |
555 | break; | |
556 | case MXL_RV64: | |
557 | tcg_gen_mov_i64(cpu_gpr[reg_num], t); | |
558 | break; | |
559 | #endif | |
560 | default: | |
561 | g_assert_not_reached(); | |
562 | } | |
563 | } | |
564 | } | |
565 | ||
db9f3fd6 | 566 | static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) |
55c2a12c MC |
567 | { |
568 | target_ulong next_pc; | |
569 | ||
570 | /* check misaligned: */ | |
0114db1c | 571 | next_pc = ctx->base.pc_next + imm; |
db9f3fd6 | 572 | if (!has_ext(ctx, RVC)) { |
55c2a12c MC |
573 | if ((next_pc & 0x3) != 0) { |
574 | gen_exception_inst_addr_mis(ctx); | |
575 | return; | |
576 | } | |
577 | } | |
55c2a12c | 578 | |
a14db52f | 579 | gen_set_gpri(ctx, rd, ctx->pc_succ_insn); |
0114db1c EC |
580 | gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */ |
581 | ctx->base.is_jmp = DISAS_NORETURN; | |
55c2a12c MC |
582 | } |
583 | ||
4302bef9 LZ |
584 | /* Compute a canonical address from a register plus offset. */ |
585 | static TCGv get_address(DisasContext *ctx, int rs1, int imm) | |
c655df7f | 586 | { |
4302bef9 LZ |
587 | TCGv addr = temp_new(ctx); |
588 | TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); | |
589 | ||
590 | tcg_gen_addi_tl(addr, src1, imm); | |
4208dc7e | 591 | if (ctx->pm_mask_enabled) { |
dec19f68 | 592 | tcg_gen_andc_tl(addr, addr, pm_mask); |
4302bef9 LZ |
593 | } else if (get_xl(ctx) == MXL_RV32) { |
594 | tcg_gen_ext32u_tl(addr, addr); | |
0774a7a1 | 595 | } |
4208dc7e LZ |
596 | if (ctx->pm_base_enabled) { |
597 | tcg_gen_or_tl(addr, addr, pm_base); | |
598 | } | |
4302bef9 | 599 | return addr; |
c655df7f AB |
600 | } |
601 | ||
45f9df86 CM |
602 | /* Compute a canonical address from a register plus reg offset. */ |
603 | static TCGv get_address_indexed(DisasContext *ctx, int rs1, TCGv offs) | |
604 | { | |
605 | TCGv addr = temp_new(ctx); | |
606 | TCGv src1 = get_gpr(ctx, rs1, EXT_NONE); | |
607 | ||
608 | tcg_gen_add_tl(addr, src1, offs); | |
609 | if (ctx->pm_mask_enabled) { | |
610 | tcg_gen_andc_tl(addr, addr, pm_mask); | |
611 | } else if (get_xl(ctx) == MXL_RV32) { | |
612 | tcg_gen_ext32u_tl(addr, addr); | |
613 | } | |
614 | if (ctx->pm_base_enabled) { | |
615 | tcg_gen_or_tl(addr, addr, pm_base); | |
616 | } | |
617 | return addr; | |
618 | } | |
619 | ||
533b8f88 RH |
620 | #ifndef CONFIG_USER_ONLY |
621 | /* The states of mstatus_fs are: | |
622 | * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty | |
623 | * We will have already diagnosed disabled state, | |
624 | * and need to turn initial/clean into dirty. | |
625 | */ | |
626 | static void mark_fs_dirty(DisasContext *ctx) | |
627 | { | |
628 | TCGv tmp; | |
4fd7455b | 629 | |
c163b3ba WL |
630 | if (!has_ext(ctx, RVF)) { |
631 | return; | |
632 | } | |
633 | ||
a88f0402 FC |
634 | if (ctx->mstatus_fs != MSTATUS_FS) { |
635 | /* Remember the state change for the rest of the TB. */ | |
636 | ctx->mstatus_fs = MSTATUS_FS; | |
533b8f88 | 637 | |
a88f0402 FC |
638 | tmp = tcg_temp_new(); |
639 | tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); | |
b550f894 | 640 | tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); |
a88f0402 FC |
641 | tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); |
642 | tcg_temp_free(tmp); | |
643 | } | |
4fd7455b | 644 | |
a88f0402 FC |
645 | if (ctx->virt_enabled && ctx->mstatus_hs_fs != MSTATUS_FS) { |
646 | /* Remember the stage change for the rest of the TB. */ | |
647 | ctx->mstatus_hs_fs = MSTATUS_FS; | |
45b4dc8b | 648 | |
a88f0402 | 649 | tmp = tcg_temp_new(); |
45b4dc8b | 650 | tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); |
b550f894 | 651 | tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); |
45b4dc8b | 652 | tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); |
a88f0402 | 653 | tcg_temp_free(tmp); |
45b4dc8b | 654 | } |
533b8f88 RH |
655 | } |
656 | #else | |
657 | static inline void mark_fs_dirty(DisasContext *ctx) { } | |
658 | #endif | |
659 | ||
8e1ee1fb FC |
660 | #ifndef CONFIG_USER_ONLY |
661 | /* The states of mstatus_vs are: | |
662 | * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty | |
663 | * We will have already diagnosed disabled state, | |
664 | * and need to turn initial/clean into dirty. | |
665 | */ | |
666 | static void mark_vs_dirty(DisasContext *ctx) | |
667 | { | |
668 | TCGv tmp; | |
669 | ||
670 | if (ctx->mstatus_vs != MSTATUS_VS) { | |
671 | /* Remember the state change for the rest of the TB. */ | |
672 | ctx->mstatus_vs = MSTATUS_VS; | |
673 | ||
674 | tmp = tcg_temp_new(); | |
675 | tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); | |
676 | tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); | |
677 | tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); | |
678 | tcg_temp_free(tmp); | |
679 | } | |
680 | ||
681 | if (ctx->virt_enabled && ctx->mstatus_hs_vs != MSTATUS_VS) { | |
682 | /* Remember the stage change for the rest of the TB. */ | |
683 | ctx->mstatus_hs_vs = MSTATUS_VS; | |
684 | ||
685 | tmp = tcg_temp_new(); | |
686 | tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); | |
687 | tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); | |
688 | tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus_hs)); | |
689 | tcg_temp_free(tmp); | |
690 | } | |
691 | } | |
692 | #else | |
693 | static inline void mark_vs_dirty(DisasContext *ctx) { } | |
694 | #endif | |
695 | ||
55c2a12c MC |
696 | static void gen_set_rm(DisasContext *ctx, int rm) |
697 | { | |
55c2a12c MC |
698 | if (ctx->frm == rm) { |
699 | return; | |
700 | } | |
701 | ctx->frm = rm; | |
75804f71 | 702 | |
3ceeb19a RH |
703 | if (rm == RISCV_FRM_DYN) { |
704 | /* The helper will return only if frm valid. */ | |
705 | ctx->frm_valid = true; | |
706 | } | |
75804f71 | 707 | |
a9814e3e RH |
708 | /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ |
709 | decode_save_opc(ctx); | |
05b80ed0 | 710 | gen_helper_set_rounding_mode(cpu_env, tcg_constant_i32(rm)); |
55c2a12c MC |
711 | } |
712 | ||
3ceeb19a RH |
713 | static void gen_set_rm_chkfrm(DisasContext *ctx, int rm) |
714 | { | |
715 | if (ctx->frm == rm && ctx->frm_valid) { | |
716 | return; | |
717 | } | |
718 | ctx->frm = rm; | |
719 | ctx->frm_valid = true; | |
720 | ||
721 | /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ | |
722 | decode_save_opc(ctx); | |
723 | gen_helper_set_rounding_mode_chkfrm(cpu_env, tcg_constant_i32(rm)); | |
724 | } | |
725 | ||
751538d5 LZ |
726 | static int ex_plus_1(DisasContext *ctx, int nf) |
727 | { | |
728 | return nf + 1; | |
729 | } | |
730 | ||
2a53cff4 | 731 | #define EX_SH(amount) \ |
451e4ffd | 732 | static int ex_shift_##amount(DisasContext *ctx, int imm) \ |
2a53cff4 BK |
733 | { \ |
734 | return imm << amount; \ | |
735 | } | |
3cca75a6 | 736 | EX_SH(1) |
e98d9140 BK |
737 | EX_SH(2) |
738 | EX_SH(3) | |
07b001c6 | 739 | EX_SH(4) |
2a53cff4 BK |
740 | EX_SH(12) |
741 | ||
d2e2c1e4 BK |
742 | #define REQUIRE_EXT(ctx, ext) do { \ |
743 | if (!has_ext(ctx, ext)) { \ | |
744 | return false; \ | |
745 | } \ | |
746 | } while (0) | |
7e68e6c7 | 747 | |
905b9fcd RH |
748 | #define REQUIRE_32BIT(ctx) do { \ |
749 | if (get_xl(ctx) != MXL_RV32) { \ | |
750 | return false; \ | |
751 | } \ | |
7e68e6c7 | 752 | } while (0) |
d2e2c1e4 | 753 | |
344b4a82 FP |
754 | #define REQUIRE_64BIT(ctx) do { \ |
755 | if (get_xl(ctx) != MXL_RV64) { \ | |
756 | return false; \ | |
757 | } \ | |
758 | } while (0) | |
759 | ||
760 | #define REQUIRE_128BIT(ctx) do { \ | |
761 | if (get_xl(ctx) != MXL_RV128) { \ | |
762 | return false; \ | |
763 | } \ | |
764 | } while (0) | |
765 | ||
766 | #define REQUIRE_64_OR_128BIT(ctx) do { \ | |
767 | if (get_xl(ctx) == MXL_RV32) { \ | |
768 | return false; \ | |
769 | } \ | |
daf866b6 AF |
770 | } while (0) |
771 | ||
d8e81e3c WL |
772 | #define REQUIRE_EITHER_EXT(ctx, A, B) do { \ |
773 | if (!ctx->cfg_ptr->ext_##A && \ | |
774 | !ctx->cfg_ptr->ext_##B) { \ | |
775 | return false; \ | |
776 | } \ | |
777 | } while (0) | |
778 | ||
451e4ffd | 779 | static int ex_rvc_register(DisasContext *ctx, int reg) |
e98d9140 BK |
780 | { |
781 | return 8 + reg; | |
782 | } | |
783 | ||
33632775 | 784 | static int ex_rvc_shiftli(DisasContext *ctx, int imm) |
6cafec92 RH |
785 | { |
786 | /* For RV128 a shamt of 0 means a shift by 64. */ | |
33632775 FP |
787 | if (get_ol(ctx) == MXL_RV128) { |
788 | imm = imm ? imm : 64; | |
789 | } | |
790 | return imm; | |
791 | } | |
792 | ||
793 | static int ex_rvc_shiftri(DisasContext *ctx, int imm) | |
794 | { | |
795 | /* | |
796 | * For RV128 a shamt of 0 means a shift by 64, furthermore, for right | |
797 | * shifts, the shamt is sign-extended. | |
798 | */ | |
799 | if (get_ol(ctx) == MXL_RV128) { | |
800 | imm = imm | (imm & 32) << 1; | |
801 | imm = imm ? imm : 64; | |
802 | } | |
803 | return imm; | |
6cafec92 RH |
804 | } |
805 | ||
2a53cff4 | 806 | /* Include the auto-generated decoder for 32 bit insn */ |
abff1abf | 807 | #include "decode-insn32.c.inc" |
7a50d3e2 | 808 | |
a1a3aac4 FP |
809 | static bool gen_logic_imm_fn(DisasContext *ctx, arg_i *a, |
810 | void (*func)(TCGv, TCGv, target_long)) | |
811 | { | |
812 | TCGv dest = dest_gpr(ctx, a->rd); | |
813 | TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); | |
814 | ||
815 | func(dest, src1, a->imm); | |
816 | ||
568f247f FP |
817 | if (get_xl(ctx) == MXL_RV128) { |
818 | TCGv src1h = get_gprh(ctx, a->rs1); | |
819 | TCGv desth = dest_gprh(ctx, a->rd); | |
820 | ||
821 | func(desth, src1h, -(a->imm < 0)); | |
822 | gen_set_gpr128(ctx, a->rd, dest, desth); | |
823 | } else { | |
824 | gen_set_gpr(ctx, a->rd, dest); | |
825 | } | |
a1a3aac4 FP |
826 | |
827 | return true; | |
828 | } | |
829 | ||
830 | static bool gen_logic(DisasContext *ctx, arg_r *a, | |
831 | void (*func)(TCGv, TCGv, TCGv)) | |
832 | { | |
833 | TCGv dest = dest_gpr(ctx, a->rd); | |
834 | TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); | |
835 | TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); | |
836 | ||
837 | func(dest, src1, src2); | |
838 | ||
568f247f FP |
839 | if (get_xl(ctx) == MXL_RV128) { |
840 | TCGv src1h = get_gprh(ctx, a->rs1); | |
841 | TCGv src2h = get_gprh(ctx, a->rs2); | |
842 | TCGv desth = dest_gprh(ctx, a->rd); | |
843 | ||
844 | func(desth, src1h, src2h); | |
845 | gen_set_gpr128(ctx, a->rd, dest, desth); | |
846 | } else { | |
847 | gen_set_gpr(ctx, a->rd, dest); | |
848 | } | |
a1a3aac4 FP |
849 | |
850 | return true; | |
851 | } | |
852 | ||
191d1daf | 853 | static bool gen_arith_imm_fn(DisasContext *ctx, arg_i *a, DisasExtend ext, |
7fd40f86 FP |
854 | void (*func)(TCGv, TCGv, target_long), |
855 | void (*f128)(TCGv, TCGv, TCGv, TCGv, target_long)) | |
598aa116 | 856 | { |
191d1daf RH |
857 | TCGv dest = dest_gpr(ctx, a->rd); |
858 | TCGv src1 = get_gpr(ctx, a->rs1, ext); | |
598aa116 | 859 | |
7fd40f86 FP |
860 | if (get_ol(ctx) < MXL_RV128) { |
861 | func(dest, src1, a->imm); | |
862 | gen_set_gpr(ctx, a->rd, dest); | |
863 | } else { | |
864 | if (f128 == NULL) { | |
865 | return false; | |
866 | } | |
867 | ||
868 | TCGv src1h = get_gprh(ctx, a->rs1); | |
869 | TCGv desth = dest_gprh(ctx, a->rd); | |
598aa116 | 870 | |
7fd40f86 FP |
871 | f128(dest, desth, src1, src1h, a->imm); |
872 | gen_set_gpr128(ctx, a->rd, dest, desth); | |
873 | } | |
598aa116 RH |
874 | return true; |
875 | } | |
876 | ||
191d1daf | 877 | static bool gen_arith_imm_tl(DisasContext *ctx, arg_i *a, DisasExtend ext, |
7fd40f86 FP |
878 | void (*func)(TCGv, TCGv, TCGv), |
879 | void (*f128)(TCGv, TCGv, TCGv, TCGv, TCGv, TCGv)) | |
7a50d3e2 | 880 | { |
191d1daf RH |
881 | TCGv dest = dest_gpr(ctx, a->rd); |
882 | TCGv src1 = get_gpr(ctx, a->rs1, ext); | |
883 | TCGv src2 = tcg_constant_tl(a->imm); | |
7a50d3e2 | 884 | |
7fd40f86 FP |
885 | if (get_ol(ctx) < MXL_RV128) { |
886 | func(dest, src1, src2); | |
887 | gen_set_gpr(ctx, a->rd, dest); | |
888 | } else { | |
889 | if (f128 == NULL) { | |
890 | return false; | |
891 | } | |
7a50d3e2 | 892 | |
7fd40f86 FP |
893 | TCGv src1h = get_gprh(ctx, a->rs1); |
894 | TCGv src2h = tcg_constant_tl(-(a->imm < 0)); | |
895 | TCGv desth = dest_gprh(ctx, a->rd); | |
896 | ||
897 | f128(dest, desth, src1, src1h, src2, src2h); | |
898 | gen_set_gpr128(ctx, a->rd, dest, desth); | |
899 | } | |
7a50d3e2 BK |
900 | return true; |
901 | } | |
902 | ||
191d1daf | 903 | static bool gen_arith(DisasContext *ctx, arg_r *a, DisasExtend ext, |
7fd40f86 FP |
904 | void (*func)(TCGv, TCGv, TCGv), |
905 | void (*f128)(TCGv, TCGv, TCGv, TCGv, TCGv, TCGv)) | |
f2ab1728 | 906 | { |
191d1daf RH |
907 | TCGv dest = dest_gpr(ctx, a->rd); |
908 | TCGv src1 = get_gpr(ctx, a->rs1, ext); | |
909 | TCGv src2 = get_gpr(ctx, a->rs2, ext); | |
f2ab1728 | 910 | |
7fd40f86 FP |
911 | if (get_ol(ctx) < MXL_RV128) { |
912 | func(dest, src1, src2); | |
913 | gen_set_gpr(ctx, a->rd, dest); | |
914 | } else { | |
915 | if (f128 == NULL) { | |
916 | return false; | |
917 | } | |
918 | ||
919 | TCGv src1h = get_gprh(ctx, a->rs1); | |
920 | TCGv src2h = get_gprh(ctx, a->rs2); | |
921 | TCGv desth = dest_gprh(ctx, a->rd); | |
f2ab1728 | 922 | |
7fd40f86 FP |
923 | f128(dest, desth, src1, src1h, src2, src2h); |
924 | gen_set_gpr128(ctx, a->rd, dest, desth); | |
925 | } | |
f2ab1728 BK |
926 | return true; |
927 | } | |
928 | ||
80347ae9 RH |
929 | static bool gen_arith_per_ol(DisasContext *ctx, arg_r *a, DisasExtend ext, |
930 | void (*f_tl)(TCGv, TCGv, TCGv), | |
7fd40f86 FP |
931 | void (*f_32)(TCGv, TCGv, TCGv), |
932 | void (*f_128)(TCGv, TCGv, TCGv, TCGv, TCGv, TCGv)) | |
80347ae9 RH |
933 | { |
934 | int olen = get_olen(ctx); | |
935 | ||
936 | if (olen != TARGET_LONG_BITS) { | |
937 | if (olen == 32) { | |
938 | f_tl = f_32; | |
7fd40f86 | 939 | } else if (olen != 128) { |
80347ae9 RH |
940 | g_assert_not_reached(); |
941 | } | |
942 | } | |
7fd40f86 | 943 | return gen_arith(ctx, a, ext, f_tl, f_128); |
80347ae9 RH |
944 | } |
945 | ||
89c88309 | 946 | static bool gen_shift_imm_fn(DisasContext *ctx, arg_shift *a, DisasExtend ext, |
6bf4bbed FP |
947 | void (*func)(TCGv, TCGv, target_long), |
948 | void (*f128)(TCGv, TCGv, TCGv, TCGv, target_long)) | |
a10b9d93 | 949 | { |
89c88309 | 950 | TCGv dest, src1; |
7667cafd | 951 | int max_len = get_olen(ctx); |
a10b9d93 | 952 | |
89c88309 | 953 | if (a->shamt >= max_len) { |
981d3568 FC |
954 | return false; |
955 | } | |
956 | ||
89c88309 RH |
957 | dest = dest_gpr(ctx, a->rd); |
958 | src1 = get_gpr(ctx, a->rs1, ext); | |
981d3568 | 959 | |
6bf4bbed FP |
960 | if (max_len < 128) { |
961 | func(dest, src1, a->shamt); | |
962 | gen_set_gpr(ctx, a->rd, dest); | |
963 | } else { | |
964 | TCGv src1h = get_gprh(ctx, a->rs1); | |
965 | TCGv desth = dest_gprh(ctx, a->rd); | |
981d3568 | 966 | |
6bf4bbed FP |
967 | if (f128 == NULL) { |
968 | return false; | |
969 | } | |
970 | f128(dest, desth, src1, src1h, a->shamt); | |
971 | gen_set_gpr128(ctx, a->rd, dest, desth); | |
972 | } | |
981d3568 FC |
973 | return true; |
974 | } | |
975 | ||
a0245d91 RH |
976 | static bool gen_shift_imm_fn_per_ol(DisasContext *ctx, arg_shift *a, |
977 | DisasExtend ext, | |
978 | void (*f_tl)(TCGv, TCGv, target_long), | |
6bf4bbed FP |
979 | void (*f_32)(TCGv, TCGv, target_long), |
980 | void (*f_128)(TCGv, TCGv, TCGv, TCGv, | |
981 | target_long)) | |
a0245d91 RH |
982 | { |
983 | int olen = get_olen(ctx); | |
984 | if (olen != TARGET_LONG_BITS) { | |
985 | if (olen == 32) { | |
986 | f_tl = f_32; | |
6bf4bbed | 987 | } else if (olen != 128) { |
a0245d91 RH |
988 | g_assert_not_reached(); |
989 | } | |
990 | } | |
6bf4bbed | 991 | return gen_shift_imm_fn(ctx, a, ext, f_tl, f_128); |
a0245d91 RH |
992 | } |
993 | ||
89c88309 RH |
994 | static bool gen_shift_imm_tl(DisasContext *ctx, arg_shift *a, DisasExtend ext, |
995 | void (*func)(TCGv, TCGv, TCGv)) | |
23cd1777 | 996 | { |
89c88309 | 997 | TCGv dest, src1, src2; |
7667cafd | 998 | int max_len = get_olen(ctx); |
89c88309 RH |
999 | |
1000 | if (a->shamt >= max_len) { | |
1001 | return false; | |
1002 | } | |
23cd1777 | 1003 | |
89c88309 RH |
1004 | dest = dest_gpr(ctx, a->rd); |
1005 | src1 = get_gpr(ctx, a->rs1, ext); | |
1006 | src2 = tcg_constant_tl(a->shamt); | |
23cd1777 | 1007 | |
89c88309 | 1008 | func(dest, src1, src2); |
23cd1777 | 1009 | |
89c88309 | 1010 | gen_set_gpr(ctx, a->rd, dest); |
23cd1777 FC |
1011 | return true; |
1012 | } | |
1013 | ||
89c88309 | 1014 | static bool gen_shift(DisasContext *ctx, arg_r *a, DisasExtend ext, |
6bf4bbed FP |
1015 | void (*func)(TCGv, TCGv, TCGv), |
1016 | void (*f128)(TCGv, TCGv, TCGv, TCGv, TCGv)) | |
981d3568 | 1017 | { |
89c88309 RH |
1018 | TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); |
1019 | TCGv ext2 = tcg_temp_new(); | |
6bf4bbed | 1020 | int max_len = get_olen(ctx); |
981d3568 | 1021 | |
6bf4bbed | 1022 | tcg_gen_andi_tl(ext2, src2, max_len - 1); |
981d3568 | 1023 | |
6bf4bbed FP |
1024 | TCGv dest = dest_gpr(ctx, a->rd); |
1025 | TCGv src1 = get_gpr(ctx, a->rs1, ext); | |
1026 | ||
1027 | if (max_len < 128) { | |
1028 | func(dest, src1, ext2); | |
1029 | gen_set_gpr(ctx, a->rd, dest); | |
1030 | } else { | |
1031 | TCGv src1h = get_gprh(ctx, a->rs1); | |
1032 | TCGv desth = dest_gprh(ctx, a->rd); | |
1033 | ||
1034 | if (f128 == NULL) { | |
1035 | return false; | |
1036 | } | |
1037 | f128(dest, desth, src1, src1h, ext2); | |
1038 | gen_set_gpr128(ctx, a->rd, dest, desth); | |
1039 | } | |
89c88309 | 1040 | tcg_temp_free(ext2); |
981d3568 FC |
1041 | return true; |
1042 | } | |
1043 | ||
a0245d91 RH |
1044 | static bool gen_shift_per_ol(DisasContext *ctx, arg_r *a, DisasExtend ext, |
1045 | void (*f_tl)(TCGv, TCGv, TCGv), | |
6bf4bbed FP |
1046 | void (*f_32)(TCGv, TCGv, TCGv), |
1047 | void (*f_128)(TCGv, TCGv, TCGv, TCGv, TCGv)) | |
a0245d91 RH |
1048 | { |
1049 | int olen = get_olen(ctx); | |
1050 | if (olen != TARGET_LONG_BITS) { | |
1051 | if (olen == 32) { | |
1052 | f_tl = f_32; | |
6bf4bbed | 1053 | } else if (olen != 128) { |
a0245d91 RH |
1054 | g_assert_not_reached(); |
1055 | } | |
1056 | } | |
6bf4bbed | 1057 | return gen_shift(ctx, a, ext, f_tl, f_128); |
a0245d91 RH |
1058 | } |
1059 | ||
60903915 RH |
1060 | static bool gen_unary(DisasContext *ctx, arg_r2 *a, DisasExtend ext, |
1061 | void (*func)(TCGv, TCGv)) | |
43824018 | 1062 | { |
60903915 RH |
1063 | TCGv dest = dest_gpr(ctx, a->rd); |
1064 | TCGv src1 = get_gpr(ctx, a->rs1, ext); | |
43824018 | 1065 | |
60903915 | 1066 | func(dest, src1); |
43824018 | 1067 | |
60903915 | 1068 | gen_set_gpr(ctx, a->rd, dest); |
43824018 KC |
1069 | return true; |
1070 | } | |
1071 | ||
fdab665f RH |
1072 | static bool gen_unary_per_ol(DisasContext *ctx, arg_r2 *a, DisasExtend ext, |
1073 | void (*f_tl)(TCGv, TCGv), | |
1074 | void (*f_32)(TCGv, TCGv)) | |
1075 | { | |
1076 | int olen = get_olen(ctx); | |
1077 | ||
1078 | if (olen != TARGET_LONG_BITS) { | |
1079 | if (olen == 32) { | |
1080 | f_tl = f_32; | |
1081 | } else { | |
1082 | g_assert_not_reached(); | |
1083 | } | |
1084 | } | |
1085 | return gen_unary(ctx, a, ext, f_tl); | |
1086 | } | |
1087 | ||
89c88309 RH |
1088 | static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) |
1089 | { | |
1090 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
1091 | CPUState *cpu = ctx->cs; | |
1092 | CPURISCVState *env = cpu->env_ptr; | |
1093 | ||
1094 | return cpu_ldl_code(env, pc); | |
1095 | } | |
1096 | ||
2a53cff4 | 1097 | /* Include insn module translation function */ |
139c1837 PB |
1098 | #include "insn_trans/trans_rvi.c.inc" |
1099 | #include "insn_trans/trans_rvm.c.inc" | |
1100 | #include "insn_trans/trans_rva.c.inc" | |
1101 | #include "insn_trans/trans_rvf.c.inc" | |
1102 | #include "insn_trans/trans_rvd.c.inc" | |
1103 | #include "insn_trans/trans_rvh.c.inc" | |
1104 | #include "insn_trans/trans_rvv.c.inc" | |
43824018 | 1105 | #include "insn_trans/trans_rvb.c.inc" |
260b594d | 1106 | #include "insn_trans/trans_rvzawrs.c.inc" |
915f77b2 | 1107 | #include "insn_trans/trans_rvzfh.c.inc" |
68d19b58 | 1108 | #include "insn_trans/trans_rvk.c.inc" |
139c1837 | 1109 | #include "insn_trans/trans_privileged.c.inc" |
c5d77ddd | 1110 | #include "insn_trans/trans_svinval.c.inc" |
49a7f3aa CM |
1111 | #include "decode-xthead.c.inc" |
1112 | #include "insn_trans/trans_xthead.c.inc" | |
0d429bd2 | 1113 | #include "insn_trans/trans_xventanacondops.c.inc" |
2a53cff4 | 1114 | |
59a3a1c0 | 1115 | /* Include the auto-generated decoder for 16 bit insn */ |
abff1abf | 1116 | #include "decode-insn16.c.inc" |
0d429bd2 PT |
1117 | /* Include decoders for factored-out extensions */ |
1118 | #include "decode-XVentanaCondOps.c.inc" | |
e98d9140 | 1119 | |
ef6e987b RH |
1120 | /* The specification allows for longer insns, but not supported by qemu. */ |
1121 | #define MAX_INSN_LEN 4 | |
1122 | ||
1123 | static inline int insn_len(uint16_t first_word) | |
1124 | { | |
1125 | return (first_word & 3) == 3 ? 4 : 2; | |
1126 | } | |
1127 | ||
25139bf7 | 1128 | static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) |
55c2a12c | 1129 | { |
5e199b6b PT |
1130 | /* |
1131 | * A table with predicate (i.e., guard) functions and decoder functions | |
1132 | * that are tested in-order until a decoder matches onto the opcode. | |
1133 | */ | |
1134 | static const struct { | |
1135 | bool (*guard_func)(DisasContext *); | |
1136 | bool (*decode_func)(DisasContext *, uint32_t); | |
1137 | } decoders[] = { | |
1138 | { always_true_p, decode_insn32 }, | |
49a7f3aa | 1139 | { has_xthead_p, decode_xthead }, |
0d429bd2 | 1140 | { has_XVentanaCondOps_p, decode_XVentanaCodeOps }, |
5e199b6b PT |
1141 | }; |
1142 | ||
fb3f3730 | 1143 | ctx->virt_inst_excp = false; |
5e199b6b | 1144 | /* Check for compressed insn */ |
ef6e987b | 1145 | if (insn_len(opcode) == 2) { |
ec2918b4 RH |
1146 | ctx->opcode = opcode; |
1147 | ctx->pc_succ_insn = ctx->base.pc_next + 2; | |
1148 | if (has_ext(ctx, RVC) && decode_insn16(ctx, opcode)) { | |
1149 | return; | |
55c2a12c MC |
1150 | } |
1151 | } else { | |
25139bf7 AB |
1152 | uint32_t opcode32 = opcode; |
1153 | opcode32 = deposit32(opcode32, 16, 16, | |
4e116893 IL |
1154 | translator_lduw(env, &ctx->base, |
1155 | ctx->base.pc_next + 2)); | |
ea7b5d5a | 1156 | ctx->opcode = opcode32; |
0114db1c | 1157 | ctx->pc_succ_insn = ctx->base.pc_next + 4; |
5e199b6b PT |
1158 | |
1159 | for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { | |
1160 | if (decoders[i].guard_func(ctx) && | |
1161 | decoders[i].decode_func(ctx, opcode32)) { | |
1162 | return; | |
1163 | } | |
2a53cff4 | 1164 | } |
55c2a12c | 1165 | } |
5e199b6b PT |
1166 | |
1167 | gen_exception_illegal(ctx); | |
55c2a12c MC |
1168 | } |
1169 | ||
5b4f1d2d | 1170 | static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) |
55c2a12c | 1171 | { |
5b4f1d2d | 1172 | DisasContext *ctx = container_of(dcbase, DisasContext, base); |
d75377bf | 1173 | CPURISCVState *env = cs->env_ptr; |
50fba816 | 1174 | RISCVCPU *cpu = RISCV_CPU(cs); |
2b7168fc | 1175 | uint32_t tb_flags = ctx->base.tb->flags; |
55c2a12c | 1176 | |
5b4f1d2d | 1177 | ctx->pc_succ_insn = ctx->base.pc_first; |
61d56494 | 1178 | ctx->mem_idx = FIELD_EX32(tb_flags, TB_FLAGS, MEM_IDX); |
2b7168fc | 1179 | ctx->mstatus_fs = tb_flags & TB_FLAGS_MSTATUS_FS; |
8e1ee1fb | 1180 | ctx->mstatus_vs = tb_flags & TB_FLAGS_MSTATUS_VS; |
d75377bf | 1181 | ctx->priv_ver = env->priv_ver; |
45b4dc8b | 1182 | #if !defined(CONFIG_USER_ONLY) |
ae84dd0a AF |
1183 | if (riscv_has_ext(env, RVH)) { |
1184 | ctx->virt_enabled = riscv_cpu_virt_enabled(env); | |
ae84dd0a AF |
1185 | } else { |
1186 | ctx->virt_enabled = false; | |
1187 | } | |
45b4dc8b AF |
1188 | #else |
1189 | ctx->virt_enabled = false; | |
1190 | #endif | |
e91a7227 | 1191 | ctx->misa_ext = env->misa_ext; |
5b4f1d2d | 1192 | ctx->frm = -1; /* unknown rounding mode */ |
3b91323e | 1193 | ctx->cfg_ptr = &(cpu->cfg); |
a88f0402 | 1194 | ctx->mstatus_hs_fs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_FS); |
8e1ee1fb | 1195 | ctx->mstatus_hs_vs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_VS); |
743077b3 | 1196 | ctx->hlsx = FIELD_EX32(tb_flags, TB_FLAGS, HLSX); |
2b7168fc LZ |
1197 | ctx->vill = FIELD_EX32(tb_flags, TB_FLAGS, VILL); |
1198 | ctx->sew = FIELD_EX32(tb_flags, TB_FLAGS, SEW); | |
33f1beaf | 1199 | ctx->lmul = sextract32(FIELD_EX32(tb_flags, TB_FLAGS, LMUL), 0, 3); |
f1eed927 | 1200 | ctx->vta = FIELD_EX32(tb_flags, TB_FLAGS, VTA) && cpu->cfg.rvv_ta_all_1s; |
355d5584 | 1201 | ctx->vma = FIELD_EX32(tb_flags, TB_FLAGS, VMA) && cpu->cfg.rvv_ma_all_1s; |
752614ca | 1202 | ctx->cfg_vta_all_1s = cpu->cfg.rvv_ta_all_1s; |
f714361e | 1203 | ctx->vstart = env->vstart; |
2b7168fc | 1204 | ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); |
a2f827ff | 1205 | ctx->misa_mxl_max = env->misa_mxl_max; |
92371bd9 | 1206 | ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); |
a10b9d93 | 1207 | ctx->cs = cs; |
ecda15d1 RH |
1208 | ctx->ntemp = 0; |
1209 | memset(ctx->temp, 0, sizeof(ctx->temp)); | |
e1a29bbd WL |
1210 | ctx->nftemp = 0; |
1211 | memset(ctx->ftemp, 0, sizeof(ctx->ftemp)); | |
4208dc7e LZ |
1212 | ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED); |
1213 | ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED); | |
2c9d7471 | 1214 | ctx->itrigger = FIELD_EX32(tb_flags, TB_FLAGS, ITRIGGER); |
ecda15d1 | 1215 | ctx->zero = tcg_constant_tl(0); |
506c6698 | 1216 | ctx->virt_inst_excp = false; |
5b4f1d2d | 1217 | } |
55c2a12c | 1218 | |
5b4f1d2d EC |
1219 | static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) |
1220 | { | |
1221 | } | |
55c2a12c | 1222 | |
5b4f1d2d EC |
1223 | static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) |
1224 | { | |
1225 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
1226 | ||
62cf0245 AP |
1227 | tcg_gen_insn_start(ctx->base.pc_next, 0); |
1228 | ctx->insn_start = tcg_last_op(); | |
5b4f1d2d EC |
1229 | } |
1230 | ||
5b4f1d2d EC |
1231 | static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) |
1232 | { | |
1233 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
1234 | CPURISCVState *env = cpu->env_ptr; | |
4e116893 | 1235 | uint16_t opcode16 = translator_lduw(env, &ctx->base, ctx->base.pc_next); |
e1a29bbd | 1236 | int i; |
55c2a12c | 1237 | |
7667cafd | 1238 | ctx->ol = ctx->xl; |
25139bf7 | 1239 | decode_opc(env, ctx, opcode16); |
5b4f1d2d | 1240 | ctx->base.pc_next = ctx->pc_succ_insn; |
ecda15d1 | 1241 | |
e1a29bbd | 1242 | for (i = ctx->ntemp - 1; i >= 0; --i) { |
ecda15d1 RH |
1243 | tcg_temp_free(ctx->temp[i]); |
1244 | ctx->temp[i] = NULL; | |
1245 | } | |
1246 | ctx->ntemp = 0; | |
e1a29bbd WL |
1247 | for (i = ctx->nftemp - 1; i >= 0; --i) { |
1248 | tcg_temp_free_i64(ctx->ftemp[i]); | |
1249 | ctx->ftemp[i] = NULL; | |
1250 | } | |
1251 | ctx->nftemp = 0; | |
5b4f1d2d | 1252 | |
00c07344 | 1253 | /* Only the first insn within a TB is allowed to cross a page boundary. */ |
5b4f1d2d | 1254 | if (ctx->base.is_jmp == DISAS_NEXT) { |
2c9d7471 | 1255 | if (ctx->itrigger || !is_same_page(&ctx->base, ctx->base.pc_next)) { |
5b4f1d2d | 1256 | ctx->base.is_jmp = DISAS_TOO_MANY; |
00c07344 RH |
1257 | } else { |
1258 | unsigned page_ofs = ctx->base.pc_next & ~TARGET_PAGE_MASK; | |
1259 | ||
1260 | if (page_ofs > TARGET_PAGE_SIZE - MAX_INSN_LEN) { | |
1261 | uint16_t next_insn = cpu_lduw_code(env, ctx->base.pc_next); | |
1262 | int len = insn_len(next_insn); | |
1263 | ||
1264 | if (!is_same_page(&ctx->base, ctx->base.pc_next + len)) { | |
1265 | ctx->base.is_jmp = DISAS_TOO_MANY; | |
1266 | } | |
1267 | } | |
55c2a12c | 1268 | } |
55c2a12c | 1269 | } |
5b4f1d2d EC |
1270 | } |
1271 | ||
1272 | static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) | |
1273 | { | |
1274 | DisasContext *ctx = container_of(dcbase, DisasContext, base); | |
1275 | ||
1276 | switch (ctx->base.is_jmp) { | |
b2e32021 | 1277 | case DISAS_TOO_MANY: |
ccf08e40 | 1278 | gen_goto_tb(ctx, 0, ctx->base.pc_next); |
55c2a12c | 1279 | break; |
b2e32021 | 1280 | case DISAS_NORETURN: |
55c2a12c | 1281 | break; |
b2e32021 EC |
1282 | default: |
1283 | g_assert_not_reached(); | |
55c2a12c | 1284 | } |
5b4f1d2d EC |
1285 | } |
1286 | ||
8eb806a7 RH |
1287 | static void riscv_tr_disas_log(const DisasContextBase *dcbase, |
1288 | CPUState *cpu, FILE *logfile) | |
5b4f1d2d | 1289 | { |
35f69039 AF |
1290 | #ifndef CONFIG_USER_ONLY |
1291 | RISCVCPU *rvcpu = RISCV_CPU(cpu); | |
1292 | CPURISCVState *env = &rvcpu->env; | |
1293 | #endif | |
1294 | ||
8eb806a7 | 1295 | fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); |
35f69039 | 1296 | #ifndef CONFIG_USER_ONLY |
8eb806a7 RH |
1297 | fprintf(logfile, "Priv: "TARGET_FMT_ld"; Virt: "TARGET_FMT_ld"\n", |
1298 | env->priv, env->virt); | |
35f69039 | 1299 | #endif |
8eb806a7 | 1300 | target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); |
5b4f1d2d EC |
1301 | } |
1302 | ||
1303 | static const TranslatorOps riscv_tr_ops = { | |
1304 | .init_disas_context = riscv_tr_init_disas_context, | |
1305 | .tb_start = riscv_tr_tb_start, | |
1306 | .insn_start = riscv_tr_insn_start, | |
5b4f1d2d EC |
1307 | .translate_insn = riscv_tr_translate_insn, |
1308 | .tb_stop = riscv_tr_tb_stop, | |
1309 | .disas_log = riscv_tr_disas_log, | |
1310 | }; | |
1311 | ||
597f9b2d | 1312 | void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, |
306c8721 | 1313 | target_ulong pc, void *host_pc) |
5b4f1d2d EC |
1314 | { |
1315 | DisasContext ctx; | |
1316 | ||
306c8721 | 1317 | translator_loop(cs, tb, max_insns, pc, host_pc, &riscv_tr_ops, &ctx.base); |
55c2a12c MC |
1318 | } |
1319 | ||
1320 | void riscv_translate_init(void) | |
1321 | { | |
1322 | int i; | |
1323 | ||
8e034ae4 RH |
1324 | /* |
1325 | * cpu_gpr[0] is a placeholder for the zero register. Do not use it. | |
1326 | * Use the gen_set_gpr and get_gpr helper functions when accessing regs, | |
1327 | * unless you specifically block reads/writes to reg 0. | |
1328 | */ | |
55c2a12c | 1329 | cpu_gpr[0] = NULL; |
2b547084 | 1330 | cpu_gprh[0] = NULL; |
55c2a12c MC |
1331 | |
1332 | for (i = 1; i < 32; i++) { | |
1333 | cpu_gpr[i] = tcg_global_mem_new(cpu_env, | |
1334 | offsetof(CPURISCVState, gpr[i]), riscv_int_regnames[i]); | |
2b547084 FP |
1335 | cpu_gprh[i] = tcg_global_mem_new(cpu_env, |
1336 | offsetof(CPURISCVState, gprh[i]), riscv_int_regnamesh[i]); | |
55c2a12c MC |
1337 | } |
1338 | ||
1339 | for (i = 0; i < 32; i++) { | |
1340 | cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, | |
1341 | offsetof(CPURISCVState, fpr[i]), riscv_fpr_regnames[i]); | |
1342 | } | |
1343 | ||
1344 | cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc"); | |
ad9e5aa2 | 1345 | cpu_vl = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, vl), "vl"); |
f714361e FC |
1346 | cpu_vstart = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, vstart), |
1347 | "vstart"); | |
55c2a12c MC |
1348 | load_res = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_res), |
1349 | "load_res"); | |
1350 | load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val), | |
1351 | "load_val"); | |
0774a7a1 | 1352 | /* Assign PM CSRs to tcg globals */ |
0cff460d LZ |
1353 | pm_mask = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, cur_pmmask), |
1354 | "pmmask"); | |
1355 | pm_base = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, cur_pmbase), | |
1356 | "pmbase"); | |
55c2a12c | 1357 | } |