xref: /openbmc/qemu/target/hppa/translate.c (revision 129e9cc3a1792b66bd189838d58f7a2944602a82)
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