161766fe9SRichard Henderson /* 261766fe9SRichard Henderson * HPPA emulation cpu translation for qemu. 361766fe9SRichard Henderson * 461766fe9SRichard Henderson * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> 561766fe9SRichard Henderson * 661766fe9SRichard Henderson * This library is free software; you can redistribute it and/or 761766fe9SRichard Henderson * modify it under the terms of the GNU Lesser General Public 861766fe9SRichard Henderson * License as published by the Free Software Foundation; either 961766fe9SRichard Henderson * version 2 of the License, or (at your option) any later version. 1061766fe9SRichard Henderson * 1161766fe9SRichard Henderson * This library is distributed in the hope that it will be useful, 1261766fe9SRichard Henderson * but WITHOUT ANY WARRANTY; without even the implied warranty of 1361766fe9SRichard Henderson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1461766fe9SRichard Henderson * Lesser General Public License for more details. 1561766fe9SRichard Henderson * 1661766fe9SRichard Henderson * You should have received a copy of the GNU Lesser General Public 1761766fe9SRichard Henderson * License along with this library; if not, see <http://www.gnu.org/licenses/>. 1861766fe9SRichard Henderson */ 1961766fe9SRichard Henderson 2061766fe9SRichard Henderson #include "qemu/osdep.h" 2161766fe9SRichard Henderson #include "cpu.h" 2261766fe9SRichard Henderson #include "disas/disas.h" 2361766fe9SRichard Henderson #include "qemu/host-utils.h" 2461766fe9SRichard Henderson #include "exec/exec-all.h" 2561766fe9SRichard Henderson #include "tcg-op.h" 2661766fe9SRichard Henderson #include "exec/cpu_ldst.h" 2761766fe9SRichard Henderson 2861766fe9SRichard Henderson #include "exec/helper-proto.h" 2961766fe9SRichard Henderson #include "exec/helper-gen.h" 3061766fe9SRichard Henderson 3161766fe9SRichard Henderson #include "trace-tcg.h" 3261766fe9SRichard Henderson #include "exec/log.h" 3361766fe9SRichard Henderson 3461766fe9SRichard Henderson typedef struct DisasCond { 3561766fe9SRichard Henderson TCGCond c; 3661766fe9SRichard Henderson TCGv a0, a1; 3761766fe9SRichard Henderson bool a0_is_n; 3861766fe9SRichard Henderson bool a1_is_0; 3961766fe9SRichard Henderson } DisasCond; 4061766fe9SRichard Henderson 4161766fe9SRichard Henderson typedef struct DisasContext { 4261766fe9SRichard Henderson struct TranslationBlock *tb; 4361766fe9SRichard Henderson CPUState *cs; 4461766fe9SRichard Henderson 4561766fe9SRichard Henderson target_ulong iaoq_f; 4661766fe9SRichard Henderson target_ulong iaoq_b; 4761766fe9SRichard Henderson target_ulong iaoq_n; 4861766fe9SRichard Henderson TCGv iaoq_n_var; 4961766fe9SRichard Henderson 5061766fe9SRichard Henderson int ntemps; 5161766fe9SRichard Henderson TCGv temps[8]; 5261766fe9SRichard Henderson 5361766fe9SRichard Henderson DisasCond null_cond; 5461766fe9SRichard Henderson TCGLabel *null_lab; 5561766fe9SRichard Henderson 5661766fe9SRichard Henderson bool singlestep_enabled; 5761766fe9SRichard Henderson bool psw_n_nonzero; 5861766fe9SRichard Henderson } DisasContext; 5961766fe9SRichard Henderson 6061766fe9SRichard Henderson /* Return values from translate_one, indicating the state of the TB. 6161766fe9SRichard Henderson Note that zero indicates that we are not exiting the TB. */ 6261766fe9SRichard Henderson 6361766fe9SRichard Henderson typedef enum { 6461766fe9SRichard Henderson NO_EXIT, 6561766fe9SRichard Henderson 6661766fe9SRichard Henderson /* We have emitted one or more goto_tb. No fixup required. */ 6761766fe9SRichard Henderson EXIT_GOTO_TB, 6861766fe9SRichard Henderson 6961766fe9SRichard Henderson /* We are not using a goto_tb (for whatever reason), but have updated 7061766fe9SRichard Henderson the iaq (for whatever reason), so don't do it again on exit. */ 7161766fe9SRichard Henderson EXIT_IAQ_N_UPDATED, 7261766fe9SRichard Henderson 7361766fe9SRichard Henderson /* We are exiting the TB, but have neither emitted a goto_tb, nor 7461766fe9SRichard Henderson updated the iaq for the next instruction to be executed. */ 7561766fe9SRichard Henderson EXIT_IAQ_N_STALE, 7661766fe9SRichard Henderson 7761766fe9SRichard Henderson /* We are ending the TB with a noreturn function call, e.g. longjmp. 7861766fe9SRichard Henderson No following code will be executed. */ 7961766fe9SRichard Henderson EXIT_NORETURN, 8061766fe9SRichard Henderson } ExitStatus; 8161766fe9SRichard Henderson 8261766fe9SRichard Henderson typedef struct DisasInsn { 8361766fe9SRichard Henderson uint32_t insn, mask; 8461766fe9SRichard Henderson ExitStatus (*trans)(DisasContext *ctx, uint32_t insn, 8561766fe9SRichard Henderson const struct DisasInsn *f); 8661766fe9SRichard Henderson } DisasInsn; 8761766fe9SRichard Henderson 8861766fe9SRichard Henderson /* global register indexes */ 8961766fe9SRichard Henderson static TCGv_env cpu_env; 9061766fe9SRichard Henderson static TCGv cpu_gr[32]; 9161766fe9SRichard Henderson static TCGv cpu_iaoq_f; 9261766fe9SRichard Henderson static TCGv cpu_iaoq_b; 9361766fe9SRichard Henderson static TCGv cpu_sar; 9461766fe9SRichard Henderson static TCGv cpu_psw_n; 9561766fe9SRichard Henderson static TCGv cpu_psw_v; 9661766fe9SRichard Henderson static TCGv cpu_psw_cb; 9761766fe9SRichard Henderson static TCGv cpu_psw_cb_msb; 9861766fe9SRichard Henderson static TCGv cpu_cr26; 9961766fe9SRichard Henderson static TCGv cpu_cr27; 10061766fe9SRichard Henderson 10161766fe9SRichard Henderson #include "exec/gen-icount.h" 10261766fe9SRichard Henderson 10361766fe9SRichard Henderson void hppa_translate_init(void) 10461766fe9SRichard Henderson { 10561766fe9SRichard Henderson #define DEF_VAR(V) { &cpu_##V, #V, offsetof(CPUHPPAState, V) } 10661766fe9SRichard Henderson 10761766fe9SRichard Henderson typedef struct { TCGv *var; const char *name; int ofs; } GlobalVar; 10861766fe9SRichard Henderson static const GlobalVar vars[] = { 10961766fe9SRichard Henderson DEF_VAR(sar), 11061766fe9SRichard Henderson DEF_VAR(cr26), 11161766fe9SRichard Henderson DEF_VAR(cr27), 11261766fe9SRichard Henderson DEF_VAR(psw_n), 11361766fe9SRichard Henderson DEF_VAR(psw_v), 11461766fe9SRichard Henderson DEF_VAR(psw_cb), 11561766fe9SRichard Henderson DEF_VAR(psw_cb_msb), 11661766fe9SRichard Henderson DEF_VAR(iaoq_f), 11761766fe9SRichard Henderson DEF_VAR(iaoq_b), 11861766fe9SRichard Henderson }; 11961766fe9SRichard Henderson 12061766fe9SRichard Henderson #undef DEF_VAR 12161766fe9SRichard Henderson 12261766fe9SRichard Henderson /* Use the symbolic register names that match the disassembler. */ 12361766fe9SRichard Henderson static const char gr_names[32][4] = { 12461766fe9SRichard Henderson "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", 12561766fe9SRichard Henderson "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", 12661766fe9SRichard Henderson "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", 12761766fe9SRichard Henderson "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" 12861766fe9SRichard Henderson }; 12961766fe9SRichard Henderson 13061766fe9SRichard Henderson static bool done_init = 0; 13161766fe9SRichard Henderson int i; 13261766fe9SRichard Henderson 13361766fe9SRichard Henderson if (done_init) { 13461766fe9SRichard Henderson return; 13561766fe9SRichard Henderson } 13661766fe9SRichard Henderson done_init = 1; 13761766fe9SRichard Henderson 13861766fe9SRichard Henderson cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); 13961766fe9SRichard Henderson tcg_ctx.tcg_env = cpu_env; 14061766fe9SRichard Henderson 14161766fe9SRichard Henderson TCGV_UNUSED(cpu_gr[0]); 14261766fe9SRichard Henderson for (i = 1; i < 32; i++) { 14361766fe9SRichard Henderson cpu_gr[i] = tcg_global_mem_new(cpu_env, 14461766fe9SRichard Henderson offsetof(CPUHPPAState, gr[i]), 14561766fe9SRichard Henderson gr_names[i]); 14661766fe9SRichard Henderson } 14761766fe9SRichard Henderson 14861766fe9SRichard Henderson for (i = 0; i < ARRAY_SIZE(vars); ++i) { 14961766fe9SRichard Henderson const GlobalVar *v = &vars[i]; 15061766fe9SRichard Henderson *v->var = tcg_global_mem_new(cpu_env, v->ofs, v->name); 15161766fe9SRichard Henderson } 15261766fe9SRichard Henderson } 15361766fe9SRichard Henderson 154*129e9cc3SRichard Henderson static DisasCond cond_make_f(void) 155*129e9cc3SRichard Henderson { 156*129e9cc3SRichard Henderson DisasCond r = { .c = TCG_COND_NEVER }; 157*129e9cc3SRichard Henderson TCGV_UNUSED(r.a0); 158*129e9cc3SRichard Henderson TCGV_UNUSED(r.a1); 159*129e9cc3SRichard Henderson return r; 160*129e9cc3SRichard Henderson } 161*129e9cc3SRichard Henderson 162*129e9cc3SRichard Henderson static DisasCond cond_make_n(void) 163*129e9cc3SRichard Henderson { 164*129e9cc3SRichard Henderson DisasCond r = { .c = TCG_COND_NE, .a0_is_n = true, .a1_is_0 = true }; 165*129e9cc3SRichard Henderson r.a0 = cpu_psw_n; 166*129e9cc3SRichard Henderson TCGV_UNUSED(r.a1); 167*129e9cc3SRichard Henderson return r; 168*129e9cc3SRichard Henderson } 169*129e9cc3SRichard Henderson 170*129e9cc3SRichard Henderson static DisasCond cond_make_0(TCGCond c, TCGv a0) 171*129e9cc3SRichard Henderson { 172*129e9cc3SRichard Henderson DisasCond r = { .c = c, .a1_is_0 = true }; 173*129e9cc3SRichard Henderson 174*129e9cc3SRichard Henderson assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); 175*129e9cc3SRichard Henderson r.a0 = tcg_temp_new(); 176*129e9cc3SRichard Henderson tcg_gen_mov_tl(r.a0, a0); 177*129e9cc3SRichard Henderson TCGV_UNUSED(r.a1); 178*129e9cc3SRichard Henderson 179*129e9cc3SRichard Henderson return r; 180*129e9cc3SRichard Henderson } 181*129e9cc3SRichard Henderson 182*129e9cc3SRichard Henderson static DisasCond cond_make(TCGCond c, TCGv a0, TCGv a1) 183*129e9cc3SRichard Henderson { 184*129e9cc3SRichard Henderson DisasCond r = { .c = c }; 185*129e9cc3SRichard Henderson 186*129e9cc3SRichard Henderson assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); 187*129e9cc3SRichard Henderson r.a0 = tcg_temp_new(); 188*129e9cc3SRichard Henderson tcg_gen_mov_tl(r.a0, a0); 189*129e9cc3SRichard Henderson r.a1 = tcg_temp_new(); 190*129e9cc3SRichard Henderson tcg_gen_mov_tl(r.a1, a1); 191*129e9cc3SRichard Henderson 192*129e9cc3SRichard Henderson return r; 193*129e9cc3SRichard Henderson } 194*129e9cc3SRichard Henderson 195*129e9cc3SRichard Henderson static void cond_prep(DisasCond *cond) 196*129e9cc3SRichard Henderson { 197*129e9cc3SRichard Henderson if (cond->a1_is_0) { 198*129e9cc3SRichard Henderson cond->a1_is_0 = false; 199*129e9cc3SRichard Henderson cond->a1 = tcg_const_tl(0); 200*129e9cc3SRichard Henderson } 201*129e9cc3SRichard Henderson } 202*129e9cc3SRichard Henderson 203*129e9cc3SRichard Henderson static void cond_free(DisasCond *cond) 204*129e9cc3SRichard Henderson { 205*129e9cc3SRichard Henderson switch (cond->c) { 206*129e9cc3SRichard Henderson default: 207*129e9cc3SRichard Henderson if (!cond->a0_is_n) { 208*129e9cc3SRichard Henderson tcg_temp_free(cond->a0); 209*129e9cc3SRichard Henderson } 210*129e9cc3SRichard Henderson if (!cond->a1_is_0) { 211*129e9cc3SRichard Henderson tcg_temp_free(cond->a1); 212*129e9cc3SRichard Henderson } 213*129e9cc3SRichard Henderson cond->a0_is_n = false; 214*129e9cc3SRichard Henderson cond->a1_is_0 = false; 215*129e9cc3SRichard Henderson TCGV_UNUSED(cond->a0); 216*129e9cc3SRichard Henderson TCGV_UNUSED(cond->a1); 217*129e9cc3SRichard Henderson /* fallthru */ 218*129e9cc3SRichard Henderson case TCG_COND_ALWAYS: 219*129e9cc3SRichard Henderson cond->c = TCG_COND_NEVER; 220*129e9cc3SRichard Henderson break; 221*129e9cc3SRichard Henderson case TCG_COND_NEVER: 222*129e9cc3SRichard Henderson break; 223*129e9cc3SRichard Henderson } 224*129e9cc3SRichard Henderson } 225*129e9cc3SRichard Henderson 22661766fe9SRichard Henderson static TCGv get_temp(DisasContext *ctx) 22761766fe9SRichard Henderson { 22861766fe9SRichard Henderson unsigned i = ctx->ntemps++; 22961766fe9SRichard Henderson g_assert(i < ARRAY_SIZE(ctx->temps)); 23061766fe9SRichard Henderson return ctx->temps[i] = tcg_temp_new(); 23161766fe9SRichard Henderson } 23261766fe9SRichard Henderson 23361766fe9SRichard Henderson static TCGv load_const(DisasContext *ctx, target_long v) 23461766fe9SRichard Henderson { 23561766fe9SRichard Henderson TCGv t = get_temp(ctx); 23661766fe9SRichard Henderson tcg_gen_movi_tl(t, v); 23761766fe9SRichard Henderson return t; 23861766fe9SRichard Henderson } 23961766fe9SRichard Henderson 24061766fe9SRichard Henderson static TCGv load_gpr(DisasContext *ctx, unsigned reg) 24161766fe9SRichard Henderson { 24261766fe9SRichard Henderson if (reg == 0) { 24361766fe9SRichard Henderson TCGv t = get_temp(ctx); 24461766fe9SRichard Henderson tcg_gen_movi_tl(t, 0); 24561766fe9SRichard Henderson return t; 24661766fe9SRichard Henderson } else { 24761766fe9SRichard Henderson return cpu_gr[reg]; 24861766fe9SRichard Henderson } 24961766fe9SRichard Henderson } 25061766fe9SRichard Henderson 25161766fe9SRichard Henderson static TCGv dest_gpr(DisasContext *ctx, unsigned reg) 25261766fe9SRichard Henderson { 253*129e9cc3SRichard Henderson if (reg == 0 || ctx->null_cond.c != TCG_COND_NEVER) { 25461766fe9SRichard Henderson return get_temp(ctx); 25561766fe9SRichard Henderson } else { 25661766fe9SRichard Henderson return cpu_gr[reg]; 25761766fe9SRichard Henderson } 25861766fe9SRichard Henderson } 25961766fe9SRichard Henderson 260*129e9cc3SRichard Henderson static void save_or_nullify(DisasContext *ctx, TCGv dest, TCGv t) 261*129e9cc3SRichard Henderson { 262*129e9cc3SRichard Henderson if (ctx->null_cond.c != TCG_COND_NEVER) { 263*129e9cc3SRichard Henderson cond_prep(&ctx->null_cond); 264*129e9cc3SRichard Henderson tcg_gen_movcond_tl(ctx->null_cond.c, dest, ctx->null_cond.a0, 265*129e9cc3SRichard Henderson ctx->null_cond.a1, dest, t); 266*129e9cc3SRichard Henderson } else { 267*129e9cc3SRichard Henderson tcg_gen_mov_tl(dest, t); 268*129e9cc3SRichard Henderson } 269*129e9cc3SRichard Henderson } 270*129e9cc3SRichard Henderson 271*129e9cc3SRichard Henderson static void save_gpr(DisasContext *ctx, unsigned reg, TCGv t) 272*129e9cc3SRichard Henderson { 273*129e9cc3SRichard Henderson if (reg != 0) { 274*129e9cc3SRichard Henderson save_or_nullify(ctx, cpu_gr[reg], t); 275*129e9cc3SRichard Henderson } 276*129e9cc3SRichard Henderson } 277*129e9cc3SRichard Henderson 278*129e9cc3SRichard Henderson /* Skip over the implementation of an insn that has been nullified. 279*129e9cc3SRichard Henderson Use this when the insn is too complex for a conditional move. */ 280*129e9cc3SRichard Henderson static void nullify_over(DisasContext *ctx) 281*129e9cc3SRichard Henderson { 282*129e9cc3SRichard Henderson if (ctx->null_cond.c != TCG_COND_NEVER) { 283*129e9cc3SRichard Henderson /* The always condition should have been handled in the main loop. */ 284*129e9cc3SRichard Henderson assert(ctx->null_cond.c != TCG_COND_ALWAYS); 285*129e9cc3SRichard Henderson 286*129e9cc3SRichard Henderson ctx->null_lab = gen_new_label(); 287*129e9cc3SRichard Henderson cond_prep(&ctx->null_cond); 288*129e9cc3SRichard Henderson 289*129e9cc3SRichard Henderson /* If we're using PSW[N], copy it to a temp because... */ 290*129e9cc3SRichard Henderson if (ctx->null_cond.a0_is_n) { 291*129e9cc3SRichard Henderson ctx->null_cond.a0_is_n = false; 292*129e9cc3SRichard Henderson ctx->null_cond.a0 = tcg_temp_new(); 293*129e9cc3SRichard Henderson tcg_gen_mov_tl(ctx->null_cond.a0, cpu_psw_n); 294*129e9cc3SRichard Henderson } 295*129e9cc3SRichard Henderson /* ... we clear it before branching over the implementation, 296*129e9cc3SRichard Henderson so that (1) it's clear after nullifying this insn and 297*129e9cc3SRichard Henderson (2) if this insn nullifies the next, PSW[N] is valid. */ 298*129e9cc3SRichard Henderson if (ctx->psw_n_nonzero) { 299*129e9cc3SRichard Henderson ctx->psw_n_nonzero = false; 300*129e9cc3SRichard Henderson tcg_gen_movi_tl(cpu_psw_n, 0); 301*129e9cc3SRichard Henderson } 302*129e9cc3SRichard Henderson 303*129e9cc3SRichard Henderson tcg_gen_brcond_tl(ctx->null_cond.c, ctx->null_cond.a0, 304*129e9cc3SRichard Henderson ctx->null_cond.a1, ctx->null_lab); 305*129e9cc3SRichard Henderson cond_free(&ctx->null_cond); 306*129e9cc3SRichard Henderson } 307*129e9cc3SRichard Henderson } 308*129e9cc3SRichard Henderson 309*129e9cc3SRichard Henderson /* Save the current nullification state to PSW[N]. */ 310*129e9cc3SRichard Henderson static void nullify_save(DisasContext *ctx) 311*129e9cc3SRichard Henderson { 312*129e9cc3SRichard Henderson if (ctx->null_cond.c == TCG_COND_NEVER) { 313*129e9cc3SRichard Henderson if (ctx->psw_n_nonzero) { 314*129e9cc3SRichard Henderson tcg_gen_movi_tl(cpu_psw_n, 0); 315*129e9cc3SRichard Henderson } 316*129e9cc3SRichard Henderson return; 317*129e9cc3SRichard Henderson } 318*129e9cc3SRichard Henderson if (!ctx->null_cond.a0_is_n) { 319*129e9cc3SRichard Henderson cond_prep(&ctx->null_cond); 320*129e9cc3SRichard Henderson tcg_gen_setcond_tl(ctx->null_cond.c, cpu_psw_n, 321*129e9cc3SRichard Henderson ctx->null_cond.a0, ctx->null_cond.a1); 322*129e9cc3SRichard Henderson ctx->psw_n_nonzero = true; 323*129e9cc3SRichard Henderson } 324*129e9cc3SRichard Henderson cond_free(&ctx->null_cond); 325*129e9cc3SRichard Henderson } 326*129e9cc3SRichard Henderson 327*129e9cc3SRichard Henderson /* Set a PSW[N] to X. The intention is that this is used immediately 328*129e9cc3SRichard Henderson before a goto_tb/exit_tb, so that there is no fallthru path to other 329*129e9cc3SRichard Henderson code within the TB. Therefore we do not update psw_n_nonzero. */ 330*129e9cc3SRichard Henderson static void nullify_set(DisasContext *ctx, bool x) 331*129e9cc3SRichard Henderson { 332*129e9cc3SRichard Henderson if (ctx->psw_n_nonzero || x) { 333*129e9cc3SRichard Henderson tcg_gen_movi_tl(cpu_psw_n, x); 334*129e9cc3SRichard Henderson } 335*129e9cc3SRichard Henderson } 336*129e9cc3SRichard Henderson 337*129e9cc3SRichard Henderson /* Mark the end of an instruction that may have been nullified. 338*129e9cc3SRichard Henderson This is the pair to nullify_over. */ 339*129e9cc3SRichard Henderson static ExitStatus nullify_end(DisasContext *ctx, ExitStatus status) 340*129e9cc3SRichard Henderson { 341*129e9cc3SRichard Henderson TCGLabel *null_lab = ctx->null_lab; 342*129e9cc3SRichard Henderson 343*129e9cc3SRichard Henderson if (likely(null_lab == NULL)) { 344*129e9cc3SRichard Henderson /* The current insn wasn't conditional or handled the condition 345*129e9cc3SRichard Henderson applied to it without a branch, so the (new) setting of 346*129e9cc3SRichard Henderson NULL_COND can be applied directly to the next insn. */ 347*129e9cc3SRichard Henderson return status; 348*129e9cc3SRichard Henderson } 349*129e9cc3SRichard Henderson ctx->null_lab = NULL; 350*129e9cc3SRichard Henderson 351*129e9cc3SRichard Henderson if (likely(ctx->null_cond.c == TCG_COND_NEVER)) { 352*129e9cc3SRichard Henderson /* The next instruction will be unconditional, 353*129e9cc3SRichard Henderson and NULL_COND already reflects that. */ 354*129e9cc3SRichard Henderson gen_set_label(null_lab); 355*129e9cc3SRichard Henderson } else { 356*129e9cc3SRichard Henderson /* The insn that we just executed is itself nullifying the next 357*129e9cc3SRichard Henderson instruction. Store the condition in the PSW[N] global. 358*129e9cc3SRichard Henderson We asserted PSW[N] = 0 in nullify_over, so that after the 359*129e9cc3SRichard Henderson label we have the proper value in place. */ 360*129e9cc3SRichard Henderson nullify_save(ctx); 361*129e9cc3SRichard Henderson gen_set_label(null_lab); 362*129e9cc3SRichard Henderson ctx->null_cond = cond_make_n(); 363*129e9cc3SRichard Henderson } 364*129e9cc3SRichard Henderson 365*129e9cc3SRichard Henderson assert(status != EXIT_GOTO_TB && status != EXIT_IAQ_N_UPDATED); 366*129e9cc3SRichard Henderson if (status == EXIT_NORETURN) { 367*129e9cc3SRichard Henderson status = NO_EXIT; 368*129e9cc3SRichard Henderson } 369*129e9cc3SRichard Henderson return status; 370*129e9cc3SRichard Henderson } 371*129e9cc3SRichard Henderson 37261766fe9SRichard Henderson static void copy_iaoq_entry(TCGv dest, target_ulong ival, TCGv vval) 37361766fe9SRichard Henderson { 37461766fe9SRichard Henderson if (unlikely(ival == -1)) { 37561766fe9SRichard Henderson tcg_gen_mov_tl(dest, vval); 37661766fe9SRichard Henderson } else { 37761766fe9SRichard Henderson tcg_gen_movi_tl(dest, ival); 37861766fe9SRichard Henderson } 37961766fe9SRichard Henderson } 38061766fe9SRichard Henderson 38161766fe9SRichard Henderson static inline target_ulong iaoq_dest(DisasContext *ctx, target_long disp) 38261766fe9SRichard Henderson { 38361766fe9SRichard Henderson return ctx->iaoq_f + disp + 8; 38461766fe9SRichard Henderson } 38561766fe9SRichard Henderson 38661766fe9SRichard Henderson static void gen_excp_1(int exception) 38761766fe9SRichard Henderson { 38861766fe9SRichard Henderson TCGv_i32 t = tcg_const_i32(exception); 38961766fe9SRichard Henderson gen_helper_excp(cpu_env, t); 39061766fe9SRichard Henderson tcg_temp_free_i32(t); 39161766fe9SRichard Henderson } 39261766fe9SRichard Henderson 39361766fe9SRichard Henderson static ExitStatus gen_excp(DisasContext *ctx, int exception) 39461766fe9SRichard Henderson { 39561766fe9SRichard Henderson copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); 39661766fe9SRichard Henderson copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); 397*129e9cc3SRichard Henderson nullify_save(ctx); 39861766fe9SRichard Henderson gen_excp_1(exception); 39961766fe9SRichard Henderson return EXIT_NORETURN; 40061766fe9SRichard Henderson } 40161766fe9SRichard Henderson 40261766fe9SRichard Henderson static ExitStatus gen_illegal(DisasContext *ctx) 40361766fe9SRichard Henderson { 404*129e9cc3SRichard Henderson nullify_over(ctx); 405*129e9cc3SRichard Henderson return nullify_end(ctx, gen_excp(ctx, EXCP_SIGILL)); 40661766fe9SRichard Henderson } 40761766fe9SRichard Henderson 40861766fe9SRichard Henderson static bool use_goto_tb(DisasContext *ctx, target_ulong dest) 40961766fe9SRichard Henderson { 41061766fe9SRichard Henderson /* Suppress goto_tb in the case of single-steping and IO. */ 41161766fe9SRichard Henderson if ((ctx->tb->cflags & CF_LAST_IO) || ctx->singlestep_enabled) { 41261766fe9SRichard Henderson return false; 41361766fe9SRichard Henderson } 41461766fe9SRichard Henderson return true; 41561766fe9SRichard Henderson } 41661766fe9SRichard Henderson 417*129e9cc3SRichard Henderson /* If the next insn is to be nullified, and it's on the same page, 418*129e9cc3SRichard Henderson and we're not attempting to set a breakpoint on it, then we can 419*129e9cc3SRichard Henderson totally skip the nullified insn. This avoids creating and 420*129e9cc3SRichard Henderson executing a TB that merely branches to the next TB. */ 421*129e9cc3SRichard Henderson static bool use_nullify_skip(DisasContext *ctx) 422*129e9cc3SRichard Henderson { 423*129e9cc3SRichard Henderson return (((ctx->iaoq_b ^ ctx->iaoq_f) & TARGET_PAGE_MASK) == 0 424*129e9cc3SRichard Henderson && !cpu_breakpoint_test(ctx->cs, ctx->iaoq_b, BP_ANY)); 425*129e9cc3SRichard Henderson } 426*129e9cc3SRichard Henderson 42761766fe9SRichard Henderson static void gen_goto_tb(DisasContext *ctx, int which, 42861766fe9SRichard Henderson target_ulong f, target_ulong b) 42961766fe9SRichard Henderson { 43061766fe9SRichard Henderson if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { 43161766fe9SRichard Henderson tcg_gen_goto_tb(which); 43261766fe9SRichard Henderson tcg_gen_movi_tl(cpu_iaoq_f, f); 43361766fe9SRichard Henderson tcg_gen_movi_tl(cpu_iaoq_b, b); 43461766fe9SRichard Henderson tcg_gen_exit_tb((uintptr_t)ctx->tb + which); 43561766fe9SRichard Henderson } else { 43661766fe9SRichard Henderson copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b); 43761766fe9SRichard Henderson copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var); 43861766fe9SRichard Henderson if (ctx->singlestep_enabled) { 43961766fe9SRichard Henderson gen_excp_1(EXCP_DEBUG); 44061766fe9SRichard Henderson } else { 44161766fe9SRichard Henderson tcg_gen_exit_tb(0); 44261766fe9SRichard Henderson } 44361766fe9SRichard Henderson } 44461766fe9SRichard Henderson } 44561766fe9SRichard Henderson 44661766fe9SRichard Henderson static ExitStatus translate_table_int(DisasContext *ctx, uint32_t insn, 44761766fe9SRichard Henderson const DisasInsn table[], size_t n) 44861766fe9SRichard Henderson { 44961766fe9SRichard Henderson size_t i; 45061766fe9SRichard Henderson for (i = 0; i < n; ++i) { 45161766fe9SRichard Henderson if ((insn & table[i].mask) == table[i].insn) { 45261766fe9SRichard Henderson return table[i].trans(ctx, insn, &table[i]); 45361766fe9SRichard Henderson } 45461766fe9SRichard Henderson } 45561766fe9SRichard Henderson return gen_illegal(ctx); 45661766fe9SRichard Henderson } 45761766fe9SRichard Henderson 45861766fe9SRichard Henderson #define translate_table(ctx, insn, table) \ 45961766fe9SRichard Henderson translate_table_int(ctx, insn, table, ARRAY_SIZE(table)) 46061766fe9SRichard Henderson 46161766fe9SRichard Henderson static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) 46261766fe9SRichard Henderson { 46361766fe9SRichard Henderson uint32_t opc = extract32(insn, 26, 6); 46461766fe9SRichard Henderson 46561766fe9SRichard Henderson switch (opc) { 46661766fe9SRichard Henderson default: 46761766fe9SRichard Henderson break; 46861766fe9SRichard Henderson } 46961766fe9SRichard Henderson return gen_illegal(ctx); 47061766fe9SRichard Henderson } 47161766fe9SRichard Henderson 47261766fe9SRichard Henderson void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) 47361766fe9SRichard Henderson { 47461766fe9SRichard Henderson HPPACPU *cpu = hppa_env_get_cpu(env); 47561766fe9SRichard Henderson CPUState *cs = CPU(cpu); 47661766fe9SRichard Henderson DisasContext ctx; 47761766fe9SRichard Henderson ExitStatus ret; 47861766fe9SRichard Henderson int num_insns, max_insns, i; 47961766fe9SRichard Henderson 48061766fe9SRichard Henderson ctx.tb = tb; 48161766fe9SRichard Henderson ctx.cs = cs; 48261766fe9SRichard Henderson ctx.iaoq_f = tb->pc; 48361766fe9SRichard Henderson ctx.iaoq_b = tb->cs_base; 48461766fe9SRichard Henderson ctx.singlestep_enabled = cs->singlestep_enabled; 48561766fe9SRichard Henderson 48661766fe9SRichard Henderson ctx.ntemps = 0; 48761766fe9SRichard Henderson for (i = 0; i < ARRAY_SIZE(ctx.temps); ++i) { 48861766fe9SRichard Henderson TCGV_UNUSED(ctx.temps[i]); 48961766fe9SRichard Henderson } 49061766fe9SRichard Henderson 49161766fe9SRichard Henderson /* Compute the maximum number of insns to execute, as bounded by 49261766fe9SRichard Henderson (1) icount, (2) single-stepping, (3) branch delay slots, or 49361766fe9SRichard Henderson (4) the number of insns remaining on the current page. */ 49461766fe9SRichard Henderson max_insns = tb->cflags & CF_COUNT_MASK; 49561766fe9SRichard Henderson if (max_insns == 0) { 49661766fe9SRichard Henderson max_insns = CF_COUNT_MASK; 49761766fe9SRichard Henderson } 49861766fe9SRichard Henderson if (ctx.singlestep_enabled || singlestep) { 49961766fe9SRichard Henderson max_insns = 1; 50061766fe9SRichard Henderson } else if (max_insns > TCG_MAX_INSNS) { 50161766fe9SRichard Henderson max_insns = TCG_MAX_INSNS; 50261766fe9SRichard Henderson } 50361766fe9SRichard Henderson 50461766fe9SRichard Henderson num_insns = 0; 50561766fe9SRichard Henderson gen_tb_start(tb); 50661766fe9SRichard Henderson 507*129e9cc3SRichard Henderson /* Seed the nullification status from PSW[N], as shown in TB->FLAGS. */ 508*129e9cc3SRichard Henderson ctx.null_cond = cond_make_f(); 509*129e9cc3SRichard Henderson ctx.psw_n_nonzero = false; 510*129e9cc3SRichard Henderson if (tb->flags & 1) { 511*129e9cc3SRichard Henderson ctx.null_cond.c = TCG_COND_ALWAYS; 512*129e9cc3SRichard Henderson ctx.psw_n_nonzero = true; 513*129e9cc3SRichard Henderson } 514*129e9cc3SRichard Henderson ctx.null_lab = NULL; 515*129e9cc3SRichard Henderson 51661766fe9SRichard Henderson do { 51761766fe9SRichard Henderson tcg_gen_insn_start(ctx.iaoq_f, ctx.iaoq_b); 51861766fe9SRichard Henderson num_insns++; 51961766fe9SRichard Henderson 52061766fe9SRichard Henderson if (unlikely(cpu_breakpoint_test(cs, ctx.iaoq_f, BP_ANY))) { 52161766fe9SRichard Henderson ret = gen_excp(&ctx, EXCP_DEBUG); 52261766fe9SRichard Henderson break; 52361766fe9SRichard Henderson } 52461766fe9SRichard Henderson if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { 52561766fe9SRichard Henderson gen_io_start(); 52661766fe9SRichard Henderson } 52761766fe9SRichard Henderson 52861766fe9SRichard Henderson { 52961766fe9SRichard Henderson /* Always fetch the insn, even if nullified, so that we check 53061766fe9SRichard Henderson the page permissions for execute. */ 53161766fe9SRichard Henderson uint32_t insn = cpu_ldl_code(env, ctx.iaoq_f); 53261766fe9SRichard Henderson 53361766fe9SRichard Henderson /* Set up the IA queue for the next insn. 53461766fe9SRichard Henderson This will be overwritten by a branch. */ 53561766fe9SRichard Henderson if (ctx.iaoq_b == -1) { 53661766fe9SRichard Henderson ctx.iaoq_n = -1; 53761766fe9SRichard Henderson ctx.iaoq_n_var = get_temp(&ctx); 53861766fe9SRichard Henderson tcg_gen_addi_tl(ctx.iaoq_n_var, cpu_iaoq_b, 4); 53961766fe9SRichard Henderson } else { 54061766fe9SRichard Henderson ctx.iaoq_n = ctx.iaoq_b + 4; 54161766fe9SRichard Henderson TCGV_UNUSED(ctx.iaoq_n_var); 54261766fe9SRichard Henderson } 54361766fe9SRichard Henderson 544*129e9cc3SRichard Henderson if (unlikely(ctx.null_cond.c == TCG_COND_ALWAYS)) { 545*129e9cc3SRichard Henderson ctx.null_cond.c = TCG_COND_NEVER; 546*129e9cc3SRichard Henderson ret = NO_EXIT; 547*129e9cc3SRichard Henderson } else { 54861766fe9SRichard Henderson ret = translate_one(&ctx, insn); 549*129e9cc3SRichard Henderson assert(ctx.null_lab == NULL); 550*129e9cc3SRichard Henderson } 55161766fe9SRichard Henderson } 55261766fe9SRichard Henderson 55361766fe9SRichard Henderson for (i = 0; i < ctx.ntemps; ++i) { 55461766fe9SRichard Henderson tcg_temp_free(ctx.temps[i]); 55561766fe9SRichard Henderson TCGV_UNUSED(ctx.temps[i]); 55661766fe9SRichard Henderson } 55761766fe9SRichard Henderson ctx.ntemps = 0; 55861766fe9SRichard Henderson 55961766fe9SRichard Henderson /* If we see non-linear instructions, exhaust instruction count, 56061766fe9SRichard Henderson or run out of buffer space, stop generation. */ 56161766fe9SRichard Henderson /* ??? The non-linear instruction restriction is purely due to 56261766fe9SRichard Henderson the debugging dump. Otherwise we *could* follow unconditional 56361766fe9SRichard Henderson branches within the same page. */ 56461766fe9SRichard Henderson if (ret == NO_EXIT 56561766fe9SRichard Henderson && (ctx.iaoq_b != ctx.iaoq_f + 4 56661766fe9SRichard Henderson || num_insns >= max_insns 56761766fe9SRichard Henderson || tcg_op_buf_full())) { 568*129e9cc3SRichard Henderson if (ctx.null_cond.c == TCG_COND_NEVER 569*129e9cc3SRichard Henderson || ctx.null_cond.c == TCG_COND_ALWAYS) { 570*129e9cc3SRichard Henderson nullify_set(&ctx, ctx.null_cond.c == TCG_COND_ALWAYS); 571*129e9cc3SRichard Henderson gen_goto_tb(&ctx, 0, ctx.iaoq_b, ctx.iaoq_n); 572*129e9cc3SRichard Henderson ret = EXIT_GOTO_TB; 573*129e9cc3SRichard Henderson } else { 57461766fe9SRichard Henderson ret = EXIT_IAQ_N_STALE; 57561766fe9SRichard Henderson } 576*129e9cc3SRichard Henderson } 57761766fe9SRichard Henderson 57861766fe9SRichard Henderson ctx.iaoq_f = ctx.iaoq_b; 57961766fe9SRichard Henderson ctx.iaoq_b = ctx.iaoq_n; 58061766fe9SRichard Henderson if (ret == EXIT_NORETURN 58161766fe9SRichard Henderson || ret == EXIT_GOTO_TB 58261766fe9SRichard Henderson || ret == EXIT_IAQ_N_UPDATED) { 58361766fe9SRichard Henderson break; 58461766fe9SRichard Henderson } 58561766fe9SRichard Henderson if (ctx.iaoq_f == -1) { 58661766fe9SRichard Henderson tcg_gen_mov_tl(cpu_iaoq_f, cpu_iaoq_b); 58761766fe9SRichard Henderson copy_iaoq_entry(cpu_iaoq_b, ctx.iaoq_n, ctx.iaoq_n_var); 588*129e9cc3SRichard Henderson nullify_save(&ctx); 58961766fe9SRichard Henderson ret = EXIT_IAQ_N_UPDATED; 59061766fe9SRichard Henderson break; 59161766fe9SRichard Henderson } 59261766fe9SRichard Henderson if (ctx.iaoq_b == -1) { 59361766fe9SRichard Henderson tcg_gen_mov_tl(cpu_iaoq_b, ctx.iaoq_n_var); 59461766fe9SRichard Henderson } 59561766fe9SRichard Henderson } while (ret == NO_EXIT); 59661766fe9SRichard Henderson 59761766fe9SRichard Henderson if (tb->cflags & CF_LAST_IO) { 59861766fe9SRichard Henderson gen_io_end(); 59961766fe9SRichard Henderson } 60061766fe9SRichard Henderson 60161766fe9SRichard Henderson switch (ret) { 60261766fe9SRichard Henderson case EXIT_GOTO_TB: 60361766fe9SRichard Henderson case EXIT_NORETURN: 60461766fe9SRichard Henderson break; 60561766fe9SRichard Henderson case EXIT_IAQ_N_STALE: 60661766fe9SRichard Henderson copy_iaoq_entry(cpu_iaoq_f, ctx.iaoq_f, cpu_iaoq_f); 60761766fe9SRichard Henderson copy_iaoq_entry(cpu_iaoq_b, ctx.iaoq_b, cpu_iaoq_b); 608*129e9cc3SRichard Henderson nullify_save(&ctx); 60961766fe9SRichard Henderson /* FALLTHRU */ 61061766fe9SRichard Henderson case EXIT_IAQ_N_UPDATED: 61161766fe9SRichard Henderson if (ctx.singlestep_enabled) { 61261766fe9SRichard Henderson gen_excp_1(EXCP_DEBUG); 61361766fe9SRichard Henderson } else { 61461766fe9SRichard Henderson tcg_gen_exit_tb(0); 61561766fe9SRichard Henderson } 61661766fe9SRichard Henderson break; 61761766fe9SRichard Henderson default: 61861766fe9SRichard Henderson abort(); 61961766fe9SRichard Henderson } 62061766fe9SRichard Henderson 62161766fe9SRichard Henderson gen_tb_end(tb, num_insns); 62261766fe9SRichard Henderson 62361766fe9SRichard Henderson tb->size = num_insns * 4; 62461766fe9SRichard Henderson tb->icount = num_insns; 62561766fe9SRichard Henderson 62661766fe9SRichard Henderson #ifdef DEBUG_DISAS 62761766fe9SRichard Henderson if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) 62861766fe9SRichard Henderson && qemu_log_in_addr_range(tb->pc)) { 62961766fe9SRichard Henderson qemu_log_lock(); 63061766fe9SRichard Henderson qemu_log("IN: %s\n", lookup_symbol(tb->pc)); 63161766fe9SRichard Henderson log_target_disas(cs, tb->pc, tb->size, 1); 63261766fe9SRichard Henderson qemu_log("\n"); 63361766fe9SRichard Henderson qemu_log_unlock(); 63461766fe9SRichard Henderson } 63561766fe9SRichard Henderson #endif 63661766fe9SRichard Henderson } 63761766fe9SRichard Henderson 63861766fe9SRichard Henderson void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb, 63961766fe9SRichard Henderson target_ulong *data) 64061766fe9SRichard Henderson { 64161766fe9SRichard Henderson env->iaoq_f = data[0]; 64261766fe9SRichard Henderson if (data[1] != -1) { 64361766fe9SRichard Henderson env->iaoq_b = data[1]; 64461766fe9SRichard Henderson } 64561766fe9SRichard Henderson /* Since we were executing the instruction at IAOQ_F, and took some 64661766fe9SRichard Henderson sort of action that provoked the cpu_restore_state, we can infer 64761766fe9SRichard Henderson that the instruction was not nullified. */ 64861766fe9SRichard Henderson env->psw_n = 0; 64961766fe9SRichard Henderson } 650