target/hppa: Introduce and use DisasIAQE for branch management

Wrap offset and space together in one structure, ensuring
that they're copied together as required.

Reviewed-by: Helge Deller <deller@gmx.de>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2024-03-21 13:56:44 -10:00
parent 1874e6c2fd
commit bc921866ce

View File

@ -42,21 +42,23 @@ typedef struct DisasCond {
TCGv_i64 a0, a1; TCGv_i64 a0, a1;
} DisasCond; } DisasCond;
typedef struct DisasIAQE {
/* IASQ; may be null for no change from TB. */
TCGv_i64 space;
/* IAOQ base; may be null for immediate absolute address. */
TCGv_i64 base;
/* IAOQ addend; absolute immedate address if base is null. */
int64_t disp;
} DisasIAQE;
typedef struct DisasContext { typedef struct DisasContext {
DisasContextBase base; DisasContextBase base;
CPUState *cs; CPUState *cs;
uint64_t iaoq_f; /* IAQ_Front, IAQ_Back. */
uint64_t iaoq_b; DisasIAQE iaq_f, iaq_b;
uint64_t iaoq_n; /* IAQ_Next, for jumps, otherwise null for simple advance. */
TCGv_i64 iaoq_n_var; DisasIAQE iaq_j, *iaq_n;
/*
* Null when IASQ_Back unchanged from IASQ_Front,
* or cpu_iasq_b, when IASQ_Back has been changed.
*/
TCGv_i64 iasq_b;
/* Null when IASQ_Next unchanged from IASQ_Back, or set by branch. */
TCGv_i64 iasq_n;
DisasCond null_cond; DisasCond null_cond;
TCGLabel *null_lab; TCGLabel *null_lab;
@ -602,49 +604,67 @@ static bool nullify_end(DisasContext *ctx)
return true; return true;
} }
static bool iaqe_variable(const DisasIAQE *e)
{
return e->base || e->space;
}
static DisasIAQE iaqe_incr(const DisasIAQE *e, int64_t disp)
{
return (DisasIAQE){
.space = e->space,
.base = e->base,
.disp = e->disp + disp,
};
}
static DisasIAQE iaqe_branchi(DisasContext *ctx, int64_t disp)
{
return (DisasIAQE){
.space = ctx->iaq_b.space,
.disp = ctx->iaq_f.disp + 8 + disp,
};
}
static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var)
{
return (DisasIAQE){
.space = ctx->iaq_b.space,
.base = var,
};
}
static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest,
uint64_t ival, TCGv_i64 vval) const DisasIAQE *src)
{ {
uint64_t mask = gva_offset_mask(ctx->tb_flags); uint64_t mask = gva_offset_mask(ctx->tb_flags);
if (ival != -1) { if (src->base == NULL) {
tcg_gen_movi_i64(dest, ival & mask); tcg_gen_movi_i64(dest, src->disp & mask);
return; } else if (src->disp == 0) {
} tcg_gen_andi_i64(dest, src->base, mask);
tcg_debug_assert(vval != NULL);
/*
* We know that the IAOQ is already properly masked.
* This optimization is primarily for "iaoq_f = iaoq_b".
*/
if (vval == cpu_iaoq_f || vval == cpu_iaoq_b) {
tcg_gen_mov_i64(dest, vval);
} else { } else {
tcg_gen_andi_i64(dest, vval, mask); tcg_gen_addi_i64(dest, src->base, src->disp);
tcg_gen_andi_i64(dest, dest, mask);
} }
} }
static void install_iaq_entries(DisasContext *ctx, static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f,
uint64_t bi, TCGv_i64 bv, TCGv_i64 bs, const DisasIAQE *b)
uint64_t ni, TCGv_i64 nv, TCGv_i64 ns)
{ {
copy_iaoq_entry(ctx, cpu_iaoq_f, bi, bv); DisasIAQE b_next;
/* Allow ni variable, with nv null, to indicate a trivial advance. */ if (b == NULL) {
if (ni != -1 || nv) { b_next = iaqe_incr(f, 4);
copy_iaoq_entry(ctx, cpu_iaoq_b, ni, nv); b = &b_next;
} else if (bi != -1) {
copy_iaoq_entry(ctx, cpu_iaoq_b, bi + 4, NULL);
} else {
tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_f, 4);
tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b,
gva_offset_mask(ctx->tb_flags));
} }
if (bs) { copy_iaoq_entry(ctx, cpu_iaoq_f, f);
tcg_gen_mov_i64(cpu_iasq_f, bs); copy_iaoq_entry(ctx, cpu_iaoq_b, b);
if (f->space) {
tcg_gen_mov_i64(cpu_iasq_f, f->space);
} }
if (ns || bs) { if (b->space || f->space) {
tcg_gen_mov_i64(cpu_iasq_b, ns ? ns : bs); tcg_gen_mov_i64(cpu_iasq_b, b->space ? : f->space);
} }
} }
@ -654,10 +674,11 @@ static void install_link(DisasContext *ctx, unsigned link, bool with_sr0)
if (!link) { if (!link) {
return; return;
} }
if (ctx->iaoq_b == -1) { if (ctx->iaq_b.base) {
tcg_gen_addi_i64(cpu_gr[link], cpu_iaoq_b, 4); tcg_gen_addi_i64(cpu_gr[link], ctx->iaq_b.base,
ctx->iaq_b.disp + 4);
} else { } else {
tcg_gen_movi_i64(cpu_gr[link], ctx->iaoq_b + 4); tcg_gen_movi_i64(cpu_gr[link], ctx->iaq_b.disp + 4);
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
if (with_sr0) { if (with_sr0) {
@ -666,11 +687,6 @@ static void install_link(DisasContext *ctx, unsigned link, bool with_sr0)
#endif #endif
} }
static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp)
{
return ctx->iaoq_f + disp + 8;
}
static void gen_excp_1(int exception) static void gen_excp_1(int exception)
{ {
gen_helper_excp(tcg_env, tcg_constant_i32(exception)); gen_helper_excp(tcg_env, tcg_constant_i32(exception));
@ -678,8 +694,7 @@ static void gen_excp_1(int exception)
static void gen_excp(DisasContext *ctx, int exception) static void gen_excp(DisasContext *ctx, int exception)
{ {
install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, NULL, install_iaq_entries(ctx, &ctx->iaq_f, &ctx->iaq_b);
ctx->iaoq_b, cpu_iaoq_b, NULL);
nullify_save(ctx); nullify_save(ctx);
gen_excp_1(exception); gen_excp_1(exception);
ctx->base.is_jmp = DISAS_NORETURN; ctx->base.is_jmp = DISAS_NORETURN;
@ -711,10 +726,12 @@ static bool gen_illegal(DisasContext *ctx)
} while (0) } while (0)
#endif #endif
static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) static bool use_goto_tb(DisasContext *ctx, const DisasIAQE *f,
const DisasIAQE *b)
{ {
return (bofs != -1 && nofs != -1 && return (!iaqe_variable(f) &&
translator_use_goto_tb(&ctx->base, bofs)); (b == NULL || !iaqe_variable(b)) &&
translator_use_goto_tb(&ctx->base, f->disp));
} }
/* If the next insn is to be nullified, and it's on the same page, /* If the next insn is to be nullified, and it's on the same page,
@ -724,20 +741,19 @@ static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs)
static bool use_nullify_skip(DisasContext *ctx) static bool use_nullify_skip(DisasContext *ctx)
{ {
return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE)
&& ctx->iaoq_b != -1 && !iaqe_variable(&ctx->iaq_b)
&& is_same_page(&ctx->base, ctx->iaoq_b)); && is_same_page(&ctx->base, ctx->iaq_b.disp));
} }
static void gen_goto_tb(DisasContext *ctx, int which, static void gen_goto_tb(DisasContext *ctx, int which,
uint64_t b, uint64_t n) const DisasIAQE *f, const DisasIAQE *b)
{ {
if (use_goto_tb(ctx, b, n)) { if (use_goto_tb(ctx, f, b)) {
tcg_gen_goto_tb(which); tcg_gen_goto_tb(which);
install_iaq_entries(ctx, b, NULL, NULL, n, NULL, NULL); install_iaq_entries(ctx, f, b);
tcg_gen_exit_tb(ctx->base.tb, which); tcg_gen_exit_tb(ctx->base.tb, which);
} else { } else {
install_iaq_entries(ctx, b, cpu_iaoq_b, ctx->iasq_b, install_iaq_entries(ctx, f, b);
n, ctx->iaoq_n_var, ctx->iasq_n);
tcg_gen_lookup_and_goto_ptr(); tcg_gen_lookup_and_goto_ptr();
} }
} }
@ -1818,37 +1834,35 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt,
static bool do_dbranch(DisasContext *ctx, int64_t disp, static bool do_dbranch(DisasContext *ctx, int64_t disp,
unsigned link, bool is_n) unsigned link, bool is_n)
{ {
uint64_t dest = iaoq_dest(ctx, disp); ctx->iaq_j = iaqe_branchi(ctx, disp);
if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) {
install_link(ctx, link, false); install_link(ctx, link, false);
if (is_n) { if (is_n) {
if (use_nullify_skip(ctx)) { if (use_nullify_skip(ctx)) {
nullify_set(ctx, 0); nullify_set(ctx, 0);
gen_goto_tb(ctx, 0, dest, dest + 4); gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL);
ctx->base.is_jmp = DISAS_NORETURN; ctx->base.is_jmp = DISAS_NORETURN;
return true; return true;
} }
ctx->null_cond.c = TCG_COND_ALWAYS; ctx->null_cond.c = TCG_COND_ALWAYS;
} }
ctx->iaoq_n = dest; ctx->iaq_n = &ctx->iaq_j;
ctx->iaoq_n_var = NULL;
} else { } else {
nullify_over(ctx); nullify_over(ctx);
install_link(ctx, link, false); install_link(ctx, link, false);
if (is_n && use_nullify_skip(ctx)) { if (is_n && use_nullify_skip(ctx)) {
nullify_set(ctx, 0); nullify_set(ctx, 0);
gen_goto_tb(ctx, 0, dest, dest + 4); gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL);
} else { } else {
nullify_set(ctx, is_n); nullify_set(ctx, is_n);
gen_goto_tb(ctx, 0, ctx->iaoq_b, dest); gen_goto_tb(ctx, 0, &ctx->iaq_b, &ctx->iaq_j);
} }
nullify_end(ctx); nullify_end(ctx);
nullify_set(ctx, 0); nullify_set(ctx, 0);
gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n); gen_goto_tb(ctx, 1, &ctx->iaq_b, NULL);
ctx->base.is_jmp = DISAS_NORETURN; ctx->base.is_jmp = DISAS_NORETURN;
} }
return true; return true;
@ -1859,7 +1873,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp,
static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n,
DisasCond *cond) DisasCond *cond)
{ {
uint64_t dest = iaoq_dest(ctx, disp); DisasIAQE next;
TCGLabel *taken = NULL; TCGLabel *taken = NULL;
TCGCond c = cond->c; TCGCond c = cond->c;
bool n; bool n;
@ -1879,26 +1893,29 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n,
n = is_n && disp < 0; n = is_n && disp < 0;
if (n && use_nullify_skip(ctx)) { if (n && use_nullify_skip(ctx)) {
nullify_set(ctx, 0); nullify_set(ctx, 0);
gen_goto_tb(ctx, 0, ctx->iaoq_n, ctx->iaoq_n + 4); next = iaqe_incr(&ctx->iaq_b, 4);
gen_goto_tb(ctx, 0, &next, NULL);
} else { } else {
if (!n && ctx->null_lab) { if (!n && ctx->null_lab) {
gen_set_label(ctx->null_lab); gen_set_label(ctx->null_lab);
ctx->null_lab = NULL; ctx->null_lab = NULL;
} }
nullify_set(ctx, n); nullify_set(ctx, n);
gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); gen_goto_tb(ctx, 0, &ctx->iaq_b, NULL);
} }
gen_set_label(taken); gen_set_label(taken);
/* Taken: Condition satisfied; nullify on forward branches. */ /* Taken: Condition satisfied; nullify on forward branches. */
n = is_n && disp >= 0; n = is_n && disp >= 0;
next = iaqe_branchi(ctx, disp);
if (n && use_nullify_skip(ctx)) { if (n && use_nullify_skip(ctx)) {
nullify_set(ctx, 0); nullify_set(ctx, 0);
gen_goto_tb(ctx, 1, dest, dest + 4); gen_goto_tb(ctx, 1, &next, NULL);
} else { } else {
nullify_set(ctx, n); nullify_set(ctx, n);
gen_goto_tb(ctx, 1, ctx->iaoq_b, dest); gen_goto_tb(ctx, 1, &ctx->iaq_b, &next);
} }
/* Not taken: the branch itself was nullified. */ /* Not taken: the branch itself was nullified. */
@ -1912,45 +1929,36 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n,
return true; return true;
} }
/* Emit an unconditional branch to an indirect target. This handles /*
nullification of the branch itself. */ * Emit an unconditional branch to an indirect target, in ctx->iaq_j.
static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 dspc, * This handles nullification of the branch itself.
unsigned link, bool with_sr0, bool is_n) */
static bool do_ibranch(DisasContext *ctx, unsigned link,
bool with_sr0, bool is_n)
{ {
TCGv_i64 next;
if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) {
next = tcg_temp_new_i64();
tcg_gen_mov_i64(next, dest);
install_link(ctx, link, with_sr0); install_link(ctx, link, with_sr0);
if (is_n) { if (is_n) {
if (use_nullify_skip(ctx)) { if (use_nullify_skip(ctx)) {
install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); install_iaq_entries(ctx, &ctx->iaq_j, NULL);
nullify_set(ctx, 0); nullify_set(ctx, 0);
ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
return true; return true;
} }
ctx->null_cond.c = TCG_COND_ALWAYS; ctx->null_cond.c = TCG_COND_ALWAYS;
} }
ctx->iaoq_n = -1; ctx->iaq_n = &ctx->iaq_j;
ctx->iaoq_n_var = next;
ctx->iasq_n = dspc;
return true; return true;
} }
nullify_over(ctx); nullify_over(ctx);
next = tcg_temp_new_i64();
tcg_gen_mov_i64(next, dest);
install_link(ctx, link, with_sr0); install_link(ctx, link, with_sr0);
if (is_n && use_nullify_skip(ctx)) { if (is_n && use_nullify_skip(ctx)) {
install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); install_iaq_entries(ctx, &ctx->iaq_j, NULL);
nullify_set(ctx, 0); nullify_set(ctx, 0);
} else { } else {
install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, install_iaq_entries(ctx, &ctx->iaq_b, &ctx->iaq_j);
-1, next, dspc);
nullify_set(ctx, is_n); nullify_set(ctx, is_n);
} }
@ -1997,8 +2005,6 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset)
aforementioned BE. */ aforementioned BE. */
static void do_page_zero(DisasContext *ctx) static void do_page_zero(DisasContext *ctx)
{ {
TCGv_i64 tmp;
/* If by some means we get here with PSW[N]=1, that implies that /* If by some means we get here with PSW[N]=1, that implies that
the B,GATE instruction would be skipped, and we'd fault on the the B,GATE instruction would be skipped, and we'd fault on the
next insn within the privileged page. */ next insn within the privileged page. */
@ -2018,11 +2024,11 @@ static void do_page_zero(DisasContext *ctx)
non-sequential instruction execution. Normally the PSW[B] bit non-sequential instruction execution. Normally the PSW[B] bit
detects this by disallowing the B,GATE instruction to execute detects this by disallowing the B,GATE instruction to execute
under such conditions. */ under such conditions. */
if (ctx->iaoq_b != ctx->iaoq_f + 4) { if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) {
goto do_sigill; goto do_sigill;
} }
switch (ctx->iaoq_f & -4) { switch (ctx->iaq_f.disp & -4) {
case 0x00: /* Null pointer call */ case 0x00: /* Null pointer call */
gen_excp_1(EXCP_IMP); gen_excp_1(EXCP_IMP);
ctx->base.is_jmp = DISAS_NORETURN; ctx->base.is_jmp = DISAS_NORETURN;
@ -2034,11 +2040,15 @@ static void do_page_zero(DisasContext *ctx)
break; break;
case 0xe0: /* SET_THREAD_POINTER */ case 0xe0: /* SET_THREAD_POINTER */
tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); {
tmp = tcg_temp_new_i64(); DisasIAQE next = { .base = tcg_temp_new_i64() };
tcg_gen_ori_i64(tmp, cpu_gr[31], 3);
install_iaq_entries(ctx, -1, tmp, NULL, -1, NULL, NULL); tcg_gen_st_i64(cpu_gr[26], tcg_env,
ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; offsetof(CPUHPPAState, cr[27]));
tcg_gen_ori_i64(next.base, cpu_gr[31], 3);
install_iaq_entries(ctx, &next, NULL);
ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
}
break; break;
case 0x100: /* SYSCALL */ case 0x100: /* SYSCALL */
@ -2077,11 +2087,12 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a)
static bool trans_mfia(DisasContext *ctx, arg_mfia *a) static bool trans_mfia(DisasContext *ctx, arg_mfia *a)
{ {
unsigned rt = a->t; TCGv_i64 dest = dest_gpr(ctx, a->t);
TCGv_i64 tmp = dest_gpr(ctx, rt);
tcg_gen_movi_i64(tmp, ctx->iaoq_f & ~3ULL);
save_gpr(ctx, rt, tmp);
copy_iaoq_entry(ctx, dest, &ctx->iaq_f);
tcg_gen_andi_i64(dest, dest, -4);
save_gpr(ctx, a->t, dest);
cond_free(&ctx->null_cond); cond_free(&ctx->null_cond);
return true; return true;
} }
@ -2781,8 +2792,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a)
nullify_over(ctx); nullify_over(ctx);
/* Advance the instruction queue. */ /* Advance the instruction queue. */
install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, install_iaq_entries(ctx, &ctx->iaq_b, NULL);
ctx->iaoq_n, ctx->iaoq_n_var, ctx->iasq_n);
nullify_set(ctx, 0); nullify_set(ctx, 0);
/* Tell the qemu main loop to halt until this cpu has work. */ /* Tell the qemu main loop to halt until this cpu has work. */
@ -3916,18 +3926,18 @@ static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a)
static bool trans_be(DisasContext *ctx, arg_be *a) static bool trans_be(DisasContext *ctx, arg_be *a)
{ {
TCGv_i64 dest = tcg_temp_new_i64();
TCGv_i64 space = NULL;
tcg_gen_addi_i64(dest, load_gpr(ctx, a->b), a->disp);
dest = do_ibranch_priv(ctx, dest);
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
space = tcg_temp_new_i64(); ctx->iaq_j.space = tcg_temp_new_i64();
load_spr(ctx, space, a->sp); load_spr(ctx, ctx->iaq_j.space, a->sp);
#endif #endif
return do_ibranch(ctx, dest, space, a->l, true, a->n); ctx->iaq_j.base = tcg_temp_new_i64();
ctx->iaq_j.disp = 0;
tcg_gen_addi_i64(ctx->iaq_j.base, load_gpr(ctx, a->b), a->disp);
ctx->iaq_j.base = do_ibranch_priv(ctx, ctx->iaq_j.base);
return do_ibranch(ctx, a->l, true, a->n);
} }
static bool trans_bl(DisasContext *ctx, arg_bl *a) static bool trans_bl(DisasContext *ctx, arg_bl *a)
@ -3937,7 +3947,7 @@ static bool trans_bl(DisasContext *ctx, arg_bl *a)
static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
{ {
uint64_t dest = iaoq_dest(ctx, a->disp); int64_t disp = a->disp;
nullify_over(ctx); nullify_over(ctx);
@ -3952,7 +3962,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
* b evil * b evil
* in which instructions at evil would run with increased privs. * in which instructions at evil would run with increased privs.
*/ */
if (ctx->iaoq_b == -1 || ctx->iaoq_b != ctx->iaoq_f + 4) { if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) {
return gen_illegal(ctx); return gen_illegal(ctx);
} }
@ -3970,10 +3980,11 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
} }
/* No change for non-gateway pages or for priv decrease. */ /* No change for non-gateway pages or for priv decrease. */
if (type >= 4 && type - 4 < ctx->privilege) { if (type >= 4 && type - 4 < ctx->privilege) {
dest = deposit64(dest, 0, 2, type - 4); disp -= ctx->privilege;
disp += type - 4;
} }
} else { } else {
dest &= -4; /* priv = 0 */ disp -= ctx->privilege; /* priv = 0 */
} }
#endif #endif
@ -3986,17 +3997,23 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
save_gpr(ctx, a->l, tmp); save_gpr(ctx, a->l, tmp);
} }
return do_dbranch(ctx, dest - iaoq_dest(ctx, 0), 0, a->n); return do_dbranch(ctx, disp, 0, a->n);
} }
static bool trans_blr(DisasContext *ctx, arg_blr *a) static bool trans_blr(DisasContext *ctx, arg_blr *a)
{ {
if (a->x) { if (a->x) {
TCGv_i64 tmp = tcg_temp_new_i64(); DisasIAQE next = iaqe_incr(&ctx->iaq_f, 8);
tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); TCGv_i64 t0 = tcg_temp_new_i64();
tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); TCGv_i64 t1 = tcg_temp_new_i64();
/* The computation here never changes privilege level. */ /* The computation here never changes privilege level. */
return do_ibranch(ctx, tmp, NULL, a->l, false, a->n); copy_iaoq_entry(ctx, t0, &next);
tcg_gen_shli_i64(t1, load_gpr(ctx, a->x), 3);
tcg_gen_add_i64(t0, t0, t1);
ctx->iaq_j = iaqe_next_absv(ctx, t0);
return do_ibranch(ctx, a->l, false, a->n);
} else { } else {
/* BLR R0,RX is a good way to load PC+8 into RX. */ /* BLR R0,RX is a good way to load PC+8 into RX. */
return do_dbranch(ctx, 0, a->l, a->n); return do_dbranch(ctx, 0, a->l, a->n);
@ -4015,20 +4032,22 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a)
tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b)); tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b));
} }
dest = do_ibranch_priv(ctx, dest); dest = do_ibranch_priv(ctx, dest);
return do_ibranch(ctx, dest, NULL, 0, false, a->n); ctx->iaq_j = iaqe_next_absv(ctx, dest);
return do_ibranch(ctx, 0, false, a->n);
} }
static bool trans_bve(DisasContext *ctx, arg_bve *a) static bool trans_bve(DisasContext *ctx, arg_bve *a)
{ {
TCGv_i64 b = load_gpr(ctx, a->b); TCGv_i64 b = load_gpr(ctx, a->b);
TCGv_i64 dest = do_ibranch_priv(ctx, b);
TCGv_i64 space = NULL;
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
space = space_select(ctx, 0, b); ctx->iaq_j.space = space_select(ctx, 0, b);
#endif #endif
ctx->iaq_j.base = do_ibranch_priv(ctx, b);
ctx->iaq_j.disp = 0;
return do_ibranch(ctx, dest, space, a->l, false, a->n); return do_ibranch(ctx, a->l, false, a->n);
} }
static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a) static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a)
@ -4600,9 +4619,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX);
ctx->mmu_idx = MMU_USER_IDX; ctx->mmu_idx = MMU_USER_IDX;
ctx->iaoq_f = ctx->base.pc_first | ctx->privilege; ctx->iaq_f.disp = ctx->base.pc_first | ctx->privilege;
ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege; ctx->iaq_b.disp = ctx->base.tb->cs_base | ctx->privilege;
ctx->iasq_b = NULL;
ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN);
#else #else
ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3;
@ -4615,9 +4633,13 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
uint64_t iasq_f = cs_base & ~0xffffffffull; uint64_t iasq_f = cs_base & ~0xffffffffull;
int32_t diff = cs_base; int32_t diff = cs_base;
ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; ctx->iaq_f.disp = (ctx->base.pc_first & ~iasq_f) + ctx->privilege;
ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); if (diff) {
ctx->iasq_b = (diff ? NULL : cpu_iasq_b); ctx->iaq_b.disp = ctx->iaq_f.disp + diff;
} else {
ctx->iaq_b.base = cpu_iaoq_b;
ctx->iaq_b.space = cpu_iasq_b;
}
#endif #endif
ctx->zero = tcg_constant_i64(0); ctx->zero = tcg_constant_i64(0);
@ -4645,7 +4667,10 @@ static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
{ {
DisasContext *ctx = container_of(dcbase, DisasContext, base); DisasContext *ctx = container_of(dcbase, DisasContext, base);
tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b, 0); tcg_debug_assert(!iaqe_variable(&ctx->iaq_f));
tcg_gen_insn_start(ctx->iaq_f.disp,
iaqe_variable(&ctx->iaq_b) ? -1 : ctx->iaq_b.disp,
0);
ctx->insn_start_updated = false; ctx->insn_start_updated = false;
} }
@ -4668,11 +4693,12 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
the page permissions for execute. */ the page permissions for execute. */
uint32_t insn = translator_ldl(env, &ctx->base, ctx->base.pc_next); uint32_t insn = translator_ldl(env, &ctx->base, ctx->base.pc_next);
/* Set up the IA queue for the next insn. /*
This will be overwritten by a branch. */ * Set up the IA queue for the next insn.
ctx->iasq_n = NULL; * This will be overwritten by a branch.
ctx->iaoq_n_var = NULL; */
ctx->iaoq_n = ctx->iaoq_b == -1 ? -1 : ctx->iaoq_b + 4; ctx->iaq_n = NULL;
memset(&ctx->iaq_j, 0, sizeof(ctx->iaq_j));
if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) {
ctx->null_cond.c = TCG_COND_NEVER; ctx->null_cond.c = TCG_COND_NEVER;
@ -4693,7 +4719,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
return; return;
} }
/* Note this also detects a priority change. */ /* Note this also detects a priority change. */
if (ctx->iaoq_b != ctx->iaoq_f + 4 || ctx->iasq_b) { if (iaqe_variable(&ctx->iaq_b)
|| ctx->iaq_b.disp != ctx->iaq_f.disp + 4) {
ctx->base.is_jmp = DISAS_IAQ_N_STALE; ctx->base.is_jmp = DISAS_IAQ_N_STALE;
return; return;
} }
@ -4702,20 +4729,25 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
* Advance the insn queue. * Advance the insn queue.
* The only exit now is DISAS_TOO_MANY from the translator loop. * The only exit now is DISAS_TOO_MANY from the translator loop.
*/ */
ctx->iaoq_f = ctx->iaoq_b; ctx->iaq_f.disp = ctx->iaq_b.disp;
ctx->iaoq_b = ctx->iaoq_n; if (!ctx->iaq_n) {
if (ctx->iaoq_b == -1) { ctx->iaq_b.disp += 4;
if (ctx->iaoq_n_var) { return;
copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var);
} else {
tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_b, 4);
tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b,
gva_offset_mask(ctx->tb_flags));
}
} }
if (ctx->iasq_n) { /*
tcg_gen_mov_i64(cpu_iasq_b, ctx->iasq_n); * If IAQ_Next is variable in any way, we need to copy into the
ctx->iasq_b = cpu_iasq_b; * IAQ_Back globals, in case the next insn raises an exception.
*/
if (ctx->iaq_n->base) {
copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaq_n);
ctx->iaq_b.base = cpu_iaoq_b;
ctx->iaq_b.disp = 0;
} else {
ctx->iaq_b.disp = ctx->iaq_n->disp;
}
if (ctx->iaq_n->space) {
tcg_gen_mov_i64(cpu_iasq_b, ctx->iaq_n->space);
ctx->iaq_b.space = cpu_iasq_b;
} }
} }
@ -4723,43 +4755,29 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
{ {
DisasContext *ctx = container_of(dcbase, DisasContext, base); DisasContext *ctx = container_of(dcbase, DisasContext, base);
DisasJumpType is_jmp = ctx->base.is_jmp; DisasJumpType is_jmp = ctx->base.is_jmp;
uint64_t fi, bi;
TCGv_i64 fv, bv;
TCGv_i64 fs, bs;
/* Assume the insn queue has not been advanced. */ /* Assume the insn queue has not been advanced. */
fi = ctx->iaoq_b; DisasIAQE *f = &ctx->iaq_b;
fv = cpu_iaoq_b; DisasIAQE *b = ctx->iaq_n;
fs = ctx->iasq_b;
bi = ctx->iaoq_n;
bv = ctx->iaoq_n_var;
bs = ctx->iasq_n;
switch (is_jmp) { switch (is_jmp) {
case DISAS_NORETURN: case DISAS_NORETURN:
break; break;
case DISAS_TOO_MANY: case DISAS_TOO_MANY:
/* The insn queue has not been advanced. */ /* The insn queue has not been advanced. */
bi = fi; f = &ctx->iaq_f;
bv = fv; b = &ctx->iaq_b;
bs = fs;
fi = ctx->iaoq_f;
fv = NULL;
fs = NULL;
/* FALLTHRU */ /* FALLTHRU */
case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE:
if (fs == NULL if (use_goto_tb(ctx, f, b)
&& bs == NULL
&& use_goto_tb(ctx, fi, bi)
&& (ctx->null_cond.c == TCG_COND_NEVER && (ctx->null_cond.c == TCG_COND_NEVER
|| ctx->null_cond.c == TCG_COND_ALWAYS)) { || ctx->null_cond.c == TCG_COND_ALWAYS)) {
nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS);
gen_goto_tb(ctx, 0, fi, bi); gen_goto_tb(ctx, 0, f, b);
break; break;
} }
/* FALLTHRU */ /* FALLTHRU */
case DISAS_IAQ_N_STALE_EXIT: case DISAS_IAQ_N_STALE_EXIT:
install_iaq_entries(ctx, fi, fv, fs, bi, bv, bs); install_iaq_entries(ctx, f, b);
nullify_save(ctx); nullify_save(ctx);
if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { if (is_jmp == DISAS_IAQ_N_STALE_EXIT) {
tcg_gen_exit_tb(NULL, 0); tcg_gen_exit_tb(NULL, 0);
@ -4815,6 +4833,6 @@ static const TranslatorOps hppa_tr_ops = {
void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
vaddr pc, void *host_pc) vaddr pc, void *host_pc)
{ {
DisasContext ctx; DisasContext ctx = { };
translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base); translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base);
} }