From b61603bfcb21a2dc4cec87a4ebd65916d1670058 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 16:53:57 -1000 Subject: [PATCH 01/43] target/hppa: Move cpu_get_tb_cpu_state out of line Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 42 ++++++++++++++++++++++++++++++++++++++++++ target/hppa/cpu.h | 43 ++----------------------------------------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 393a81988d..582036b31e 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -43,6 +43,48 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) return cpu->env.iaoq_f; } +void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + uint32_t flags = env->psw_n * PSW_N; + + /* TB lookup assumes that PC contains the complete virtual address. + If we leave space+offset separate, we'll get ITLB misses to an + incomplete virtual address. This also means that we must separate + out current cpu privilege from the low bits of IAOQ_F. */ +#ifdef CONFIG_USER_ONLY + *pc = env->iaoq_f & -4; + *cs_base = env->iaoq_b & -4; + flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; +#else + /* ??? E, T, H, L, B bits need to be here, when implemented. */ + flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); + flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; + + *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); + *cs_base = env->iasq_f; + + /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero + low 32-bits of CS_BASE. This will succeed for all direct branches, + which is the primary case we care about -- using goto_tb within a page. + Failure is indicated by a zero difference. */ + if (env->iasq_f == env->iasq_b) { + target_long diff = env->iaoq_b - env->iaoq_f; + if (diff == (int32_t)diff) { + *cs_base |= (uint32_t)diff; + } + } + if ((env->sr[4] == env->sr[5]) + & (env->sr[4] == env->sr[6]) + & (env->sr[4] == env->sr[7])) { + flags |= TB_FLAG_SR_SAME; + } +#endif + + *pflags = flags; +} + static void hppa_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index fb2e4c4a98..61f1353133 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -314,47 +314,8 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); #define TB_FLAG_PRIV_SHIFT 8 #define TB_FLAG_UNALIGN 0x400 -static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - uint32_t flags = env->psw_n * PSW_N; - - /* TB lookup assumes that PC contains the complete virtual address. - If we leave space+offset separate, we'll get ITLB misses to an - incomplete virtual address. This also means that we must separate - out current cpu privilege from the low bits of IAOQ_F. */ -#ifdef CONFIG_USER_ONLY - *pc = env->iaoq_f & -4; - *cs_base = env->iaoq_b & -4; - flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; -#else - /* ??? E, T, H, L, B bits need to be here, when implemented. */ - flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); - flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - - *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), - env->iaoq_f & -4); - *cs_base = env->iasq_f; - - /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero - low 32-bits of CS_BASE. This will succeed for all direct branches, - which is the primary case we care about -- using goto_tb within a page. - Failure is indicated by a zero difference. */ - if (env->iasq_f == env->iasq_b) { - target_long diff = env->iaoq_b - env->iaoq_f; - if (diff == (int32_t)diff) { - *cs_base |= (uint32_t)diff; - } - } - if ((env->sr[4] == env->sr[5]) - & (env->sr[4] == env->sr[6]) - & (env->sr[4] == env->sr[7])) { - flags |= TB_FLAG_SR_SAME; - } -#endif - - *pflags = flags; -} +void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags); target_ulong cpu_hppa_get_psw(CPUHPPAState *env); void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong); From 19da5d125842e56f115873a4af06d59559db9bb6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 19:09:05 -1000 Subject: [PATCH 02/43] target/hppa: Use hppa_form_gva_psw in hppa_cpu_get_pc This function is for log_pc(), which needs to produce a similar result to cpu_get_tb_cpu_state(). Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 582036b31e..be8c558014 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -38,9 +38,10 @@ static void hppa_cpu_set_pc(CPUState *cs, vaddr value) static vaddr hppa_cpu_get_pc(CPUState *cs) { - HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = cpu_env(cs); - return cpu->env.iaoq_f; + return hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); } void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, @@ -61,8 +62,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - *pc = hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), - env->iaoq_f & -4); + *pc = hppa_cpu_get_pc(env_cpu(env)); *cs_base = env->iasq_f; /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero From 4e31e68bb6e77a48e57aa3a27549313ea0f19f69 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Mar 2024 09:23:35 -1000 Subject: [PATCH 03/43] target/hppa: Move constant destination check into use_goto_tb Share this check between gen_goto_tb and hppa_tr_translate_insn. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 6d45611888..398803981c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -662,9 +662,10 @@ static bool gen_illegal(DisasContext *ctx) } while (0) #endif -static bool use_goto_tb(DisasContext *ctx, uint64_t dest) +static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) { - return translator_use_goto_tb(&ctx->base, dest); + return (bofs != -1 && nofs != -1 && + translator_use_goto_tb(&ctx->base, bofs)); } /* If the next insn is to be nullified, and it's on the same page, @@ -678,16 +679,16 @@ static bool use_nullify_skip(DisasContext *ctx) } static void gen_goto_tb(DisasContext *ctx, int which, - uint64_t f, uint64_t b) + uint64_t b, uint64_t n) { - if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { + if (use_goto_tb(ctx, b, n)) { tcg_gen_goto_tb(which); - copy_iaoq_entry(ctx, cpu_iaoq_f, f, NULL); - copy_iaoq_entry(ctx, cpu_iaoq_b, b, NULL); + copy_iaoq_entry(ctx, cpu_iaoq_f, b, NULL); + copy_iaoq_entry(ctx, cpu_iaoq_b, n, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, f, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, b, ctx->iaoq_n_var); + copy_iaoq_entry(ctx, cpu_iaoq_f, b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, n, ctx->iaoq_n_var); tcg_gen_lookup_and_goto_ptr(); } } @@ -4744,8 +4745,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Advance the insn queue. Note that this check also detects a priority change within the instruction queue. */ if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) { - if (ctx->iaoq_b != -1 && ctx->iaoq_n != -1 - && use_goto_tb(ctx, ctx->iaoq_b) + if (use_goto_tb(ctx, ctx->iaoq_b, ctx->iaoq_n) && (ctx->null_cond.c == TCG_COND_NEVER || ctx->null_cond.c == TCG_COND_ALWAYS)) { nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); From 2644f80ba5e0628af058072ed44b14e8d0bee115 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 20 Mar 2024 20:02:42 -1000 Subject: [PATCH 04/43] target/hppa: Pass displacement to do_dbranch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass a displacement instead of an absolute value. In trans_be, remove the user-only do_dbranch case. The branch we are attempting to optimize is to the zero page, which is perforce on a different page than the code currently executing, which means that we will *not* use a goto_tb. Use a plain indirect branch instead, which is what we got out of the attempted direct branch anyway. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 398803981c..4c42b518c5 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1766,9 +1766,11 @@ static bool do_fop_dedd(DisasContext *ctx, unsigned rt, /* Emit an unconditional branch to a direct target, which may or may not have already had nullification handled. */ -static bool do_dbranch(DisasContext *ctx, uint64_t dest, +static bool do_dbranch(DisasContext *ctx, int64_t disp, unsigned link, bool is_n) { + uint64_t dest = iaoq_dest(ctx, disp); + if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (link != 0) { copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); @@ -1815,10 +1817,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, /* Handle TRUE and NEVER as direct branches. */ if (c == TCG_COND_ALWAYS) { - return do_dbranch(ctx, dest, 0, is_n && disp >= 0); - } - if (c == TCG_COND_NEVER) { - return do_dbranch(ctx, ctx->iaoq_n, 0, is_n && disp < 0); + return do_dbranch(ctx, disp, 0, is_n && disp >= 0); } taken = gen_new_label(); @@ -3914,22 +3913,6 @@ static bool trans_be(DisasContext *ctx, arg_be *a) { TCGv_i64 tmp; -#ifdef CONFIG_USER_ONLY - /* ??? It seems like there should be a good way of using - "be disp(sr2, r0)", the canonical gateway entry mechanism - to our advantage. But that appears to be inconvenient to - manage along side branch delay slots. Therefore we handle - entry into the gateway page via absolute address. */ - /* Since we don't implement spaces, just branch. Do notice the special - case of "be disp(*,r0)" using a direct branch to disp, so that we can - goto_tb to the TB containing the syscall. */ - if (a->b == 0) { - return do_dbranch(ctx, a->disp, a->l, a->n); - } -#else - nullify_over(ctx); -#endif - tmp = tcg_temp_new_i64(); tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); @@ -3939,6 +3922,8 @@ static bool trans_be(DisasContext *ctx, arg_be *a) #else TCGv_i64 new_spc = tcg_temp_new_i64(); + nullify_over(ctx); + load_spr(ctx, new_spc, a->sp); if (a->l) { copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); @@ -3968,7 +3953,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) static bool trans_bl(DisasContext *ctx, arg_bl *a) { - return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n); + return do_dbranch(ctx, a->disp, a->l, a->n); } static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) @@ -4022,7 +4007,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) save_gpr(ctx, a->l, tmp); } - return do_dbranch(ctx, dest, 0, a->n); + return do_dbranch(ctx, dest - iaoq_dest(ctx, 0), 0, a->n); } static bool trans_blr(DisasContext *ctx, arg_blr *a) @@ -4035,7 +4020,7 @@ static bool trans_blr(DisasContext *ctx, arg_blr *a) return do_ibranch(ctx, tmp, a->l, a->n); } else { /* BLR R0,RX is a good way to load PC+8 into RX. */ - return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n); + return do_dbranch(ctx, 0, a->l, a->n); } } From d582c1faa3dcf6a87736383536b38335796fa214 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 14:33:28 -1000 Subject: [PATCH 05/43] target/hppa: Allow prior nullification in do_ibranch Simplify the function by not attempting a conditional move on the branch destination -- just use nullify_over normally. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 73 +++++++++++------------------------------ 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4c42b518c5..140dfb747a 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1871,17 +1871,15 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, unsigned link, bool is_n) { - TCGv_i64 a0, a1, next, tmp; - TCGCond c; + TCGv_i64 next; - assert(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); - if (ctx->null_cond.c == TCG_COND_NEVER) { if (link != 0) { copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); } - next = tcg_temp_new_i64(); - tcg_gen_mov_i64(next, dest); if (is_n) { if (use_nullify_skip(ctx)) { copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next); @@ -1895,60 +1893,29 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, } ctx->iaoq_n = -1; ctx->iaoq_n_var = next; - } else if (is_n && use_nullify_skip(ctx)) { - /* The (conditional) branch, B, nullifies the next insn, N, - and we're allowed to skip execution N (no single-step or - tracepoint in effect). Since the goto_ptr that we must use - for the indirect branch consumes no special resources, we - can (conditionally) skip B and continue execution. */ - /* The use_nullify_skip test implies we have a known control path. */ - tcg_debug_assert(ctx->iaoq_b != -1); - tcg_debug_assert(ctx->iaoq_n != -1); + return true; + } - /* We do have to handle the non-local temporary, DEST, before - branching. Since IOAQ_F is not really live at this point, we - can simply store DEST optimistically. Similarly with IAOQ_B. */ + nullify_over(ctx); + + if (is_n && use_nullify_skip(ctx)) { copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); next = tcg_temp_new_i64(); tcg_gen_addi_i64(next, dest, 4); copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); - - nullify_over(ctx); - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } - tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx); + nullify_set(ctx, 0); } else { - c = ctx->null_cond.c; - a0 = ctx->null_cond.a0; - a1 = ctx->null_cond.a1; - - tmp = tcg_temp_new_i64(); - next = tcg_temp_new_i64(); - - copy_iaoq_entry(ctx, tmp, ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_movcond_i64(c, next, a0, a1, tmp, dest); - ctx->iaoq_n = -1; - ctx->iaoq_n_var = next; - - if (link != 0) { - tcg_gen_movcond_i64(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); - } - - if (is_n) { - /* The branch nullifies the next insn, which means the state of N - after the branch is the inverse of the state of N that applied - to the branch. */ - tcg_gen_setcond_i64(tcg_invert_cond(c), cpu_psw_n, a0, a1); - cond_free(&ctx->null_cond); - ctx->null_cond = cond_make_n(); - ctx->psw_n_nonzero = true; - } else { - cond_free(&ctx->null_cond); - } + copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); + nullify_set(ctx, is_n); } - return true; + if (link != 0) { + copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + } + + tcg_gen_lookup_and_goto_ptr(); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); } /* Implement From f9b11bc2e75de806f189f7a5d1e63ba735ef4207 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 16:59:40 -1000 Subject: [PATCH 06/43] target/hppa: Use CF_BP_PAGE instead of cpu_breakpoint_test The generic tcg driver will have already checked for breakpoints. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 140dfb747a..d272be0e6e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -674,8 +674,9 @@ static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) executing a TB that merely branches to the next TB. */ static bool use_nullify_skip(DisasContext *ctx) { - return (((ctx->iaoq_b ^ ctx->iaoq_f) & TARGET_PAGE_MASK) == 0 - && !cpu_breakpoint_test(ctx->cs, ctx->iaoq_b, BP_ANY)); + return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) + && ctx->iaoq_b != -1 + && is_same_page(&ctx->base, ctx->iaoq_b)); } static void gen_goto_tb(DisasContext *ctx, int which, From 85e6cda0082c6e8107b9141bfcfb041f7f2a738e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 18:40:54 -1000 Subject: [PATCH 07/43] target/hppa: Add install_iaq_entries Instead of two separate cpu_iaoq_entry calls, use one call to update both IAQ_Front and IAQ_Back. Simplify with an argument combination that automatically handles a simple increment from Front to Back. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 64 +++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index d272be0e6e..08d5e2a4bc 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -617,6 +617,23 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, } } +static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, + uint64_t ni, TCGv_i64 nv) +{ + copy_iaoq_entry(ctx, cpu_iaoq_f, bi, bv); + + /* Allow ni variable, with nv null, to indicate a trivial advance. */ + if (ni != -1 || nv) { + copy_iaoq_entry(ctx, cpu_iaoq_b, ni, nv); + } 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)); + } +} + static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp) { return ctx->iaoq_f + disp + 8; @@ -629,8 +646,7 @@ static void gen_excp_1(int exception) static void gen_excp(DisasContext *ctx, int exception) { - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -684,12 +700,10 @@ static void gen_goto_tb(DisasContext *ctx, int which, { if (use_goto_tb(ctx, b, n)) { tcg_gen_goto_tb(which); - copy_iaoq_entry(ctx, cpu_iaoq_f, b, NULL); - copy_iaoq_entry(ctx, cpu_iaoq_b, n, NULL); + install_iaq_entries(ctx, b, NULL, n, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, b, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, n, ctx->iaoq_n_var); + install_iaq_entries(ctx, b, cpu_iaoq_b, n, ctx->iaoq_n_var); tcg_gen_lookup_and_goto_ptr(); } } @@ -1883,9 +1897,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, } if (is_n) { if (use_nullify_skip(ctx)) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, next); - tcg_gen_addi_i64(next, next, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); + install_iaq_entries(ctx, -1, next, -1, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1900,14 +1912,10 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, nullify_over(ctx); if (is_n && use_nullify_skip(ctx)) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, dest); - next = tcg_temp_new_i64(); - tcg_gen_addi_i64(next, dest, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, next); + install_iaq_entries(ctx, -1, dest, -1, NULL); nullify_set(ctx, 0); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); nullify_set(ctx, is_n); } if (link != 0) { @@ -1998,9 +2006,7 @@ static void do_page_zero(DisasContext *ctx) tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); tmp = tcg_temp_new_i64(); tcg_gen_ori_i64(tmp, cpu_gr[31], 3); - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); - tcg_gen_addi_i64(tmp, tmp, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); + install_iaq_entries(ctx, -1, tmp, -1, NULL); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; break; @@ -2744,8 +2750,8 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) nullify_over(ctx); /* Advance the instruction queue. */ - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, + ctx->iaoq_n, ctx->iaoq_n_var); nullify_set(ctx, 0); /* Tell the qemu main loop to halt until this cpu has work. */ @@ -3898,18 +3904,15 @@ static bool trans_be(DisasContext *ctx, arg_be *a) tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); } if (a->n && use_nullify_skip(ctx)) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, tmp); - tcg_gen_addi_i64(tmp, tmp, 4); - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); + install_iaq_entries(ctx, -1, tmp, -1, NULL); tcg_gen_mov_i64(cpu_iasq_f, new_spc); tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); nullify_set(ctx, 0); } else { - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, tmp); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, tmp); tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, a->n); } @@ -4018,11 +4021,10 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) nullify_over(ctx); dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, dest); tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); if (a->l) { copy_iaoq_entry(ctx, cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); @@ -4721,8 +4723,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: if (ctx->iaoq_f == -1) { - copy_iaoq_entry(ctx, cpu_iaoq_f, -1, cpu_iaoq_b); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + install_iaq_entries(ctx, -1, cpu_iaoq_b, + ctx->iaoq_n, ctx->iaoq_n_var); #ifndef CONFIG_USER_ONLY tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); #endif @@ -4751,8 +4753,8 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_TOO_MANY: case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE_EXIT: - copy_iaoq_entry(ctx, cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); - copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, + ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); /* FALLTHRU */ case DISAS_IAQ_N_UPDATED: From 43541db0c40dad6291ce1dcb76b7729f55704893 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 20:30:12 -1000 Subject: [PATCH 08/43] target/hppa: Add install_link Add a common routine for writing the return address. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 55 ++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 08d5e2a4bc..220665d98e 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -634,6 +634,24 @@ static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, } } +static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) +{ + tcg_debug_assert(ctx->null_cond.c == TCG_COND_NEVER); + if (!link) { + return; + } + if (ctx->iaoq_b == -1) { + tcg_gen_addi_i64(cpu_gr[link], cpu_iaoq_b, 4); + } else { + tcg_gen_movi_i64(cpu_gr[link], ctx->iaoq_b + 4); + } +#ifndef CONFIG_USER_ONLY + if (with_sr0) { + tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); + } +#endif +} + static inline uint64_t iaoq_dest(DisasContext *ctx, int64_t disp) { return ctx->iaoq_f + disp + 8; @@ -1787,9 +1805,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, uint64_t dest = iaoq_dest(ctx, disp); if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } + install_link(ctx, link, false); ctx->iaoq_n = dest; if (is_n) { ctx->null_cond.c = TCG_COND_ALWAYS; @@ -1797,10 +1813,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, } else { nullify_over(ctx); - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } - + install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); gen_goto_tb(ctx, 0, dest, dest + 4); @@ -1892,9 +1905,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, next = tcg_temp_new_i64(); tcg_gen_mov_i64(next, dest); - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } + install_link(ctx, link, false); if (is_n) { if (use_nullify_skip(ctx)) { install_iaq_entries(ctx, -1, next, -1, NULL); @@ -1911,16 +1922,17 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, nullify_over(ctx); + next = tcg_temp_new_i64(); + tcg_gen_mov_i64(next, dest); + + install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, dest, -1, NULL); + install_iaq_entries(ctx, -1, next, -1, NULL); nullify_set(ctx, 0); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, next); nullify_set(ctx, is_n); } - if (link != 0) { - copy_iaoq_entry(ctx, cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; @@ -3899,10 +3911,7 @@ static bool trans_be(DisasContext *ctx, arg_be *a) nullify_over(ctx); load_spr(ctx, new_spc, a->sp); - if (a->l) { - copy_iaoq_entry(ctx, cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); - } + install_link(ctx, a->l, true); if (a->n && use_nullify_skip(ctx)) { install_iaq_entries(ctx, -1, tmp, -1, NULL); tcg_gen_mov_i64(cpu_iasq_f, new_spc); @@ -4019,16 +4028,16 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) return do_ibranch(ctx, dest, a->l, a->n); #else nullify_over(ctx); - dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); + dest = tcg_temp_new_i64(); + tcg_gen_mov_i64(dest, load_gpr(ctx, a->b)); + dest = do_ibranch_priv(ctx, dest); + install_link(ctx, a->l, false); install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); if (ctx->iaoq_b == -1) { tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); } tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); - if (a->l) { - copy_iaoq_entry(ctx, cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); - } nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; From 0dcd6640e49c3cbd534e5a1d39338bcd6e691e18 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 15:38:32 -1000 Subject: [PATCH 09/43] target/hppa: Delay computation of IAQ_Next We no longer have to allocate a temp and perform an addition before translation of the rest of the insn. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 220665d98e..9189e0350b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1807,6 +1807,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { install_link(ctx, link, false); ctx->iaoq_n = dest; + ctx->iaoq_n_var = NULL; if (is_n) { ctx->null_cond.c = TCG_COND_ALWAYS; } @@ -1863,11 +1864,6 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, ctx->null_lab = NULL; } nullify_set(ctx, n); - if (ctx->iaoq_n == -1) { - /* The temporary iaoq_n_var died at the branch above. - Regenerate it here instead of saving it. */ - tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); - } gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); } @@ -4631,8 +4627,6 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); #endif - ctx->iaoq_n = -1; - ctx->iaoq_n_var = NULL; ctx->zero = tcg_constant_i64(0); @@ -4684,14 +4678,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Set up the IA queue for the next insn. This will be overwritten by a branch. */ - if (ctx->iaoq_b == -1) { - ctx->iaoq_n = -1; - ctx->iaoq_n_var = tcg_temp_new_i64(); - tcg_gen_addi_i64(ctx->iaoq_n_var, cpu_iaoq_b, 4); - } else { - ctx->iaoq_n = ctx->iaoq_b + 4; - ctx->iaoq_n_var = NULL; - } + ctx->iaoq_n_var = NULL; + ctx->iaoq_n = ctx->iaoq_b == -1 ? -1 : ctx->iaoq_b + 4; if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { ctx->null_cond.c = TCG_COND_NEVER; @@ -4742,7 +4730,13 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ? DISAS_EXIT : DISAS_IAQ_N_UPDATED); } else if (ctx->iaoq_b == -1) { - copy_iaoq_entry(ctx, cpu_iaoq_b, -1, ctx->iaoq_n_var); + if (ctx->iaoq_n_var) { + 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)); + } } break; From d08ad0e0f08d3b6561be83beb992b9835aa47895 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 13:07:06 -0700 Subject: [PATCH 10/43] target/hppa: Skip nullified insns in unconditional dbranch path Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 9189e0350b..6c5efb0023 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1806,11 +1806,17 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { install_link(ctx, link, false); - ctx->iaoq_n = dest; - ctx->iaoq_n_var = NULL; if (is_n) { + if (use_nullify_skip(ctx)) { + nullify_set(ctx, 0); + gen_goto_tb(ctx, 0, dest, dest + 4); + ctx->base.is_jmp = DISAS_NORETURN; + return true; + } ctx->null_cond.c = TCG_COND_ALWAYS; } + ctx->iaoq_n = dest; + ctx->iaoq_n_var = NULL; } else { nullify_over(ctx); From dbdccbdf8167881be9974fc582b86ffc6f69ffa1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 20:13:40 -1000 Subject: [PATCH 11/43] target/hppa: Simplify TB end Minimize the amount of code in hppa_tr_translate_insn advancing the insn queue for the next insn. Move the goto_tb path to hppa_tr_tb_stop. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 109 +++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 6c5efb0023..b79c44bd49 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4700,54 +4700,31 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) } } - /* Advance the insn queue. Note that this check also detects - a priority change within the instruction queue. */ - if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) { - if (use_goto_tb(ctx, ctx->iaoq_b, ctx->iaoq_n) - && (ctx->null_cond.c == TCG_COND_NEVER - || ctx->null_cond.c == TCG_COND_ALWAYS)) { - nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); - gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); - ctx->base.is_jmp = ret = DISAS_NORETURN; - } else { - ctx->base.is_jmp = ret = DISAS_IAQ_N_STALE; - } + /* If the TranslationBlock must end, do so. */ + ctx->base.pc_next += 4; + if (ret != DISAS_NEXT) { + return; } + /* Note this also detects a priority change. */ + if (ctx->iaoq_b != ctx->iaoq_f + 4) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + return; + } + + /* + * Advance the insn queue. + * The only exit now is DISAS_TOO_MANY from the translator loop. + */ ctx->iaoq_f = ctx->iaoq_b; ctx->iaoq_b = ctx->iaoq_n; - ctx->base.pc_next += 4; - - switch (ret) { - case DISAS_NORETURN: - case DISAS_IAQ_N_UPDATED: - break; - - case DISAS_NEXT: - case DISAS_IAQ_N_STALE: - case DISAS_IAQ_N_STALE_EXIT: - if (ctx->iaoq_f == -1) { - install_iaq_entries(ctx, -1, cpu_iaoq_b, - ctx->iaoq_n, ctx->iaoq_n_var); -#ifndef CONFIG_USER_ONLY - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); -#endif - nullify_save(ctx); - ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT - ? DISAS_EXIT - : DISAS_IAQ_N_UPDATED); - } else if (ctx->iaoq_b == -1) { - if (ctx->iaoq_n_var) { - 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->iaoq_b == -1) { + if (ctx->iaoq_n_var) { + 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)); } - break; - - default: - g_assert_not_reached(); } } @@ -4755,23 +4732,51 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); DisasJumpType is_jmp = ctx->base.is_jmp; + uint64_t fi, bi; + TCGv_i64 fv, bv; + TCGv_i64 fs; + + /* Assume the insn queue has not been advanced. */ + fi = ctx->iaoq_b; + fv = cpu_iaoq_b; + fs = fi == -1 ? cpu_iasq_b : NULL; + bi = ctx->iaoq_n; + bv = ctx->iaoq_n_var; switch (is_jmp) { case DISAS_NORETURN: break; case DISAS_TOO_MANY: - case DISAS_IAQ_N_STALE: - case DISAS_IAQ_N_STALE_EXIT: - install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, - ctx->iaoq_b, cpu_iaoq_b); - nullify_save(ctx); + /* The insn queue has not been advanced. */ + bi = fi; + bv = fv; + fi = ctx->iaoq_f; + fv = NULL; + fs = NULL; /* FALLTHRU */ - case DISAS_IAQ_N_UPDATED: - if (is_jmp != DISAS_IAQ_N_STALE_EXIT) { - tcg_gen_lookup_and_goto_ptr(); + case DISAS_IAQ_N_STALE: + if (use_goto_tb(ctx, fi, bi) + && (ctx->null_cond.c == TCG_COND_NEVER + || ctx->null_cond.c == TCG_COND_ALWAYS)) { + nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); + gen_goto_tb(ctx, 0, fi, bi); break; } /* FALLTHRU */ + case DISAS_IAQ_N_STALE_EXIT: + install_iaq_entries(ctx, fi, fv, bi, bv); + if (fs) { + tcg_gen_mov_i64(cpu_iasq_f, fs); + } + nullify_save(ctx); + if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { + tcg_gen_exit_tb(NULL, 0); + break; + } + /* FALLTHRU */ + case DISAS_IAQ_N_UPDATED: + tcg_gen_lookup_and_goto_ptr(); + break; case DISAS_EXIT: tcg_gen_exit_tb(NULL, 0); break; From 142faf5fab337b809b84890579157c9064668011 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 20:45:21 -1000 Subject: [PATCH 12/43] target/hppa: Add IASQ entries to DisasContext Add variable to track space changes to IAQ. So far, no such changes are introduced, but the new checks vs ctx->iasq_b may eliminate an unnecessary copy to cpu_iasq_f with e.g. BLR. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b79c44bd49..b4e384baa3 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -50,6 +50,13 @@ typedef struct DisasContext { uint64_t iaoq_b; uint64_t iaoq_n; TCGv_i64 iaoq_n_var; + /* + * 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; TCGLabel *null_lab; @@ -3917,12 +3924,12 @@ static bool trans_be(DisasContext *ctx, arg_be *a) if (a->n && use_nullify_skip(ctx)) { install_iaq_entries(ctx, -1, tmp, -1, NULL); tcg_gen_mov_i64(cpu_iasq_f, new_spc); - tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); + tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, 0); } else { install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, tmp); - if (ctx->iaoq_b == -1) { - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); + if (ctx->iasq_b) { + tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); } tcg_gen_mov_i64(cpu_iasq_b, new_spc); nullify_set(ctx, a->n); @@ -4036,8 +4043,8 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) install_link(ctx, a->l, false); install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); - if (ctx->iaoq_b == -1) { - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); + if (ctx->iasq_b) { + tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); } tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); nullify_set(ctx, a->n); @@ -4618,6 +4625,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->mmu_idx = MMU_USER_IDX; ctx->iaoq_f = ctx->base.pc_first | ctx->privilege; ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege; + ctx->iasq_b = NULL; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; @@ -4632,6 +4640,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); + ctx->iasq_b = (diff ? NULL : cpu_iasq_b); #endif ctx->zero = tcg_constant_i64(0); @@ -4684,6 +4693,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Set up the IA queue for the next insn. This will be overwritten by a branch. */ + ctx->iasq_n = NULL; ctx->iaoq_n_var = NULL; ctx->iaoq_n = ctx->iaoq_b == -1 ? -1 : ctx->iaoq_b + 4; @@ -4706,7 +4716,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) return; } /* Note this also detects a priority change. */ - if (ctx->iaoq_b != ctx->iaoq_f + 4) { + if (ctx->iaoq_b != ctx->iaoq_f + 4 || ctx->iasq_b) { ctx->base.is_jmp = DISAS_IAQ_N_STALE; return; } @@ -4726,6 +4736,10 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) gva_offset_mask(ctx->tb_flags)); } } + if (ctx->iasq_n) { + tcg_gen_mov_i64(cpu_iasq_b, ctx->iasq_n); + ctx->iasq_b = cpu_iasq_b; + } } static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) @@ -4734,14 +4748,15 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) DisasJumpType is_jmp = ctx->base.is_jmp; uint64_t fi, bi; TCGv_i64 fv, bv; - TCGv_i64 fs; + TCGv_i64 fs, bs; /* Assume the insn queue has not been advanced. */ fi = ctx->iaoq_b; fv = cpu_iaoq_b; - fs = fi == -1 ? cpu_iasq_b : NULL; + fs = ctx->iasq_b; bi = ctx->iaoq_n; bv = ctx->iaoq_n_var; + bs = ctx->iasq_n; switch (is_jmp) { case DISAS_NORETURN: @@ -4750,12 +4765,15 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) /* The insn queue has not been advanced. */ bi = fi; bv = fv; + bs = fs; fi = ctx->iaoq_f; fv = NULL; fs = NULL; /* FALLTHRU */ case DISAS_IAQ_N_STALE: - if (use_goto_tb(ctx, fi, bi) + if (fs == NULL + && bs == NULL + && use_goto_tb(ctx, fi, bi) && (ctx->null_cond.c == TCG_COND_NEVER || ctx->null_cond.c == TCG_COND_ALWAYS)) { nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); @@ -4768,6 +4786,9 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) if (fs) { tcg_gen_mov_i64(cpu_iasq_f, fs); } + if (bs) { + tcg_gen_mov_i64(cpu_iasq_b, bs); + } nullify_save(ctx); if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { tcg_gen_exit_tb(NULL, 0); From 588deeda6e7cc2c401ca3cbf22383f019a69ec14 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 21:04:24 -1000 Subject: [PATCH 13/43] target/hppa: Add space arguments to install_iaq_entries Move space assighments to a central location. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 58 +++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b4e384baa3..eed0f92db4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -624,8 +624,9 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, } } -static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, - uint64_t ni, TCGv_i64 nv) +static void install_iaq_entries(DisasContext *ctx, + uint64_t bi, TCGv_i64 bv, TCGv_i64 bs, + uint64_t ni, TCGv_i64 nv, TCGv_i64 ns) { copy_iaoq_entry(ctx, cpu_iaoq_f, bi, bv); @@ -639,6 +640,12 @@ static void install_iaq_entries(DisasContext *ctx, uint64_t bi, TCGv_i64 bv, tcg_gen_andi_i64(cpu_iaoq_b, cpu_iaoq_b, gva_offset_mask(ctx->tb_flags)); } + if (bs) { + tcg_gen_mov_i64(cpu_iasq_f, bs); + } + if (ns || bs) { + tcg_gen_mov_i64(cpu_iasq_b, ns ? ns : bs); + } } static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) @@ -671,7 +678,8 @@ static void gen_excp_1(int exception) static void gen_excp(DisasContext *ctx, int exception) { - install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, NULL, + ctx->iaoq_b, cpu_iaoq_b, NULL); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -725,10 +733,11 @@ static void gen_goto_tb(DisasContext *ctx, int which, { if (use_goto_tb(ctx, b, n)) { tcg_gen_goto_tb(which); - install_iaq_entries(ctx, b, NULL, n, NULL); + install_iaq_entries(ctx, b, NULL, NULL, n, NULL, NULL); tcg_gen_exit_tb(ctx->base.tb, which); } else { - install_iaq_entries(ctx, b, cpu_iaoq_b, n, ctx->iaoq_n_var); + install_iaq_entries(ctx, b, cpu_iaoq_b, ctx->iasq_b, + n, ctx->iaoq_n_var, ctx->iasq_n); tcg_gen_lookup_and_goto_ptr(); } } @@ -1917,7 +1926,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, install_link(ctx, link, false); if (is_n) { if (use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, -1, NULL); + install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1936,10 +1945,11 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, -1, NULL); + install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); nullify_set(ctx, 0); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, next); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + -1, next, NULL); nullify_set(ctx, is_n); } @@ -2027,7 +2037,7 @@ static void do_page_zero(DisasContext *ctx) tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); tmp = tcg_temp_new_i64(); tcg_gen_ori_i64(tmp, cpu_gr[31], 3); - install_iaq_entries(ctx, -1, tmp, -1, NULL); + install_iaq_entries(ctx, -1, tmp, NULL, -1, NULL, NULL); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; break; @@ -2771,8 +2781,8 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) nullify_over(ctx); /* Advance the instruction queue. */ - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, - ctx->iaoq_n, ctx->iaoq_n_var); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + ctx->iaoq_n, ctx->iaoq_n_var, ctx->iasq_n); nullify_set(ctx, 0); /* Tell the qemu main loop to halt until this cpu has work. */ @@ -3922,16 +3932,11 @@ static bool trans_be(DisasContext *ctx, arg_be *a) load_spr(ctx, new_spc, a->sp); install_link(ctx, a->l, true); if (a->n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, tmp, -1, NULL); - tcg_gen_mov_i64(cpu_iasq_f, new_spc); - tcg_gen_mov_i64(cpu_iasq_b, new_spc); + install_iaq_entries(ctx, -1, tmp, new_spc, -1, NULL, new_spc); nullify_set(ctx, 0); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, tmp); - if (ctx->iasq_b) { - tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); - } - tcg_gen_mov_i64(cpu_iasq_b, new_spc); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + -1, tmp, new_spc); nullify_set(ctx, a->n); } tcg_gen_lookup_and_goto_ptr(); @@ -4042,11 +4047,8 @@ static bool trans_bve(DisasContext *ctx, arg_bve *a) dest = do_ibranch_priv(ctx, dest); install_link(ctx, a->l, false); - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, -1, dest); - if (ctx->iasq_b) { - tcg_gen_mov_i64(cpu_iasq_f, ctx->iasq_b); - } - tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); + install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, + -1, dest, space_select(ctx, 0, dest)); nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); ctx->base.is_jmp = DISAS_NORETURN; @@ -4782,13 +4784,7 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } /* FALLTHRU */ case DISAS_IAQ_N_STALE_EXIT: - install_iaq_entries(ctx, fi, fv, bi, bv); - if (fs) { - tcg_gen_mov_i64(cpu_iasq_f, fs); - } - if (bs) { - tcg_gen_mov_i64(cpu_iasq_b, bs); - } + install_iaq_entries(ctx, fi, fv, fs, bi, bv, bs); nullify_save(ctx); if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { tcg_gen_exit_tb(NULL, 0); From 019f41591d95ffd7f1d49ace4626aec2cbec19d8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 22 Mar 2024 21:29:47 -1000 Subject: [PATCH 14/43] target/hppa: Add space argument to do_ibranch This allows unification of BE, BLR, BV, BVE with a common helper. Since we can now track space with IAQ_Next, we can now let the TranslationBlock continue across the delay slot with BE, BVE. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 76 ++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index eed0f92db4..1758c6e1d4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1914,8 +1914,8 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, /* Emit an unconditional branch to an indirect target. This handles nullification of the branch itself. */ -static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, - unsigned link, bool is_n) +static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 dspc, + unsigned link, bool with_sr0, bool is_n) { TCGv_i64 next; @@ -1923,10 +1923,10 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, next = tcg_temp_new_i64(); tcg_gen_mov_i64(next, dest); - install_link(ctx, link, false); + install_link(ctx, link, with_sr0); if (is_n) { if (use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); + install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; @@ -1935,6 +1935,7 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, } ctx->iaoq_n = -1; ctx->iaoq_n_var = next; + ctx->iasq_n = dspc; return true; } @@ -1943,13 +1944,13 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, next = tcg_temp_new_i64(); tcg_gen_mov_i64(next, dest); - install_link(ctx, link, false); + install_link(ctx, link, with_sr0); if (is_n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, next, NULL, -1, NULL, NULL); + install_iaq_entries(ctx, -1, next, dspc, -1, NULL, NULL); nullify_set(ctx, 0); } else { install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, next, NULL); + -1, next, dspc); nullify_set(ctx, is_n); } @@ -3916,33 +3917,18 @@ static bool trans_depi_sar(DisasContext *ctx, arg_depi_sar *a) static bool trans_be(DisasContext *ctx, arg_be *a) { - TCGv_i64 tmp; + TCGv_i64 dest = tcg_temp_new_i64(); + TCGv_i64 space = NULL; - tmp = tcg_temp_new_i64(); - tcg_gen_addi_i64(tmp, load_gpr(ctx, a->b), a->disp); - tmp = do_ibranch_priv(ctx, tmp); + tcg_gen_addi_i64(dest, load_gpr(ctx, a->b), a->disp); + dest = do_ibranch_priv(ctx, dest); -#ifdef CONFIG_USER_ONLY - return do_ibranch(ctx, tmp, a->l, a->n); -#else - TCGv_i64 new_spc = tcg_temp_new_i64(); - - nullify_over(ctx); - - load_spr(ctx, new_spc, a->sp); - install_link(ctx, a->l, true); - if (a->n && use_nullify_skip(ctx)) { - install_iaq_entries(ctx, -1, tmp, new_spc, -1, NULL, new_spc); - nullify_set(ctx, 0); - } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, tmp, new_spc); - nullify_set(ctx, a->n); - } - tcg_gen_lookup_and_goto_ptr(); - ctx->base.is_jmp = DISAS_NORETURN; - return nullify_end(ctx); +#ifndef CONFIG_USER_ONLY + space = tcg_temp_new_i64(); + load_spr(ctx, space, a->sp); #endif + + return do_ibranch(ctx, dest, space, a->l, true, a->n); } static bool trans_bl(DisasContext *ctx, arg_bl *a) @@ -4011,7 +3997,7 @@ static bool trans_blr(DisasContext *ctx, arg_blr *a) tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); /* The computation here never changes privilege level. */ - return do_ibranch(ctx, tmp, a->l, a->n); + return do_ibranch(ctx, tmp, NULL, a->l, false, a->n); } else { /* BLR R0,RX is a good way to load PC+8 into RX. */ return do_dbranch(ctx, 0, a->l, a->n); @@ -4030,30 +4016,20 @@ static bool trans_bv(DisasContext *ctx, arg_bv *a) tcg_gen_add_i64(dest, dest, load_gpr(ctx, a->b)); } dest = do_ibranch_priv(ctx, dest); - return do_ibranch(ctx, dest, 0, a->n); + return do_ibranch(ctx, dest, NULL, 0, false, a->n); } static bool trans_bve(DisasContext *ctx, arg_bve *a) { - TCGv_i64 dest; + TCGv_i64 b = load_gpr(ctx, a->b); + TCGv_i64 dest = do_ibranch_priv(ctx, b); + TCGv_i64 space = NULL; -#ifdef CONFIG_USER_ONLY - dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); - return do_ibranch(ctx, dest, a->l, a->n); -#else - nullify_over(ctx); - dest = tcg_temp_new_i64(); - tcg_gen_mov_i64(dest, load_gpr(ctx, a->b)); - dest = do_ibranch_priv(ctx, dest); - - install_link(ctx, a->l, false); - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, dest, space_select(ctx, 0, dest)); - nullify_set(ctx, a->n); - tcg_gen_lookup_and_goto_ptr(); - ctx->base.is_jmp = DISAS_NORETURN; - return nullify_end(ctx); +#ifndef CONFIG_USER_ONLY + space = space_select(ctx, 0, b); #endif + + return do_ibranch(ctx, dest, space, a->l, false, a->n); } static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a) From 0bb0202962a5f77d1e4cac4533628bb7a566de54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 12:53:06 -1000 Subject: [PATCH 15/43] target/hppa: Use umax in do_ibranch_priv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using umax is clearer than the same operation using movcond. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1758c6e1d4..e9ba792065 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1982,7 +1982,7 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) dest = tcg_temp_new_i64(); tcg_gen_andi_i64(dest, offset, -4); tcg_gen_ori_i64(dest, dest, ctx->privilege); - tcg_gen_movcond_i64(TCG_COND_GTU, dest, dest, offset, dest, offset); + tcg_gen_umax_i64(dest, dest, offset); break; } return dest; From 1874e6c2fdb351120c234e6840729c9553d77d05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 12:50:07 -1000 Subject: [PATCH 16/43] target/hppa: Always make a copy in do_ibranch_priv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies callers, which might otherwise have to make another copy. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e9ba792065..1ede4bd725 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1968,18 +1968,17 @@ static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 dspc, */ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) { - TCGv_i64 dest; + TCGv_i64 dest = tcg_temp_new_i64(); switch (ctx->privilege) { case 0: /* Privilege 0 is maximum and is allowed to decrease. */ - return offset; + tcg_gen_mov_i64(dest, offset); + break; case 3: /* Privilege 3 is minimum and is never allowed to increase. */ - dest = tcg_temp_new_i64(); tcg_gen_ori_i64(dest, offset, 3); break; default: - dest = tcg_temp_new_i64(); tcg_gen_andi_i64(dest, offset, -4); tcg_gen_ori_i64(dest, dest, ctx->privilege); tcg_gen_umax_i64(dest, dest, offset); From bc921866cefb3ec4031714aeb4569e0e7622dfba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 13:56:44 -1000 Subject: [PATCH 17/43] 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 Signed-off-by: Richard Henderson --- target/hppa/translate.c | 378 +++++++++++++++++++++------------------- 1 file changed, 198 insertions(+), 180 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1ede4bd725..e0e4db75ee 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -42,21 +42,23 @@ typedef struct DisasCond { TCGv_i64 a0, a1; } 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 { DisasContextBase base; CPUState *cs; - uint64_t iaoq_f; - uint64_t iaoq_b; - uint64_t iaoq_n; - TCGv_i64 iaoq_n_var; - /* - * 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; + /* IAQ_Front, IAQ_Back. */ + DisasIAQE iaq_f, iaq_b; + /* IAQ_Next, for jumps, otherwise null for simple advance. */ + DisasIAQE iaq_j, *iaq_n; DisasCond null_cond; TCGLabel *null_lab; @@ -602,49 +604,67 @@ static bool nullify_end(DisasContext *ctx) 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, - uint64_t ival, TCGv_i64 vval) + const DisasIAQE *src) { uint64_t mask = gva_offset_mask(ctx->tb_flags); - if (ival != -1) { - tcg_gen_movi_i64(dest, ival & mask); - return; - } - 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); + if (src->base == NULL) { + tcg_gen_movi_i64(dest, src->disp & mask); + } else if (src->disp == 0) { + tcg_gen_andi_i64(dest, src->base, mask); } 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, - uint64_t bi, TCGv_i64 bv, TCGv_i64 bs, - uint64_t ni, TCGv_i64 nv, TCGv_i64 ns) +static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f, + const DisasIAQE *b) { - copy_iaoq_entry(ctx, cpu_iaoq_f, bi, bv); + DisasIAQE b_next; - /* Allow ni variable, with nv null, to indicate a trivial advance. */ - if (ni != -1 || nv) { - copy_iaoq_entry(ctx, cpu_iaoq_b, ni, nv); - } 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 (b == NULL) { + b_next = iaqe_incr(f, 4); + b = &b_next; } - if (bs) { - tcg_gen_mov_i64(cpu_iasq_f, bs); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + copy_iaoq_entry(ctx, cpu_iaoq_b, b); + if (f->space) { + tcg_gen_mov_i64(cpu_iasq_f, f->space); } - if (ns || bs) { - tcg_gen_mov_i64(cpu_iasq_b, ns ? ns : bs); + if (b->space || f->space) { + 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) { return; } - if (ctx->iaoq_b == -1) { - tcg_gen_addi_i64(cpu_gr[link], cpu_iaoq_b, 4); + if (ctx->iaq_b.base) { + tcg_gen_addi_i64(cpu_gr[link], ctx->iaq_b.base, + ctx->iaq_b.disp + 4); } 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 if (with_sr0) { @@ -666,11 +687,6 @@ static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) #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) { 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) { - install_iaq_entries(ctx, ctx->iaoq_f, cpu_iaoq_f, NULL, - ctx->iaoq_b, cpu_iaoq_b, NULL); + install_iaq_entries(ctx, &ctx->iaq_f, &ctx->iaq_b); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; @@ -711,10 +726,12 @@ static bool gen_illegal(DisasContext *ctx) } while (0) #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 && - translator_use_goto_tb(&ctx->base, bofs)); + return (!iaqe_variable(f) && + (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, @@ -724,20 +741,19 @@ static bool use_goto_tb(DisasContext *ctx, uint64_t bofs, uint64_t nofs) static bool use_nullify_skip(DisasContext *ctx) { return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) - && ctx->iaoq_b != -1 - && is_same_page(&ctx->base, ctx->iaoq_b)); + && !iaqe_variable(&ctx->iaq_b) + && is_same_page(&ctx->base, ctx->iaq_b.disp)); } 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); - install_iaq_entries(ctx, b, NULL, NULL, n, NULL, NULL); + install_iaq_entries(ctx, f, b); tcg_gen_exit_tb(ctx->base.tb, which); } else { - install_iaq_entries(ctx, b, cpu_iaoq_b, ctx->iasq_b, - n, ctx->iaoq_n_var, ctx->iasq_n); + install_iaq_entries(ctx, f, b); 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, 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) { install_link(ctx, link, false); if (is_n) { if (use_nullify_skip(ctx)) { 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; return true; } ctx->null_cond.c = TCG_COND_ALWAYS; } - ctx->iaoq_n = dest; - ctx->iaoq_n_var = NULL; + ctx->iaq_n = &ctx->iaq_j; } else { nullify_over(ctx); install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 0, dest, dest + 4); + gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); } else { 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_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; } 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, DisasCond *cond) { - uint64_t dest = iaoq_dest(ctx, disp); + DisasIAQE next; TCGLabel *taken = NULL; TCGCond c = cond->c; bool n; @@ -1879,26 +1893,29 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, n = is_n && disp < 0; if (n && use_nullify_skip(ctx)) { 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 { if (!n && ctx->null_lab) { gen_set_label(ctx->null_lab); ctx->null_lab = NULL; } 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); /* Taken: Condition satisfied; nullify on forward branches. */ n = is_n && disp >= 0; + + next = iaqe_branchi(ctx, disp); if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 1, dest, dest + 4); + gen_goto_tb(ctx, 1, &next, NULL); } else { 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. */ @@ -1912,45 +1929,36 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, return true; } -/* Emit an unconditional branch to an indirect target. This handles - nullification of the branch itself. */ -static bool do_ibranch(DisasContext *ctx, TCGv_i64 dest, TCGv_i64 dspc, - unsigned link, bool with_sr0, bool is_n) +/* + * Emit an unconditional branch to an indirect target, in ctx->iaq_j. + * This handles nullification of the branch itself. + */ +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) { - next = tcg_temp_new_i64(); - tcg_gen_mov_i64(next, dest); - install_link(ctx, link, with_sr0); if (is_n) { 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); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; } ctx->null_cond.c = TCG_COND_ALWAYS; } - ctx->iaoq_n = -1; - ctx->iaoq_n_var = next; - ctx->iasq_n = dspc; + ctx->iaq_n = &ctx->iaq_j; return true; } nullify_over(ctx); - next = tcg_temp_new_i64(); - tcg_gen_mov_i64(next, dest); - install_link(ctx, link, with_sr0); 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); } else { - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - -1, next, dspc); + install_iaq_entries(ctx, &ctx->iaq_b, &ctx->iaq_j); nullify_set(ctx, is_n); } @@ -1997,8 +2005,6 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { - TCGv_i64 tmp; - /* 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 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 detects this by disallowing the B,GATE instruction to execute 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; } - switch (ctx->iaoq_f & -4) { + switch (ctx->iaq_f.disp & -4) { case 0x00: /* Null pointer call */ gen_excp_1(EXCP_IMP); ctx->base.is_jmp = DISAS_NORETURN; @@ -2034,11 +2040,15 @@ static void do_page_zero(DisasContext *ctx) break; case 0xe0: /* SET_THREAD_POINTER */ - tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); - tmp = tcg_temp_new_i64(); - tcg_gen_ori_i64(tmp, cpu_gr[31], 3); - install_iaq_entries(ctx, -1, tmp, NULL, -1, NULL, NULL); - ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + { + DisasIAQE next = { .base = tcg_temp_new_i64() }; + + tcg_gen_st_i64(cpu_gr[26], tcg_env, + 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; 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) { - unsigned rt = a->t; - TCGv_i64 tmp = dest_gpr(ctx, rt); - tcg_gen_movi_i64(tmp, ctx->iaoq_f & ~3ULL); - save_gpr(ctx, rt, tmp); + TCGv_i64 dest = dest_gpr(ctx, a->t); + 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); return true; } @@ -2781,8 +2792,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) nullify_over(ctx); /* Advance the instruction queue. */ - install_iaq_entries(ctx, ctx->iaoq_b, cpu_iaoq_b, ctx->iasq_b, - ctx->iaoq_n, ctx->iaoq_n_var, ctx->iasq_n); + install_iaq_entries(ctx, &ctx->iaq_b, NULL); nullify_set(ctx, 0); /* 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) { - 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 - space = tcg_temp_new_i64(); - load_spr(ctx, space, a->sp); + ctx->iaq_j.space = tcg_temp_new_i64(); + load_spr(ctx, ctx->iaq_j.space, a->sp); #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) @@ -3937,7 +3947,7 @@ static bool trans_bl(DisasContext *ctx, arg_bl *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); @@ -3952,7 +3962,7 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) * b evil * 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); } @@ -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. */ if (type >= 4 && type - 4 < ctx->privilege) { - dest = deposit64(dest, 0, 2, type - 4); + disp -= ctx->privilege; + disp += type - 4; } } else { - dest &= -4; /* priv = 0 */ + disp -= ctx->privilege; /* priv = 0 */ } #endif @@ -3986,17 +3997,23 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) 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) { if (a->x) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_shli_i64(tmp, load_gpr(ctx, a->x), 3); - tcg_gen_addi_i64(tmp, tmp, ctx->iaoq_f + 8); + DisasIAQE next = iaqe_incr(&ctx->iaq_f, 8); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + /* 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 { /* BLR R0,RX is a good way to load PC+8 into RX. */ 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)); } 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) { TCGv_i64 b = load_gpr(ctx, a->b); - TCGv_i64 dest = do_ibranch_priv(ctx, b); - TCGv_i64 space = NULL; #ifndef CONFIG_USER_ONLY - space = space_select(ctx, 0, b); + ctx->iaq_j.space = space_select(ctx, 0, b); #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) @@ -4600,9 +4619,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #ifdef CONFIG_USER_ONLY ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); ctx->mmu_idx = MMU_USER_IDX; - ctx->iaoq_f = ctx->base.pc_first | ctx->privilege; - ctx->iaoq_b = ctx->base.tb->cs_base | ctx->privilege; - ctx->iasq_b = NULL; + ctx->iaq_f.disp = ctx->base.pc_first | ctx->privilege; + ctx->iaq_b.disp = ctx->base.tb->cs_base | ctx->privilege; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else 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; int32_t diff = cs_base; - ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; - ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); - ctx->iasq_b = (diff ? NULL : cpu_iasq_b); + ctx->iaq_f.disp = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; + if (diff) { + 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 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); - 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; } @@ -4668,11 +4693,12 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) the page permissions for execute. */ 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. */ - ctx->iasq_n = NULL; - ctx->iaoq_n_var = NULL; - ctx->iaoq_n = ctx->iaoq_b == -1 ? -1 : ctx->iaoq_b + 4; + /* + * Set up the IA queue for the next insn. + * This will be overwritten by a branch. + */ + ctx->iaq_n = NULL; + memset(&ctx->iaq_j, 0, sizeof(ctx->iaq_j)); if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { ctx->null_cond.c = TCG_COND_NEVER; @@ -4693,7 +4719,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) return; } /* 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; return; } @@ -4702,20 +4729,25 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) * Advance the insn queue. * The only exit now is DISAS_TOO_MANY from the translator loop. */ - ctx->iaoq_f = ctx->iaoq_b; - ctx->iaoq_b = ctx->iaoq_n; - if (ctx->iaoq_b == -1) { - if (ctx->iaoq_n_var) { - 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)); - } + ctx->iaq_f.disp = ctx->iaq_b.disp; + if (!ctx->iaq_n) { + ctx->iaq_b.disp += 4; + return; } - if (ctx->iasq_n) { - tcg_gen_mov_i64(cpu_iasq_b, ctx->iasq_n); - ctx->iasq_b = cpu_iasq_b; + /* + * If IAQ_Next is variable in any way, we need to copy into the + * 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); 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. */ - fi = ctx->iaoq_b; - fv = cpu_iaoq_b; - fs = ctx->iasq_b; - bi = ctx->iaoq_n; - bv = ctx->iaoq_n_var; - bs = ctx->iasq_n; + DisasIAQE *f = &ctx->iaq_b; + DisasIAQE *b = ctx->iaq_n; switch (is_jmp) { case DISAS_NORETURN: break; case DISAS_TOO_MANY: /* The insn queue has not been advanced. */ - bi = fi; - bv = fv; - bs = fs; - fi = ctx->iaoq_f; - fv = NULL; - fs = NULL; + f = &ctx->iaq_f; + b = &ctx->iaq_b; /* FALLTHRU */ case DISAS_IAQ_N_STALE: - if (fs == NULL - && bs == NULL - && use_goto_tb(ctx, fi, bi) + if (use_goto_tb(ctx, f, b) && (ctx->null_cond.c == TCG_COND_NEVER || 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; } /* FALLTHRU */ 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); if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { 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, vaddr pc, void *host_pc) { - DisasContext ctx; + DisasContext ctx = { }; translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base); } From 0d89cb7c29d9030d96c32ea4cdde7b4ee6f3dcf4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 21 Mar 2024 16:47:14 -1000 Subject: [PATCH 18/43] target/hppa: Use displacements in DisasIAQE This is a first step in enabling CF_PCREL, but for now we regenerate the absolute address before writeback. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 43 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index e0e4db75ee..de077e7a57 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -45,9 +45,9 @@ typedef struct 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. */ + /* IAOQ base; may be null for relative address. */ TCGv_i64 base; - /* IAOQ addend; absolute immedate address if base is null. */ + /* IAOQ addend; if base is null, relative to ctx->iaoq_first. */ int64_t disp; } DisasIAQE; @@ -60,6 +60,9 @@ typedef struct DisasContext { /* IAQ_Next, for jumps, otherwise null for simple advance. */ DisasIAQE iaq_j, *iaq_n; + /* IAOQ_Front at entry to TB. */ + uint64_t iaoq_first; + DisasCond null_cond; TCGLabel *null_lab; @@ -640,7 +643,7 @@ static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, uint64_t mask = gva_offset_mask(ctx->tb_flags); if (src->base == NULL) { - tcg_gen_movi_i64(dest, src->disp & mask); + tcg_gen_movi_i64(dest, (ctx->iaoq_first + src->disp) & mask); } else if (src->disp == 0) { tcg_gen_andi_i64(dest, src->base, mask); } else { @@ -674,12 +677,8 @@ static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) if (!link) { return; } - if (ctx->iaq_b.base) { - tcg_gen_addi_i64(cpu_gr[link], ctx->iaq_b.base, - ctx->iaq_b.disp + 4); - } else { - tcg_gen_movi_i64(cpu_gr[link], ctx->iaq_b.disp + 4); - } + DisasIAQE next = iaqe_incr(&ctx->iaq_b, 4); + copy_iaoq_entry(ctx, cpu_gr[link], &next); #ifndef CONFIG_USER_ONLY if (with_sr0) { tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); @@ -731,7 +730,7 @@ static bool use_goto_tb(DisasContext *ctx, const DisasIAQE *f, { return (!iaqe_variable(f) && (b == NULL || !iaqe_variable(b)) && - translator_use_goto_tb(&ctx->base, f->disp)); + translator_use_goto_tb(&ctx->base, ctx->iaoq_first + f->disp)); } /* If the next insn is to be nullified, and it's on the same page, @@ -742,7 +741,8 @@ static bool use_nullify_skip(DisasContext *ctx) { return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) && !iaqe_variable(&ctx->iaq_b) - && is_same_page(&ctx->base, ctx->iaq_b.disp)); + && (((ctx->iaoq_first + ctx->iaq_b.disp) ^ ctx->iaoq_first) + & TARGET_PAGE_MASK) == 0); } static void gen_goto_tb(DisasContext *ctx, int which, @@ -2005,6 +2005,8 @@ static TCGv_i64 do_ibranch_priv(DisasContext *ctx, TCGv_i64 offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { + assert(ctx->iaq_f.disp == 0); + /* 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 next insn within the privileged page. */ @@ -2024,11 +2026,11 @@ static void do_page_zero(DisasContext *ctx) non-sequential instruction execution. Normally the PSW[B] bit detects this by disallowing the B,GATE instruction to execute under such conditions. */ - if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { + if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != 4) { goto do_sigill; } - switch (ctx->iaq_f.disp & -4) { + switch (ctx->base.pc_first) { case 0x00: /* Null pointer call */ gen_excp_1(EXCP_IMP); ctx->base.is_jmp = DISAS_NORETURN; @@ -4619,8 +4621,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #ifdef CONFIG_USER_ONLY ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); ctx->mmu_idx = MMU_USER_IDX; - ctx->iaq_f.disp = ctx->base.pc_first | ctx->privilege; - ctx->iaq_b.disp = ctx->base.tb->cs_base | ctx->privilege; + ctx->iaoq_first = ctx->base.pc_first | ctx->privilege; + ctx->iaq_b.disp = ctx->base.tb->cs_base - ctx->base.pc_first; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; @@ -4633,9 +4635,10 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) uint64_t iasq_f = cs_base & ~0xffffffffull; int32_t diff = cs_base; - ctx->iaq_f.disp = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; + ctx->iaoq_first = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; + if (diff) { - ctx->iaq_b.disp = ctx->iaq_f.disp + diff; + ctx->iaq_b.disp = diff; } else { ctx->iaq_b.base = cpu_iaoq_b; ctx->iaq_b.space = cpu_iasq_b; @@ -4668,9 +4671,9 @@ static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) DisasContext *ctx = container_of(dcbase, DisasContext, base); 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); + tcg_gen_insn_start(ctx->iaoq_first + ctx->iaq_f.disp, + (iaqe_variable(&ctx->iaq_b) ? -1 : + ctx->iaoq_first + ctx->iaq_b.disp), 0); ctx->insn_start_updated = false; } From 4c42fd0d4e543aaf8981c737f971d27e7c9d4df0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 07:30:19 -1000 Subject: [PATCH 19/43] target/hppa: Rename cond_make_* helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use 'v' for a variable that needs copying, 't' for a temp that doesn't need copying, and 'i' for an immediate, and use this naming for both arguments of the comparison. So: cond_make_tmp -> cond_make_tt cond_make_0_tmp -> cond_make_ti cond_make_0 -> cond_make_vi cond_make -> cond_make_vv Pass 0 explictly, rather than implicitly in the function name. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index de077e7a57..07ba35001b 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -345,32 +345,32 @@ static DisasCond cond_make_n(void) }; } -static DisasCond cond_make_tmp(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) +static DisasCond cond_make_tt(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); return (DisasCond){ .c = c, .a0 = a0, .a1 = a1 }; } -static DisasCond cond_make_0_tmp(TCGCond c, TCGv_i64 a0) +static DisasCond cond_make_ti(TCGCond c, TCGv_i64 a0, uint64_t imm) { - return cond_make_tmp(c, a0, tcg_constant_i64(0)); + return cond_make_tt(c, a0, tcg_constant_i64(imm)); } -static DisasCond cond_make_0(TCGCond c, TCGv_i64 a0) +static DisasCond cond_make_vi(TCGCond c, TCGv_i64 a0, uint64_t imm) { TCGv_i64 tmp = tcg_temp_new_i64(); tcg_gen_mov_i64(tmp, a0); - return cond_make_0_tmp(c, tmp); + return cond_make_ti(c, tmp, imm); } -static DisasCond cond_make(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) +static DisasCond cond_make_vv(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); tcg_gen_mov_i64(t0, a0); tcg_gen_mov_i64(t1, a1); - return cond_make_tmp(c, t0, t1); + return cond_make_tt(c, t0, t1); } static void cond_free(DisasCond *cond) @@ -789,7 +789,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_ext32u_i64(tmp, res); res = tmp; } - cond = cond_make_0(TCG_COND_EQ, res); + cond = cond_make_vi(TCG_COND_EQ, res, 0); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ tmp = tcg_temp_new_i64(); @@ -797,7 +797,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, if (!d) { tcg_gen_ext32s_i64(tmp, tmp); } - cond = cond_make_0_tmp(TCG_COND_LT, tmp); + cond = cond_make_ti(TCG_COND_LT, tmp, 0); break; case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ /* @@ -819,10 +819,10 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_sari_i64(tmp, tmp, 63); tcg_gen_and_i64(tmp, tmp, res); } - cond = cond_make_0_tmp(TCG_COND_EQ, tmp); + cond = cond_make_ti(TCG_COND_EQ, tmp, 0); break; case 4: /* NUV / UV (!UV / UV) */ - cond = cond_make_0(TCG_COND_EQ, uv); + cond = cond_make_vi(TCG_COND_EQ, uv, 0); break; case 5: /* ZNV / VNZ (!UV | Z / UV & !Z) */ tmp = tcg_temp_new_i64(); @@ -830,7 +830,7 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, if (!d) { tcg_gen_ext32u_i64(tmp, tmp); } - cond = cond_make_0_tmp(TCG_COND_EQ, tmp); + cond = cond_make_ti(TCG_COND_EQ, tmp, 0); break; case 6: /* SV / NSV (V / !V) */ if (!d) { @@ -838,12 +838,12 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_ext32s_i64(tmp, sv); sv = tmp; } - cond = cond_make_0(TCG_COND_LT, sv); + cond = cond_make_ti(TCG_COND_LT, sv, 0); break; case 7: /* OD / EV */ tmp = tcg_temp_new_i64(); tcg_gen_andi_i64(tmp, res, 1); - cond = cond_make_0_tmp(TCG_COND_NE, tmp); + cond = cond_make_ti(TCG_COND_NE, tmp, 0); break; default: g_assert_not_reached(); @@ -905,9 +905,9 @@ static DisasCond do_sub_cond(DisasContext *ctx, unsigned cf, bool d, tcg_gen_ext32s_i64(t1, in1); tcg_gen_ext32s_i64(t2, in2); } - return cond_make_tmp(tc, t1, t2); + return cond_make_tt(tc, t1, t2); } - return cond_make(tc, in1, in2); + return cond_make_vv(tc, in1, in2); } /* @@ -979,9 +979,9 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, } else { tcg_gen_ext32s_i64(tmp, res); } - return cond_make_0_tmp(tc, tmp); + return cond_make_ti(tc, tmp, 0); } - return cond_make_0(tc, res); + return cond_make_vi(tc, res, 0); } /* Similar, but for shift/extract/deposit conditions. */ @@ -1040,7 +1040,7 @@ static DisasCond do_unit_zero_cond(unsigned cf, bool d, TCGv_i64 res) tcg_gen_andc_i64(tmp, tmp, res); tcg_gen_andi_i64(tmp, tmp, sgns); - return cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp); + return cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp, 0); } static TCGv_i64 get_carry(DisasContext *ctx, bool d, @@ -1454,7 +1454,7 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } tcg_gen_andi_i64(cb, cb, test_cb); - cond = cond_make_0_tmp(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb); + cond = cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb, 0); } if (is_tc) { @@ -3543,7 +3543,7 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) tcg_gen_shl_i64(tmp, tcg_r, tmp); } - cond = cond_make_0_tmp(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + cond = cond_make_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -3560,7 +3560,7 @@ static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) p = a->p | (a->d ? 0 : 32); tcg_gen_shli_i64(tmp, tcg_r, p); - cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + cond = cond_make_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0); return do_cbranch(ctx, a->disp, a->n, &cond); } @@ -4364,7 +4364,7 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) switch (a->c) { case 0: /* simple */ tcg_gen_andi_i64(t, t, 0x4000000); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); + ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); goto done; case 2: /* rej */ inv = true; @@ -4394,16 +4394,16 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) if (inv) { TCGv_i64 c = tcg_constant_i64(mask); tcg_gen_or_i64(t, t, c); - ctx->null_cond = cond_make(TCG_COND_EQ, t, c); + ctx->null_cond = cond_make_tt(TCG_COND_EQ, t, c); } else { tcg_gen_andi_i64(t, t, mask); - ctx->null_cond = cond_make_0(TCG_COND_EQ, t); + ctx->null_cond = cond_make_ti(TCG_COND_EQ, t, 0); } } else { unsigned cbit = (a->y ^ 1) - 1; tcg_gen_extract_i64(t, t, 21 - cbit, 1); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); + ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); } done: From d6d46be1bf3876db6168d155ed273866d5f595cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 10:27:12 -1000 Subject: [PATCH 20/43] target/hppa: Use TCG_COND_TST* in do_cond We can directly test bits of a 32-bit comparison without zero or sign-extending an intermediate result. We can directly test bit 0 for odd/even. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 64 ++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 07ba35001b..813f1571e9 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -776,28 +776,36 @@ static bool cond_need_cb(int c) static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, TCGv_i64 res, TCGv_i64 uv, TCGv_i64 sv) { + TCGCond sign_cond, zero_cond; + uint64_t sign_imm, zero_imm; DisasCond cond; TCGv_i64 tmp; + if (d) { + /* 64-bit condition. */ + sign_imm = 0; + sign_cond = TCG_COND_LT; + zero_imm = 0; + zero_cond = TCG_COND_EQ; + } else { + /* 32-bit condition. */ + sign_imm = 1ull << 31; + sign_cond = TCG_COND_TSTNE; + zero_imm = UINT32_MAX; + zero_cond = TCG_COND_TSTEQ; + } + switch (cf >> 1) { case 0: /* Never / TR (0 / 1) */ cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ - if (!d) { - tmp = tcg_temp_new_i64(); - tcg_gen_ext32u_i64(tmp, res); - res = tmp; - } - cond = cond_make_vi(TCG_COND_EQ, res, 0); + cond = cond_make_vi(zero_cond, res, zero_imm); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ tmp = tcg_temp_new_i64(); tcg_gen_xor_i64(tmp, res, sv); - if (!d) { - tcg_gen_ext32s_i64(tmp, tmp); - } - cond = cond_make_ti(TCG_COND_LT, tmp, 0); + cond = cond_make_ti(sign_cond, tmp, sign_imm); break; case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ /* @@ -805,21 +813,15 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, * (N ^ V) | Z * ((res < 0) ^ (sv < 0)) | !res * ((res ^ sv) < 0) | !res - * (~(res ^ sv) >= 0) | !res - * !(~(res ^ sv) >> 31) | !res - * !(~(res ^ sv) >> 31 & res) + * ((res ^ sv) < 0 ? 1 : !res) + * !((res ^ sv) < 0 ? 0 : res) */ tmp = tcg_temp_new_i64(); - tcg_gen_eqv_i64(tmp, res, sv); - if (!d) { - tcg_gen_sextract_i64(tmp, tmp, 31, 1); - tcg_gen_and_i64(tmp, tmp, res); - tcg_gen_ext32u_i64(tmp, tmp); - } else { - tcg_gen_sari_i64(tmp, tmp, 63); - tcg_gen_and_i64(tmp, tmp, res); - } - cond = cond_make_ti(TCG_COND_EQ, tmp, 0); + tcg_gen_xor_i64(tmp, res, sv); + tcg_gen_movcond_i64(sign_cond, tmp, + tmp, tcg_constant_i64(sign_imm), + ctx->zero, res); + cond = cond_make_ti(zero_cond, tmp, zero_imm); break; case 4: /* NUV / UV (!UV / UV) */ cond = cond_make_vi(TCG_COND_EQ, uv, 0); @@ -827,23 +829,13 @@ static DisasCond do_cond(DisasContext *ctx, unsigned cf, bool d, case 5: /* ZNV / VNZ (!UV | Z / UV & !Z) */ tmp = tcg_temp_new_i64(); tcg_gen_movcond_i64(TCG_COND_EQ, tmp, uv, ctx->zero, ctx->zero, res); - if (!d) { - tcg_gen_ext32u_i64(tmp, tmp); - } - cond = cond_make_ti(TCG_COND_EQ, tmp, 0); + cond = cond_make_ti(zero_cond, tmp, zero_imm); break; case 6: /* SV / NSV (V / !V) */ - if (!d) { - tmp = tcg_temp_new_i64(); - tcg_gen_ext32s_i64(tmp, sv); - sv = tmp; - } - cond = cond_make_ti(TCG_COND_LT, sv, 0); + cond = cond_make_vi(sign_cond, sv, sign_imm); break; case 7: /* OD / EV */ - tmp = tcg_temp_new_i64(); - tcg_gen_andi_i64(tmp, res, 1); - cond = cond_make_ti(TCG_COND_NE, tmp, 0); + cond = cond_make_vi(TCG_COND_TSTNE, res, 1); break; default: g_assert_not_reached(); From fbe65c648d46efd5b77f30e9d60a14f09535ea1a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:00:36 -1000 Subject: [PATCH 21/43] target/hppa: Use TCG_COND_TST* in do_log_cond We can directly test bits of a 32-bit comparison without zero or sign-extending an intermediate result. We can directly test bit 0 for odd/even. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 78 ++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 813f1571e9..62cc3c3117 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -915,65 +915,41 @@ static DisasCond do_log_cond(DisasContext *ctx, unsigned cf, bool d, TCGv_i64 res) { TCGCond tc; - bool ext_uns; + uint64_t imm; - switch (cf) { - case 0: /* never */ - case 9: /* undef, C */ - case 11: /* undef, C & !Z */ - case 12: /* undef, V */ - return cond_make_f(); - - case 1: /* true */ - case 8: /* undef, !C */ - case 10: /* undef, !C | Z */ - case 13: /* undef, !V */ - return cond_make_t(); - - case 2: /* == */ - tc = TCG_COND_EQ; - ext_uns = true; + switch (cf >> 1) { + case 0: /* never / always */ + case 4: /* undef, C */ + case 5: /* undef, C & !Z */ + case 6: /* undef, V */ + return cf & 1 ? cond_make_t() : cond_make_f(); + case 1: /* == / <> */ + tc = d ? TCG_COND_EQ : TCG_COND_TSTEQ; + imm = d ? 0 : UINT32_MAX; break; - case 3: /* <> */ - tc = TCG_COND_NE; - ext_uns = true; + case 2: /* < / >= */ + tc = d ? TCG_COND_LT : TCG_COND_TSTNE; + imm = d ? 0 : 1ull << 31; break; - case 4: /* < */ - tc = TCG_COND_LT; - ext_uns = false; + case 3: /* <= / > */ + tc = cf & 1 ? TCG_COND_GT : TCG_COND_LE; + if (!d) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(tmp, res); + return cond_make_ti(tc, tmp, 0); + } + return cond_make_vi(tc, res, 0); + case 7: /* OD / EV */ + tc = TCG_COND_TSTNE; + imm = 1; break; - case 5: /* >= */ - tc = TCG_COND_GE; - ext_uns = false; - break; - case 6: /* <= */ - tc = TCG_COND_LE; - ext_uns = false; - break; - case 7: /* > */ - tc = TCG_COND_GT; - ext_uns = false; - break; - - case 14: /* OD */ - case 15: /* EV */ - return do_cond(ctx, cf, d, res, NULL, NULL); - default: g_assert_not_reached(); } - - if (!d) { - TCGv_i64 tmp = tcg_temp_new_i64(); - - if (ext_uns) { - tcg_gen_ext32u_i64(tmp, res); - } else { - tcg_gen_ext32s_i64(tmp, res); - } - return cond_make_ti(tc, tmp, 0); + if (cf & 1) { + tc = tcg_invert_cond(tc); } - return cond_make_vi(tc, res, 0); + return cond_make_vi(tc, res, imm); } /* Similar, but for shift/extract/deposit conditions. */ From 25f97be7236cc96bccce35f27999b0289c2b221f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:04:06 -1000 Subject: [PATCH 22/43] target/hppa: Use TCG_COND_TST* in do_unit_zero_cond Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 62cc3c3117..b19d7c64fe 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1006,9 +1006,8 @@ static DisasCond do_unit_zero_cond(unsigned cf, bool d, TCGv_i64 res) tmp = tcg_temp_new_i64(); tcg_gen_subi_i64(tmp, res, ones); tcg_gen_andc_i64(tmp, tmp, res); - tcg_gen_andi_i64(tmp, tmp, sgns); - return cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, tmp, 0); + return cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, tmp, sgns); } static TCGv_i64 get_carry(DisasContext *ctx, bool d, From 3289ea0e8f4894a67f827540d674e3e199596f33 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:05:46 -1000 Subject: [PATCH 23/43] target/hppa: Use TCG_COND_TST* in do_unit_addsub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index b19d7c64fe..4e49bd2b67 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1420,8 +1420,8 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, tcg_gen_shri_i64(cb, cb, 1); } - tcg_gen_andi_i64(cb, cb, test_cb); - cond = cond_make_ti(cf & 1 ? TCG_COND_EQ : TCG_COND_NE, cb, 0); + cond = cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + cb, test_cb); } if (is_tc) { From b041ec9d7173ec893c1a5bc0bfd25fe860b4fcb0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 11:22:59 -1000 Subject: [PATCH 24/43] target/hppa: Use TCG_COND_TST* in trans_bb_imm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 4e49bd2b67..af6be5100c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3516,18 +3516,12 @@ static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) { - TCGv_i64 tmp, tcg_r; DisasCond cond; - int p; + int p = a->p | (a->d ? 0 : 32); nullify_over(ctx); - - tmp = tcg_temp_new_i64(); - tcg_r = load_gpr(ctx, a->r); - p = a->p | (a->d ? 0 : 32); - tcg_gen_shli_i64(tmp, tcg_r, p); - - cond = cond_make_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0); + cond = cond_make_vi(a->c ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + load_gpr(ctx, a->r), 1ull << (63 - p)); return do_cbranch(ctx, a->disp, a->n, &cond); } From f33a22c1a28f0ba7b9c2fef1953aa6f593da870d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 12:20:31 -1000 Subject: [PATCH 25/43] target/hppa: Use registerfields.h for FPSR Define all of the context dependent field definitions. Use FIELD_EX32 and FIELD_DP32 with named fields instead of extract32 and deposit32 with raw constants. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 25 +++++++++++++++++++++++++ target/hppa/fpu_helper.c | 26 +++++++++++++------------- target/hppa/translate.c | 18 ++++++++---------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 61f1353133..c37b4e12fb 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -24,6 +24,7 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" #include "qemu/interval-tree.h" +#include "hw/registerfields.h" #define MMU_ABS_W_IDX 6 #define MMU_ABS_IDX 7 @@ -152,6 +153,30 @@ #define CR_IPSW 22 #define CR_EIRR 23 +FIELD(FPSR, ENA_I, 0, 1) +FIELD(FPSR, ENA_U, 1, 1) +FIELD(FPSR, ENA_O, 2, 1) +FIELD(FPSR, ENA_Z, 3, 1) +FIELD(FPSR, ENA_V, 4, 1) +FIELD(FPSR, ENABLES, 0, 5) +FIELD(FPSR, D, 5, 1) +FIELD(FPSR, T, 6, 1) +FIELD(FPSR, RM, 9, 2) +FIELD(FPSR, CQ, 11, 11) +FIELD(FPSR, CQ0_6, 15, 7) +FIELD(FPSR, CQ0_4, 17, 5) +FIELD(FPSR, CQ0_2, 19, 3) +FIELD(FPSR, CQ0, 21, 1) +FIELD(FPSR, CA, 15, 7) +FIELD(FPSR, CA0, 21, 1) +FIELD(FPSR, C, 26, 1) +FIELD(FPSR, FLG_I, 27, 1) +FIELD(FPSR, FLG_U, 28, 1) +FIELD(FPSR, FLG_O, 29, 1) +FIELD(FPSR, FLG_Z, 30, 1) +FIELD(FPSR, FLG_V, 31, 1) +FIELD(FPSR, FLAGS, 27, 5) + typedef struct HPPATLBEntry { union { IntervalTreeNode itree; diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 576f283b04..deaed2b65d 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -30,7 +30,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) env->fr0_shadow = shadow; - switch (extract32(shadow, 9, 2)) { + switch (FIELD_EX32(shadow, FPSR, RM)) { default: rm = float_round_nearest_even; break; @@ -46,7 +46,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) } set_float_rounding_mode(rm, &env->fp_status); - d = extract32(shadow, 5, 1); + d = FIELD_EX32(shadow, FPSR, D); set_flush_to_zero(d, &env->fp_status); set_flush_inputs_to_zero(d, &env->fp_status); } @@ -57,7 +57,7 @@ void cpu_hppa_loaded_fr0(CPUHPPAState *env) } #define CONVERT_BIT(X, SRC, DST) \ - ((SRC) > (DST) \ + ((unsigned)(SRC) > (unsigned)(DST) \ ? (X) / ((SRC) / (DST)) & (DST) \ : ((X) & (SRC)) * ((DST) / (SRC))) @@ -73,12 +73,12 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) } set_float_exception_flags(0, &env->fp_status); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); - hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); - shadow |= hard_exp << (32 - 5); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, R_FPSR_ENA_I_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK); + shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); env->fr0_shadow = shadow; env->fr[0] = (uint64_t)shadow << 32; @@ -378,15 +378,15 @@ static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, if (y) { /* targeted comparison */ /* set fpsr[ca[y - 1]] to current compare */ - shadow = deposit32(shadow, 21 - (y - 1), 1, c); + shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c); } else { /* queued comparison */ /* shift cq right by one place */ - shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); + shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK); /* move fpsr[c] to fpsr[cq[0]] */ - shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); + shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C)); /* set fpsr[c] to current compare */ - shadow = deposit32(shadow, 26, 1, c); + shadow = FIELD_DP32(shadow, FPSR, C, c); } env->fr0_shadow = shadow; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index af6be5100c..efd4398437 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4324,29 +4324,28 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) switch (a->c) { case 0: /* simple */ - tcg_gen_andi_i64(t, t, 0x4000000); - ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); - goto done; + mask = R_FPSR_C_MASK; + break; case 2: /* rej */ inv = true; /* fallthru */ case 1: /* acc */ - mask = 0x43ff800; + mask = R_FPSR_C_MASK | R_FPSR_CQ_MASK; break; case 6: /* rej8 */ inv = true; /* fallthru */ case 5: /* acc8 */ - mask = 0x43f8000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_6_MASK; break; case 9: /* acc6 */ - mask = 0x43e0000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_4_MASK; break; case 13: /* acc4 */ - mask = 0x4380000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_2_MASK; break; case 17: /* acc2 */ - mask = 0x4200000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_MASK; break; default: gen_illegal(ctx); @@ -4363,11 +4362,10 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) } else { unsigned cbit = (a->y ^ 1) - 1; - tcg_gen_extract_i64(t, t, 21 - cbit, 1); + tcg_gen_extract_i64(t, t, R_FPSR_CA0_SHIFT - cbit, 1); ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); } - done: return nullify_end(ctx); } From 3692ad21f558f1e3425a5d6cecc241d2601db022 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 13:26:10 -1000 Subject: [PATCH 26/43] target/hppa: Use TCG_COND_TST* in trans_ftest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index efd4398437..11d74bb2aa 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -4311,6 +4311,8 @@ static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a) static bool trans_ftest(DisasContext *ctx, arg_ftest *a) { + TCGCond tc = TCG_COND_TSTNE; + uint32_t mask; TCGv_i64 t; nullify_over(ctx); @@ -4319,21 +4321,18 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) tcg_gen_ld32u_i64(t, tcg_env, offsetof(CPUHPPAState, fr0_shadow)); if (a->y == 1) { - int mask; - bool inv = false; - switch (a->c) { case 0: /* simple */ mask = R_FPSR_C_MASK; break; case 2: /* rej */ - inv = true; + tc = TCG_COND_TSTEQ; /* fallthru */ case 1: /* acc */ mask = R_FPSR_C_MASK | R_FPSR_CQ_MASK; break; case 6: /* rej8 */ - inv = true; + tc = TCG_COND_TSTEQ; /* fallthru */ case 5: /* acc8 */ mask = R_FPSR_C_MASK | R_FPSR_CQ0_6_MASK; @@ -4351,21 +4350,12 @@ static bool trans_ftest(DisasContext *ctx, arg_ftest *a) gen_illegal(ctx); return true; } - if (inv) { - TCGv_i64 c = tcg_constant_i64(mask); - tcg_gen_or_i64(t, t, c); - ctx->null_cond = cond_make_tt(TCG_COND_EQ, t, c); - } else { - tcg_gen_andi_i64(t, t, mask); - ctx->null_cond = cond_make_ti(TCG_COND_EQ, t, 0); - } } else { unsigned cbit = (a->y ^ 1) - 1; - - tcg_gen_extract_i64(t, t, R_FPSR_CA0_SHIFT - cbit, 1); - ctx->null_cond = cond_make_ti(TCG_COND_NE, t, 0); + mask = R_FPSR_CA0_MASK >> cbit; } + ctx->null_cond = cond_make_ti(tc, t, mask); return nullify_end(ctx); } From e0137378ed4f3eecc44b7ac9b435f9d91ca7e8fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 25 Mar 2024 13:50:11 -1000 Subject: [PATCH 27/43] target/hppa: Remove cond_free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we do not need to free tcg temporaries, the only thing cond_free does is reset the condition to never. Instead, simply write a new condition over the old, which may be simply cond_make_f() for the never condition. The do_*_cond functions do the right thing with c or cf == 0, so there's no need for a special case anymore. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/translate.c | 102 +++++++++++----------------------------- 1 file changed, 27 insertions(+), 75 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 11d74bb2aa..81a75ddf95 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -373,21 +373,6 @@ static DisasCond cond_make_vv(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) return cond_make_tt(c, t0, t1); } -static void cond_free(DisasCond *cond) -{ - switch (cond->c) { - default: - cond->a0 = NULL; - cond->a1 = NULL; - /* fallthru */ - case TCG_COND_ALWAYS: - cond->c = TCG_COND_NEVER; - break; - case TCG_COND_NEVER: - break; - } -} - static TCGv_i64 load_gpr(DisasContext *ctx, unsigned reg) { if (reg == 0) { @@ -537,7 +522,7 @@ static void nullify_over(DisasContext *ctx) tcg_gen_brcond_i64(ctx->null_cond.c, ctx->null_cond.a0, ctx->null_cond.a1, ctx->null_lab); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); } } @@ -555,7 +540,7 @@ static void nullify_save(DisasContext *ctx) ctx->null_cond.a0, ctx->null_cond.a1); ctx->psw_n_nonzero = true; } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); } /* Set a PSW[N] to X. The intention is that this is used immediately @@ -1166,7 +1151,6 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1263,7 +1247,6 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1318,7 +1301,6 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_i64 in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1333,10 +1315,7 @@ static void do_log(DisasContext *ctx, unsigned rt, TCGv_i64 in1, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (cf) { - ctx->null_cond = do_log_cond(ctx, cf, d, dest); - } + ctx->null_cond = do_log_cond(ctx, cf, d, dest); } static bool do_log_reg(DisasContext *ctx, arg_rrr_cf_d *a, @@ -1431,7 +1410,6 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } save_gpr(ctx, rt, dest); - cond_free(&ctx->null_cond); ctx->null_cond = cond; } @@ -1854,7 +1832,6 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, taken = gen_new_label(); tcg_gen_brcond_i64(c, cond->a0, cond->a1, taken); - cond_free(cond); /* Not taken: Condition not satisfied; nullify on backward branches. */ n = is_n && disp < 0; @@ -2036,7 +2013,7 @@ static void do_page_zero(DisasContext *ctx) static bool trans_nop(DisasContext *ctx, arg_nop *a) { - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2050,7 +2027,7 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a) /* No point in nullifying the memory barrier. */ tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2062,7 +2039,7 @@ static bool trans_mfia(DisasContext *ctx, arg_mfia *a) tcg_gen_andi_i64(dest, dest, -4); save_gpr(ctx, a->t, dest); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2077,7 +2054,7 @@ static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a) save_gpr(ctx, rt, t0); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2122,7 +2099,7 @@ static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) save_gpr(ctx, rt, tmp); done: - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2162,7 +2139,7 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) tcg_gen_andi_i64(tmp, reg, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2236,7 +2213,7 @@ static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) tcg_gen_andi_i64(tmp, tmp, ctx->is_pa20 ? 63 : 31); save_or_nullify(ctx, cpu_sar, tmp); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2253,7 +2230,7 @@ static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) #endif save_gpr(ctx, a->t, dest); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2415,7 +2392,7 @@ static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) tcg_gen_add_i64(dest, src1, src2); save_gpr(ctx, a->b, dest); } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2657,7 +2634,7 @@ static bool trans_lci(DisasContext *ctx, arg_lci *a) since the entire address space is coherent. */ save_gpr(ctx, a->t, ctx->zero); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2734,7 +2711,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) unsigned rt = a->t; if (rt == 0) { /* NOP */ - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } if (r2 == 0) { /* COPY */ @@ -2745,7 +2722,7 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) } else { save_gpr(ctx, rt, cpu_gr[r1]); } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } #ifndef CONFIG_USER_ONLY @@ -2810,11 +2787,7 @@ static bool trans_uxor(DisasContext *ctx, arg_rrr_cf_d *a) tcg_gen_xor_i64(dest, tcg_r1, tcg_r2); save_gpr(ctx, a->t, dest); - cond_free(&ctx->null_cond); - if (a->cf) { - ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest); - } - + ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest); return nullify_end(ctx); } @@ -2840,7 +2813,7 @@ static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) tcg_gen_subi_i64(tmp, tmp, 1); } save_gpr(ctx, a->t, tmp); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3366,7 +3339,7 @@ static bool trans_ldil(DisasContext *ctx, arg_ldil *a) tcg_gen_movi_i64(tcg_rt, a->i); save_gpr(ctx, a->t, tcg_rt); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3377,7 +3350,7 @@ static bool trans_addil(DisasContext *ctx, arg_addil *a) tcg_gen_addi_i64(tcg_r1, tcg_rt, a->i); save_gpr(ctx, 1, tcg_r1); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3393,7 +3366,7 @@ static bool trans_ldo(DisasContext *ctx, arg_ldo *a) tcg_gen_addi_i64(tcg_rt, cpu_gr[a->b], a->i); } save_gpr(ctx, a->t, tcg_rt); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -3619,10 +3592,7 @@ static bool trans_shrp_sar(DisasContext *ctx, arg_shrp_sar *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3662,10 +3632,7 @@ static bool trans_shrp_imm(DisasContext *ctx, arg_shrp_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3707,10 +3674,7 @@ static bool trans_extr_sar(DisasContext *ctx, arg_extr_sar *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3743,10 +3707,7 @@ static bool trans_extr_imm(DisasContext *ctx, arg_extr_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3783,10 +3744,7 @@ static bool trans_depi_imm(DisasContext *ctx, arg_depi_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3819,10 +3777,7 @@ static bool trans_dep_imm(DisasContext *ctx, arg_dep_imm *a) save_gpr(ctx, a->t, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (a->c) { - ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); - } + ctx->null_cond = do_sed_cond(ctx, a->c, a->d, dest); return nullify_end(ctx); } @@ -3856,10 +3811,7 @@ static bool do_dep_sar(DisasContext *ctx, unsigned rt, unsigned c, save_gpr(ctx, rt, dest); /* Install the new nullification. */ - cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(ctx, c, d, dest); - } + ctx->null_cond = do_sed_cond(ctx, c, d, dest); return nullify_end(ctx); } From 806030074b25a980f879e43df4e9c06fc45f308c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 12:47:11 -1000 Subject: [PATCH 28/43] target/hppa: Introduce DisasDelayException Allow an exception to be emitted at the end of the TranslationBlock, leaving only the conditional branch inline. Use it for simple exception instructions like break, which happen to be nullified. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 60 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 81a75ddf95..706537ea59 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -51,6 +51,17 @@ typedef struct DisasIAQE { int64_t disp; } DisasIAQE; +typedef struct DisasDelayException { + struct DisasDelayException *next; + TCGLabel *lab; + uint32_t insn; + bool set_iir; + int8_t set_n; + uint8_t excp; + /* Saved state at parent insn. */ + DisasIAQE iaq_f, iaq_b; +} DisasDelayException; + typedef struct DisasContext { DisasContextBase base; CPUState *cs; @@ -66,6 +77,7 @@ typedef struct DisasContext { DisasCond null_cond; TCGLabel *null_lab; + DisasDelayException *delay_excp_list; TCGv_i64 zero; uint32_t insn; @@ -684,13 +696,38 @@ static void gen_excp(DisasContext *ctx, int exception) ctx->base.is_jmp = DISAS_NORETURN; } +static DisasDelayException *delay_excp(DisasContext *ctx, uint8_t excp) +{ + DisasDelayException *e = tcg_malloc(sizeof(DisasDelayException)); + + memset(e, 0, sizeof(*e)); + e->next = ctx->delay_excp_list; + ctx->delay_excp_list = e; + + e->lab = gen_new_label(); + e->insn = ctx->insn; + e->set_iir = true; + e->set_n = ctx->psw_n_nonzero ? 0 : -1; + e->excp = excp; + e->iaq_f = ctx->iaq_f; + e->iaq_b = ctx->iaq_b; + + return e; +} + static bool gen_excp_iir(DisasContext *ctx, int exc) { - nullify_over(ctx); - tcg_gen_st_i64(tcg_constant_i64(ctx->insn), - tcg_env, offsetof(CPUHPPAState, cr[CR_IIR])); - gen_excp(ctx, exc); - return nullify_end(ctx); + if (ctx->null_cond.c == TCG_COND_NEVER) { + tcg_gen_st_i64(tcg_constant_i64(ctx->insn), + tcg_env, offsetof(CPUHPPAState, cr[CR_IIR])); + gen_excp(ctx, exc); + } else { + DisasDelayException *e = delay_excp(ctx, exc); + tcg_gen_brcond_i64(tcg_invert_cond(ctx->null_cond.c), + ctx->null_cond.a0, ctx->null_cond.a1, e->lab); + ctx->null_cond = cond_make_f(); + } + return true; } static bool gen_illegal(DisasContext *ctx) @@ -4697,6 +4734,19 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) default: g_assert_not_reached(); } + + for (DisasDelayException *e = ctx->delay_excp_list; e ; e = e->next) { + gen_set_label(e->lab); + if (e->set_n >= 0) { + tcg_gen_movi_i64(cpu_psw_n, e->set_n); + } + if (e->set_iir) { + tcg_gen_st_i64(tcg_constant_i64(e->insn), tcg_env, + offsetof(CPUHPPAState, cr[CR_IIR])); + } + install_iaq_entries(ctx, &e->iaq_f, &e->iaq_b); + gen_excp_1(e->excp); + } } static void hppa_tr_disas_log(const DisasContextBase *dcbase, From 269ca0a9ccaafd023b7c1fc914c1386128796fb0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 13:16:15 -1000 Subject: [PATCH 29/43] target/hppa: Use delay_excp for conditional traps Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/helper.h | 1 - target/hppa/int_helper.c | 2 +- target/hppa/op_helper.c | 7 ------- target/hppa/translate.c | 41 ++++++++++++++++++++++++++++++---------- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 5900fd70bc..3d0d143aed 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,6 +1,5 @@ DEF_HELPER_2(excp, noreturn, env, int) DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index a667ee380d..1aa3e88ef1 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -134,13 +134,13 @@ void hppa_cpu_do_interrupt(CPUState *cs) switch (i) { case EXCP_ILL: case EXCP_BREAK: + case EXCP_COND: case EXCP_PRIV_REG: case EXCP_PRIV_OPR: /* IIR set via translate.c. */ break; case EXCP_OVERFLOW: - case EXCP_COND: case EXCP_ASSIST: case EXCP_DTLB_MISS: case EXCP_NA_ITLB_MISS: diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 6cf49f33b7..a8b69fd481 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -49,13 +49,6 @@ void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) } } -void HELPER(tcond)(CPUHPPAState *env, target_ulong cond) -{ - if (unlikely(cond)) { - hppa_dynamic_excp(env, EXCP_COND, GETPC()); - } -} - static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr, uint32_t val, uint32_t mask, uintptr_t ra) { diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 706537ea59..ae291124f2 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1117,6 +1117,25 @@ static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res, return sv; } +static void gen_tc(DisasContext *ctx, DisasCond *cond) +{ + DisasDelayException *e; + + switch (cond->c) { + case TCG_COND_NEVER: + break; + case TCG_COND_ALWAYS: + gen_excp_iir(ctx, EXCP_COND); + break; + default: + e = delay_excp(ctx, EXCP_COND); + tcg_gen_brcond_i64(cond->c, cond->a0, cond->a1, e->lab); + /* In the non-trap path, the condition is known false. */ + *cond = cond_make_f(); + break; + } +} + static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, TCGv_i64 in2, unsigned shift, bool is_l, bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) @@ -1175,9 +1194,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, /* Emit any conditional trap before any writeback. */ cond = do_cond(ctx, cf, d, dest, uv, sv); if (is_tc) { - tmp = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(tcg_env, tmp); + gen_tc(ctx, &cond); } /* Write back the result. */ @@ -1196,6 +1213,10 @@ static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_d_sh *a, { TCGv_i64 tcg_r1, tcg_r2; + if (unlikely(is_tc && a->cf == 1)) { + /* Unconditional trap on condition. */ + return gen_excp_iir(ctx, EXCP_COND); + } if (a->cf) { nullify_over(ctx); } @@ -1211,6 +1232,10 @@ static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, { TCGv_i64 tcg_im, tcg_r2; + if (unlikely(is_tc && a->cf == 1)) { + /* Unconditional trap on condition. */ + return gen_excp_iir(ctx, EXCP_COND); + } if (a->cf) { nullify_over(ctx); } @@ -1225,7 +1250,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, TCGv_i64 in2, bool is_tsv, bool is_b, bool is_tc, unsigned cf, bool d) { - TCGv_i64 dest, sv, cb, cb_msb, tmp; + TCGv_i64 dest, sv, cb, cb_msb; unsigned c = cf >> 1; DisasCond cond; @@ -1273,9 +1298,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, /* Emit any conditional trap before any writeback. */ if (is_tc) { - tmp = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(tcg_env, tmp); + gen_tc(ctx, &cond); } /* Write back the result. */ @@ -1441,9 +1464,7 @@ static void do_unit_addsub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, } if (is_tc) { - TCGv_i64 tmp = tcg_temp_new_i64(); - tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1); - gen_helper_tcond(tcg_env, tmp); + gen_tc(ctx, &cond); } save_gpr(ctx, rt, dest); From a0ea4becca28c1c14de5c3b8bff8343ab184070c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Mar 2024 13:54:01 -1000 Subject: [PATCH 30/43] target/hppa: Use delay_excp for conditional trap on overflow Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/helper.h | 1 - target/hppa/int_helper.c | 2 +- target/hppa/op_helper.c | 7 ------- target/hppa/translate.c | 21 +++++++++++++-------- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 3d0d143aed..c12b48a04a 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,5 +1,4 @@ DEF_HELPER_2(excp, noreturn, env, int) -DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_3(stby_b_parallel, TCG_CALL_NO_WG, void, env, tl, tl) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 1aa3e88ef1..97e5f0b9a7 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -134,13 +134,13 @@ void hppa_cpu_do_interrupt(CPUState *cs) switch (i) { case EXCP_ILL: case EXCP_BREAK: + case EXCP_OVERFLOW: case EXCP_COND: case EXCP_PRIV_REG: case EXCP_PRIV_OPR: /* IIR set via translate.c. */ break; - case EXCP_OVERFLOW: case EXCP_ASSIST: case EXCP_DTLB_MISS: case EXCP_NA_ITLB_MISS: diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index a8b69fd481..66cad78a57 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -42,13 +42,6 @@ G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra) cpu_loop_exit_restore(cs, ra); } -void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) -{ - if (unlikely((target_long)cond < 0)) { - hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC()); - } -} - static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr, uint32_t val, uint32_t mask, uintptr_t ra) { diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ae291124f2..16b25a9ede 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1136,6 +1136,17 @@ static void gen_tc(DisasContext *ctx, DisasCond *cond) } } +static void gen_tsv(DisasContext *ctx, TCGv_i64 *sv, bool d) +{ + DisasCond cond = do_cond(ctx, /* SV */ 12, d, NULL, NULL, *sv); + DisasDelayException *e = delay_excp(ctx, EXCP_OVERFLOW); + + tcg_gen_brcond_i64(cond.c, cond.a0, cond.a1, e->lab); + + /* In the non-trap path, V is known zero. */ + *sv = tcg_constant_i64(0); +} + static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, TCGv_i64 in2, unsigned shift, bool is_l, bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d) @@ -1178,10 +1189,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, if (is_tsv || cond_need_sv(c)) { sv = do_add_sv(ctx, dest, in1, in2, orig_in1, shift, d); if (is_tsv) { - if (!d) { - tcg_gen_ext32s_i64(sv, sv); - } - gen_helper_tsv(tcg_env, sv); + gen_tsv(ctx, &sv, d); } } @@ -1282,10 +1290,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, if (is_tsv || cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); if (is_tsv) { - if (!d) { - tcg_gen_ext32s_i64(sv, sv); - } - gen_helper_tsv(tcg_env, sv); + gen_tsv(ctx, &sv, d); } } From 3c13b0ffe76057e93e007bedbad3cc556146e3ed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 10:54:06 -1000 Subject: [PATCH 31/43] linux-user/hppa: Force all code addresses to PRIV_USER The kernel does this along the return path to user mode. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- linux-user/elfload.c | 4 ++-- linux-user/hppa/cpu_loop.c | 14 +++++++------- linux-user/hppa/signal.c | 6 ++++-- linux-user/hppa/target_cpu.h | 4 ++-- target/hppa/cpu.c | 7 +++++-- target/hppa/cpu.h | 3 +++ target/hppa/gdbstub.c | 6 ++++++ target/hppa/translate.c | 4 ++-- 8 files changed, 31 insertions(+), 17 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index b473cda6b4..c1e1511ff2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1887,8 +1887,8 @@ static inline void init_thread(struct target_pt_regs *regs, static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { - regs->iaoq[0] = infop->entry; - regs->iaoq[1] = infop->entry + 4; + regs->iaoq[0] = infop->entry | PRIV_USER; + regs->iaoq[1] = regs->iaoq[0] + 4; regs->gr[23] = 0; regs->gr[24] = infop->argv; regs->gr[25] = infop->argc; diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index d5232f37fe..bc093b8fe8 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -129,8 +129,8 @@ void cpu_loop(CPUHPPAState *env) default: env->gr[28] = ret; /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; break; case -QEMU_ERESTARTSYS: case -QEMU_ESIGRETURN: @@ -140,8 +140,8 @@ void cpu_loop(CPUHPPAState *env) case EXCP_SYSCALL_LWS: env->gr[21] = hppa_lws(env); /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; break; case EXCP_IMP: force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, env->iaoq_f); @@ -152,9 +152,9 @@ void cpu_loop(CPUHPPAState *env) case EXCP_PRIV_OPR: /* check for glibc ABORT_INSTRUCTION "iitlbp %r0,(%sr0, %r0)" */ if (env->cr[CR_IIR] == 0x04000000) { - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); } else { - force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); } break; case EXCP_PRIV_REG: @@ -170,7 +170,7 @@ void cpu_loop(CPUHPPAState *env) force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f); break; case EXCP_BREAK: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f & ~3); + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); break; case EXCP_DEBUG: force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c index 682ba25922..f6f094c960 100644 --- a/linux-user/hppa/signal.c +++ b/linux-user/hppa/signal.c @@ -101,7 +101,9 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) cpu_hppa_loaded_fr0(env); __get_user(env->iaoq_f, &sc->sc_iaoq[0]); + env->iaoq_f |= PRIV_USER; __get_user(env->iaoq_b, &sc->sc_iaoq[1]); + env->iaoq_b |= PRIV_USER; __get_user(env->cr[CR_SAR], &sc->sc_sar); } @@ -162,8 +164,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, unlock_user(fdesc, haddr, 0); haddr = dest; } - env->iaoq_f = haddr; - env->iaoq_b = haddr + 4; + env->iaoq_f = haddr | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; env->psw_n = 0; return; diff --git a/linux-user/hppa/target_cpu.h b/linux-user/hppa/target_cpu.h index aacf3e9e02..4b84422a90 100644 --- a/linux-user/hppa/target_cpu.h +++ b/linux-user/hppa/target_cpu.h @@ -28,8 +28,8 @@ static inline void cpu_clone_regs_child(CPUHPPAState *env, target_ulong newsp, /* Indicate child in return value. */ env->gr[28] = 0; /* Return from the syscall. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; } static inline void cpu_clone_regs_parent(CPUHPPAState *env, unsigned flags) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index be8c558014..a007de5521 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -32,6 +32,9 @@ static void hppa_cpu_set_pc(CPUState *cs, vaddr value) { HPPACPU *cpu = HPPA_CPU(cs); +#ifdef CONFIG_USER_ONLY + value |= PRIV_USER; +#endif cpu->env.iaoq_f = value; cpu->env.iaoq_b = value + 4; } @@ -93,8 +96,8 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); #ifdef CONFIG_USER_ONLY - cpu->env.iaoq_f = tb->pc; - cpu->env.iaoq_b = tb->cs_base; + cpu->env.iaoq_f = tb->pc | PRIV_USER; + cpu->env.iaoq_b = tb->cs_base | PRIV_USER; #else /* Recover the IAOQ values from the GVA + PRIV. */ uint32_t priv = (tb->flags >> TB_FLAG_PRIV_SHIFT) & 3; diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index c37b4e12fb..5a1e720bb6 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -42,6 +42,9 @@ #define MMU_IDX_TO_P(MIDX) (((MIDX) - MMU_KERNEL_IDX) & 1) #define PRIV_P_TO_MMU_IDX(PRIV, P) ((PRIV) * 2 + !!(P) + MMU_KERNEL_IDX) +#define PRIV_KERNEL 0 +#define PRIV_USER 3 + #define TARGET_INSN_START_EXTRA_WORDS 2 /* No need to flush MMU_ABS*_IDX */ diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index 4a965b38d7..0daa52f7af 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -163,12 +163,18 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->cr[CR_SAR] = val & (hppa_is_pa20(env) ? 63 : 31); break; case 33: +#ifdef CONFIG_USER_ONLY + val |= PRIV_USER; +#endif env->iaoq_f = val; break; case 34: env->iasq_f = (uint64_t)val << 32; break; case 35: +#ifdef CONFIG_USER_ONLY + val |= PRIV_USER; +#endif env->iaoq_b = val; break; case 36: diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 16b25a9ede..12359bafb6 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2054,7 +2054,7 @@ static void do_page_zero(DisasContext *ctx) tcg_gen_st_i64(cpu_gr[26], tcg_env, offsetof(CPUHPPAState, cr[27])); - tcg_gen_ori_i64(next.base, cpu_gr[31], 3); + tcg_gen_ori_i64(next.base, cpu_gr[31], PRIV_USER); install_iaq_entries(ctx, &next, NULL); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; } @@ -4583,7 +4583,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); #ifdef CONFIG_USER_ONLY - ctx->privilege = MMU_IDX_TO_PRIV(MMU_USER_IDX); + ctx->privilege = PRIV_USER; ctx->mmu_idx = MMU_USER_IDX; ctx->iaoq_first = ctx->base.pc_first | ctx->privilege; ctx->iaq_b.disp = ctx->base.tb->cs_base - ctx->base.pc_first; From 9dfcd2434989bb09b1ca11258180d9095c1d7ba8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 11:52:21 -1000 Subject: [PATCH 32/43] target/hppa: Store full iaoq_f and page offset of iaoq_b in TB In preparation for CF_PCREL. store the iaoq_f in 3 parts: high bits in cs_base, middle bits in pc, and low bits in priv. For iaoq_b, set a bit for either of space or page differing, else the page offset. Install iaq entries before goto_tb. The change to not record the full direct branch difference in TB means that we have to store at least iaoq_b before goto_tb. But since a later change to enable CF_PCREL will require both iaoq_f and iaoq_b to be updated before goto_tb, go ahead and update both fields now. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 72 ++++++++++++++++++----------------------- target/hppa/cpu.h | 2 ++ target/hppa/translate.c | 29 +++++++++-------- 3 files changed, 48 insertions(+), 55 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index a007de5521..003af63e20 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -48,36 +48,43 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) } void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) + uint64_t *pcsbase, uint32_t *pflags) { uint32_t flags = env->psw_n * PSW_N; + uint64_t cs_base = 0; + + /* + * TB lookup assumes that PC contains the complete virtual address. + * If we leave space+offset separate, we'll get ITLB misses to an + * incomplete virtual address. This also means that we must separate + * out current cpu privilege from the low bits of IAOQ_F. + */ + *pc = hppa_cpu_get_pc(env_cpu(env)); + flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; + + if (hppa_is_pa20(env)) { + cs_base = env->iaoq_f & MAKE_64BIT_MASK(32, 32); + } + + /* + * The only really interesting case is if IAQ_Back is on the same page + * as IAQ_Front, so that we can use goto_tb between the blocks. In all + * other cases, we'll be ending the TranslationBlock with one insn and + * not linking between them. + */ + if (env->iasq_f != env->iasq_b) { + cs_base |= CS_BASE_DIFFSPACE; + } else if ((env->iaoq_f ^ env->iaoq_b) & TARGET_PAGE_MASK) { + cs_base |= CS_BASE_DIFFPAGE; + } else { + cs_base |= env->iaoq_b & ~TARGET_PAGE_MASK; + } - /* TB lookup assumes that PC contains the complete virtual address. - If we leave space+offset separate, we'll get ITLB misses to an - incomplete virtual address. This also means that we must separate - out current cpu privilege from the low bits of IAOQ_F. */ #ifdef CONFIG_USER_ONLY - *pc = env->iaoq_f & -4; - *cs_base = env->iaoq_b & -4; flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; #else /* ??? E, T, H, L, B bits need to be here, when implemented. */ flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); - flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - - *pc = hppa_cpu_get_pc(env_cpu(env)); - *cs_base = env->iasq_f; - - /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero - low 32-bits of CS_BASE. This will succeed for all direct branches, - which is the primary case we care about -- using goto_tb within a page. - Failure is indicated by a zero difference. */ - if (env->iasq_f == env->iasq_b) { - target_long diff = env->iaoq_b - env->iaoq_f; - if (diff == (int32_t)diff) { - *cs_base |= (uint32_t)diff; - } - } if ((env->sr[4] == env->sr[5]) & (env->sr[4] == env->sr[6]) & (env->sr[4] == env->sr[7])) { @@ -85,6 +92,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, } #endif + *pcsbase = cs_base; *pflags = flags; } @@ -93,25 +101,7 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, { HPPACPU *cpu = HPPA_CPU(cs); - tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); - -#ifdef CONFIG_USER_ONLY - cpu->env.iaoq_f = tb->pc | PRIV_USER; - cpu->env.iaoq_b = tb->cs_base | PRIV_USER; -#else - /* Recover the IAOQ values from the GVA + PRIV. */ - uint32_t priv = (tb->flags >> TB_FLAG_PRIV_SHIFT) & 3; - target_ulong cs_base = tb->cs_base; - target_ulong iasq_f = cs_base & ~0xffffffffull; - int32_t diff = cs_base; - - cpu->env.iasq_f = iasq_f; - cpu->env.iaoq_f = (tb->pc & ~iasq_f) + priv; - if (diff) { - cpu->env.iaoq_b = cpu->env.iaoq_f + diff; - } -#endif - + /* IAQ is always up-to-date before goto_tb. */ cpu->env.psw_n = (tb->flags & PSW_N) != 0; } diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 5a1e720bb6..1232a4cef2 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -341,6 +341,8 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); #define TB_FLAG_SR_SAME PSW_I #define TB_FLAG_PRIV_SHIFT 8 #define TB_FLAG_UNALIGN 0x400 +#define CS_BASE_DIFFPAGE (1 << 12) +#define CS_BASE_DIFFSPACE (1 << 13) void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, uint64_t *cs_base, uint32_t *pflags); diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 12359bafb6..70df42f558 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -770,12 +770,11 @@ static bool use_nullify_skip(DisasContext *ctx) static void gen_goto_tb(DisasContext *ctx, int which, const DisasIAQE *f, const DisasIAQE *b) { + install_iaq_entries(ctx, f, b); if (use_goto_tb(ctx, f, b)) { tcg_gen_goto_tb(which); - install_iaq_entries(ctx, f, b); tcg_gen_exit_tb(ctx->base.tb, which); } else { - install_iaq_entries(ctx, f, b); tcg_gen_lookup_and_goto_ptr(); } } @@ -4576,6 +4575,7 @@ static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint64_t cs_base, iaoq_f, iaoq_b; int bound; ctx->cs = cs; @@ -4585,29 +4585,30 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #ifdef CONFIG_USER_ONLY ctx->privilege = PRIV_USER; ctx->mmu_idx = MMU_USER_IDX; - ctx->iaoq_first = ctx->base.pc_first | ctx->privilege; - ctx->iaq_b.disp = ctx->base.tb->cs_base - ctx->base.pc_first; ctx->unalign = (ctx->tb_flags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN); #else ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3; ctx->mmu_idx = (ctx->tb_flags & PSW_D ? PRIV_P_TO_MMU_IDX(ctx->privilege, ctx->tb_flags & PSW_P) : ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); +#endif /* Recover the IAOQ values from the GVA + PRIV. */ - uint64_t cs_base = ctx->base.tb->cs_base; - uint64_t iasq_f = cs_base & ~0xffffffffull; - int32_t diff = cs_base; + cs_base = ctx->base.tb->cs_base; + iaoq_f = cs_base & MAKE_64BIT_MASK(32, 32); + iaoq_f |= ctx->base.pc_first & MAKE_64BIT_MASK(2, 30); + iaoq_f |= ctx->privilege; + ctx->iaoq_first = iaoq_f; - ctx->iaoq_first = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; - - if (diff) { - ctx->iaq_b.disp = diff; - } else { - ctx->iaq_b.base = cpu_iaoq_b; + if (unlikely(cs_base & CS_BASE_DIFFSPACE)) { ctx->iaq_b.space = cpu_iasq_b; + ctx->iaq_b.base = cpu_iaoq_b; + } else if (unlikely(cs_base & CS_BASE_DIFFPAGE)) { + ctx->iaq_b.base = cpu_iaoq_b; + } else { + iaoq_b = (iaoq_f & TARGET_PAGE_MASK) | (cs_base & ~TARGET_PAGE_MASK); + ctx->iaq_b.disp = iaoq_b - iaoq_f; } -#endif ctx->zero = tcg_constant_i64(0); From 081a0ed188d8d9d9038f00337d331d185a7ae331 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Mar 2024 13:04:00 -1000 Subject: [PATCH 33/43] target/hppa: Do not mask in copy_iaoq_entry As with loads and stores, code offsets are kept intact until the full gva is formed. In qemu, this is in cpu_get_tb_cpu_state. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 70df42f558..ab8dd161ad 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -637,15 +637,10 @@ static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var) static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, const DisasIAQE *src) { - uint64_t mask = gva_offset_mask(ctx->tb_flags); - if (src->base == NULL) { - tcg_gen_movi_i64(dest, (ctx->iaoq_first + src->disp) & mask); - } else if (src->disp == 0) { - tcg_gen_andi_i64(dest, src->base, mask); + tcg_gen_movi_i64(dest, ctx->iaoq_first + src->disp); } else { tcg_gen_addi_i64(dest, src->base, src->disp); - tcg_gen_andi_i64(dest, dest, mask); } } From d2e22fde144ea5f32980031537a7e1bb88e8bcf1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Mar 2024 11:06:38 -1000 Subject: [PATCH 34/43] target/hppa: Improve hppa_cpu_dump_state Print both raw IAQ_Front and IAQ_Back as well as the GVAs. Print control registers in system mode. Print floating point registers if CPU_DUMP_FPU. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/helper.c | 60 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/target/hppa/helper.c b/target/hppa/helper.c index 9d217d051c..7d22c248fb 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -102,6 +102,19 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) { +#ifndef CONFIG_USER_ONLY + static const char cr_name[32][5] = { + "RC", "CR1", "CR2", "CR3", + "CR4", "CR5", "CR6", "CR7", + "PID1", "PID2", "CCR", "SAR", + "PID3", "PID4", "IVA", "EIEM", + "ITMR", "ISQF", "IOQF", "IIR", + "ISR", "IOR", "IPSW", "EIRR", + "TR0", "TR1", "TR2", "TR3", + "TR4", "TR5", "TR6", "TR7", + }; +#endif + CPUHPPAState *env = cpu_env(cs); target_ulong psw = cpu_hppa_get_psw(env); target_ulong psw_cb; @@ -117,11 +130,12 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) m = UINT32_MAX; } - qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx - " IIR %0*" PRIx64 "\n", + qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n" + "IA_B %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n", + env->iasq_f >> 32, w, m & env->iaoq_f, hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f), - hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b), - w, m & env->cr[CR_IIR]); + env->iasq_b >> 32, w, m & env->iaoq_b, + hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b)); psw_c[0] = (psw & PSW_W ? 'W' : '-'); psw_c[1] = (psw & PSW_E ? 'E' : '-'); @@ -154,12 +168,46 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags) (i & 3) == 3 ? '\n' : ' '); } #ifndef CONFIG_USER_ONLY + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "%-4s %0*" PRIx64 "%c", + cr_name[i], w, m & env->cr[i], + (i & 3) == 3 ? '\n' : ' '); + } + qemu_fprintf(f, "ISQB %0*" PRIx64 " IOQB %0*" PRIx64 "\n", + w, m & env->cr_back[0], w, m & env->cr_back[1]); for (i = 0; i < 8; i++) { qemu_fprintf(f, "SR%02d %08x%c", i, (uint32_t)(env->sr[i] >> 32), (i & 3) == 3 ? '\n' : ' '); } #endif - qemu_fprintf(f, "\n"); - /* ??? FR */ + if (flags & CPU_DUMP_FPU) { + static const char rm[4][4] = { "RN", "RZ", "R+", "R-" }; + char flg[6], ena[6]; + uint32_t fpsr = env->fr0_shadow; + + flg[0] = (fpsr & R_FPSR_FLG_V_MASK ? 'V' : '-'); + flg[1] = (fpsr & R_FPSR_FLG_Z_MASK ? 'Z' : '-'); + flg[2] = (fpsr & R_FPSR_FLG_O_MASK ? 'O' : '-'); + flg[3] = (fpsr & R_FPSR_FLG_U_MASK ? 'U' : '-'); + flg[4] = (fpsr & R_FPSR_FLG_I_MASK ? 'I' : '-'); + flg[5] = '\0'; + + ena[0] = (fpsr & R_FPSR_ENA_V_MASK ? 'V' : '-'); + ena[1] = (fpsr & R_FPSR_ENA_Z_MASK ? 'Z' : '-'); + ena[2] = (fpsr & R_FPSR_ENA_O_MASK ? 'O' : '-'); + ena[3] = (fpsr & R_FPSR_ENA_U_MASK ? 'U' : '-'); + ena[4] = (fpsr & R_FPSR_ENA_I_MASK ? 'I' : '-'); + ena[5] = '\0'; + + qemu_fprintf(f, "FPSR %08x flag %s enable %s %s\n", + fpsr, flg, ena, rm[FIELD_EX32(fpsr, FPSR, RM)]); + + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "FR%02d %016" PRIx64 "%c", + i, env->fr[i], (i & 3) == 3 ? '\n' : ' '); + } + } + + qemu_fprintf(f, "\n"); } From ebc9401a4067fd61afea811d1d059d8ac0fc5db9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 20:23:16 -0700 Subject: [PATCH 35/43] target/hppa: Split PSW X and B into their own field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generally, both of these bits are cleared at the end of each instruction. By separating these, we will be able to clear both with a single insn, instead of 2 or 3. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 3 ++- target/hppa/helper.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 1232a4cef2..f247ad56d7 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -208,7 +208,8 @@ typedef struct CPUArchState { uint64_t fr[32]; uint64_t sr[8]; /* stored shifted into place for gva */ - target_ulong psw; /* All psw bits except the following: */ + uint32_t psw; /* All psw bits except the following: */ + uint32_t psw_xb; /* X and B, in their normal positions */ target_ulong psw_n; /* boolean */ target_long psw_v; /* in most significant bit */ diff --git a/target/hppa/helper.c b/target/hppa/helper.c index 7d22c248fb..b79ddd8184 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -54,7 +54,7 @@ target_ulong cpu_hppa_get_psw(CPUHPPAState *env) psw |= env->psw_n * PSW_N; psw |= (env->psw_v < 0) * PSW_V; - psw |= env->psw; + psw |= env->psw | env->psw_xb; return psw; } @@ -76,8 +76,8 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) } psw &= ~reserved; - env->psw = psw & (uint32_t)~(PSW_N | PSW_V | PSW_CB); - + env->psw = psw & (uint32_t)~(PSW_B | PSW_N | PSW_V | PSW_X | PSW_CB); + env->psw_xb = psw & (PSW_X | PSW_B); env->psw_n = (psw / PSW_N) & 1; env->psw_v = -((psw / PSW_V) & 1); From d27fe7c3af30a9b6281e7aafb5d603efe64ff939 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 20:43:00 -0700 Subject: [PATCH 36/43] target/hppa: Manage PSW_X and PSW_B in translator PSW_X is cleared after every instruction, and only set by RFI. PSW_B is cleared after every non-branch, or branch not taken, and only set by taken branches. We can clear both bits with a single store, at most once per TB. Taken branches set PSW_B, at most once per TB. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 10 ++++++--- target/hppa/translate.c | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 003af63e20..5f0df0697a 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -50,7 +50,7 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, uint64_t *pcsbase, uint32_t *pflags) { - uint32_t flags = env->psw_n * PSW_N; + uint32_t flags = 0; uint64_t cs_base = 0; /* @@ -80,11 +80,14 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, cs_base |= env->iaoq_b & ~TARGET_PAGE_MASK; } + /* ??? E, T, H, L bits need to be here, when implemented. */ + flags |= env->psw_n * PSW_N; + flags |= env->psw_xb; + flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); + #ifdef CONFIG_USER_ONLY flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; #else - /* ??? E, T, H, L, B bits need to be here, when implemented. */ - flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); if ((env->sr[4] == env->sr[5]) & (env->sr[4] == env->sr[6]) & (env->sr[4] == env->sr[7])) { @@ -103,6 +106,7 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, /* IAQ is always up-to-date before goto_tb. */ cpu->env.psw_n = (tb->flags & PSW_N) != 0; + cpu->env.psw_xb = tb->flags & (PSW_X | PSW_B); } static void hppa_restore_state_to_opc(CPUState *cs, diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ab8dd161ad..f7d54f4009 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -84,7 +84,9 @@ typedef struct DisasContext { uint32_t tb_flags; int mmu_idx; int privilege; + uint32_t psw_xb; bool psw_n_nonzero; + bool psw_b_next; bool is_pa20; bool insn_start_updated; @@ -263,6 +265,7 @@ static TCGv_i64 cpu_psw_n; static TCGv_i64 cpu_psw_v; static TCGv_i64 cpu_psw_cb; static TCGv_i64 cpu_psw_cb_msb; +static TCGv_i32 cpu_psw_xb; void hppa_translate_init(void) { @@ -315,6 +318,9 @@ void hppa_translate_init(void) *v->var = tcg_global_mem_new(tcg_env, v->ofs, v->name); } + cpu_psw_xb = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHPPAState, psw_xb), + "psw_xb"); cpu_iasq_f = tcg_global_mem_new_i64(tcg_env, offsetof(CPUHPPAState, iasq_f), "iasq_f"); @@ -509,6 +515,25 @@ static void load_spr(DisasContext *ctx, TCGv_i64 dest, unsigned reg) #endif } +/* + * Write a value to psw_xb, bearing in mind the known value. + * To be used just before exiting the TB, so do not update the known value. + */ +static void store_psw_xb(DisasContext *ctx, uint32_t xb) +{ + tcg_debug_assert(xb == 0 || xb == PSW_B); + if (ctx->psw_xb != xb) { + tcg_gen_movi_i32(cpu_psw_xb, xb); + } +} + +/* Write a value to psw_xb, and update the known value. */ +static void set_psw_xb(DisasContext *ctx, uint32_t xb) +{ + store_psw_xb(ctx, xb); + ctx->psw_xb = xb; +} + /* Skip over the implementation of an insn that has been nullified. Use this when the insn is too complex for a conditional move. */ static void nullify_over(DisasContext *ctx) @@ -576,6 +601,8 @@ static bool nullify_end(DisasContext *ctx) /* For NEXT, NORETURN, STALE, we can easily continue (or exit). For UPDATED, we cannot update on the nullified path. */ assert(status != DISAS_IAQ_N_UPDATED); + /* Taken branches are handled manually. */ + assert(!ctx->psw_b_next); if (likely(null_lab == NULL)) { /* The current insn wasn't conditional or handled the condition @@ -1843,6 +1870,7 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, if (is_n) { if (use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); ctx->base.is_jmp = DISAS_NORETURN; return true; @@ -1850,20 +1878,24 @@ static bool do_dbranch(DisasContext *ctx, int64_t disp, ctx->null_cond.c = TCG_COND_ALWAYS; } ctx->iaq_n = &ctx->iaq_j; + ctx->psw_b_next = true; } else { nullify_over(ctx); install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); } else { nullify_set(ctx, is_n); + store_psw_xb(ctx, PSW_B); gen_goto_tb(ctx, 0, &ctx->iaq_b, &ctx->iaq_j); } nullify_end(ctx); nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 1, &ctx->iaq_b, NULL); ctx->base.is_jmp = DISAS_NORETURN; } @@ -1894,6 +1926,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, n = is_n && disp < 0; if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); next = iaqe_incr(&ctx->iaq_b, 4); gen_goto_tb(ctx, 0, &next, NULL); } else { @@ -1902,6 +1935,7 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, ctx->null_lab = NULL; } nullify_set(ctx, n); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 0, &ctx->iaq_b, NULL); } @@ -1913,9 +1947,11 @@ static bool do_cbranch(DisasContext *ctx, int64_t disp, bool is_n, next = iaqe_branchi(ctx, disp); if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); + store_psw_xb(ctx, 0); gen_goto_tb(ctx, 1, &next, NULL); } else { nullify_set(ctx, n); + store_psw_xb(ctx, PSW_B); gen_goto_tb(ctx, 1, &ctx->iaq_b, &next); } @@ -1949,6 +1985,7 @@ static bool do_ibranch(DisasContext *ctx, unsigned link, ctx->null_cond.c = TCG_COND_ALWAYS; } ctx->iaq_n = &ctx->iaq_j; + ctx->psw_b_next = true; return true; } @@ -1958,9 +1995,11 @@ static bool do_ibranch(DisasContext *ctx, unsigned link, if (is_n && use_nullify_skip(ctx)) { install_iaq_entries(ctx, &ctx->iaq_j, NULL); nullify_set(ctx, 0); + store_psw_xb(ctx, 0); } else { install_iaq_entries(ctx, &ctx->iaq_b, &ctx->iaq_j); nullify_set(ctx, is_n); + store_psw_xb(ctx, PSW_B); } tcg_gen_lookup_and_goto_ptr(); @@ -2387,6 +2426,7 @@ static bool trans_halt(DisasContext *ctx, arg_halt *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY + set_psw_xb(ctx, 0); nullify_over(ctx); gen_helper_halt(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; @@ -2398,6 +2438,7 @@ static bool trans_reset(DisasContext *ctx, arg_reset *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY + set_psw_xb(ctx, 0); nullify_over(ctx); gen_helper_reset(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; @@ -2792,6 +2833,9 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf_d *a) if ((rt == 10 || rt == 31) && r1 == rt && r2 == rt) { /* PAUSE */ /* No need to check for supervisor, as userland can only pause until the next timer interrupt. */ + + set_psw_xb(ctx, 0); + nullify_over(ctx); /* Advance the instruction queue. */ @@ -4576,6 +4620,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cs = cs; ctx->tb_flags = ctx->base.tb->flags; ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); + ctx->psw_xb = ctx->tb_flags & (PSW_X | PSW_B); #ifdef CONFIG_USER_ONLY ctx->privilege = PRIV_USER; @@ -4662,6 +4707,7 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) */ ctx->iaq_n = NULL; memset(&ctx->iaq_j, 0, sizeof(ctx->iaq_j)); + ctx->psw_b_next = false; if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { ctx->null_cond.c = TCG_COND_NEVER; @@ -4674,6 +4720,10 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ret = ctx->base.is_jmp; assert(ctx->null_lab == NULL); } + + if (ret != DISAS_NORETURN) { + set_psw_xb(ctx, ctx->psw_b_next ? PSW_B : 0); + } } /* If the TranslationBlock must end, do so. */ From 5ae8adbb01cdb0bd2f3c1c444b3e543b38737102 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 20:50:30 -0700 Subject: [PATCH 37/43] target/hppa: Implement PSW_B PSW_B causes B,GATE to trap as an illegal instruction, removing our previous sequential execution test that was merely an approximation. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/translate.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index f7d54f4009..f40ac92e98 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2062,11 +2062,8 @@ static void do_page_zero(DisasContext *ctx) g_assert_not_reached(); } - /* Check that we didn't arrive here via some means that allowed - non-sequential instruction execution. Normally the PSW[B] bit - detects this by disallowing the B,GATE instruction to execute - under such conditions. */ - if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != 4) { + /* If PSW[B] is set, the B,GATE insn would trap. */ + if (ctx->psw_xb & PSW_B) { goto do_sigill; } @@ -3965,23 +3962,13 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { int64_t disp = a->disp; - nullify_over(ctx); - - /* Make sure the caller hasn't done something weird with the queue. - * ??? This is not quite the same as the PSW[B] bit, which would be - * expensive to track. Real hardware will trap for - * b gateway - * b gateway+4 (in delay slot of first branch) - * However, checking for a non-sequential instruction queue *will* - * diagnose the security hole - * b gateway - * b evil - * in which instructions at evil would run with increased privs. - */ - if (iaqe_variable(&ctx->iaq_b) || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { + /* Trap if PSW[B] is set. */ + if (ctx->psw_xb & PSW_B) { return gen_illegal(ctx); } + nullify_over(ctx); + #ifndef CONFIG_USER_ONLY if (ctx->tb_flags & PSW_C) { int type = hppa_artype_for_page(cpu_env(ctx->cs), ctx->base.pc_next); From d8bc1381250b39079d5ed33d2be7eee801ca41c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 17:39:25 -0700 Subject: [PATCH 38/43] target/hppa: Implement PSW_X Use PAGE_WRITE_INV to temporarily enable write permission on for a given page, driven by PSW_X being set. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/mem_helper.c | 46 +++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index d09877afd7..ca7bbe0a7c 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -296,30 +296,38 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, goto egress; } - /* In reverse priority order, check for conditions which raise faults. - As we go, remove PROT bits that cover the condition we want to check. - In this way, the resulting PROT will force a re-check of the - architectural TLB entry for the next access. */ - if (unlikely(!ent->d)) { - if (type & PAGE_WRITE) { - /* The D bit is not set -- TLB Dirty Bit Fault. */ - ret = EXCP_TLB_DIRTY; - } - prot &= PAGE_READ | PAGE_EXEC; - } - if (unlikely(ent->b)) { - if (type & PAGE_WRITE) { - /* The B bit is set -- Data Memory Break Fault. */ - ret = EXCP_DMB; - } - prot &= PAGE_READ | PAGE_EXEC; - } + /* + * In priority order, check for conditions which raise faults. + * Remove PROT bits that cover the condition we want to check, + * so that the resulting PROT will force a re-check of the + * architectural TLB entry for the next access. + */ if (unlikely(ent->t)) { + prot &= PAGE_EXEC; if (!(type & PAGE_EXEC)) { /* The T bit is set -- Page Reference Fault. */ ret = EXCP_PAGE_REF; } - prot &= PAGE_EXEC; + } else if (!ent->d) { + prot &= PAGE_READ | PAGE_EXEC; + if (type & PAGE_WRITE) { + /* The D bit is not set -- TLB Dirty Bit Fault. */ + ret = EXCP_TLB_DIRTY; + } + } else if (unlikely(ent->b)) { + prot &= PAGE_READ | PAGE_EXEC; + if (type & PAGE_WRITE) { + /* + * The B bit is set -- Data Memory Break Fault. + * Except when PSW_X is set, allow this single access to succeed. + * The write bit will be invalidated for subsequent accesses. + */ + if (env->psw_xb & PSW_X) { + prot |= PAGE_WRITE_INV; + } else { + ret = EXCP_DMB; + } + } } egress: From 190d7fa5721dced9704717d64b247552a9152482 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 16:50:58 -0700 Subject: [PATCH 39/43] target/hppa: Drop tlb_entry return from hppa_get_physical_address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return-by-reference is never used. Reviewed-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 3 +-- target/hppa/int_helper.c | 2 +- target/hppa/mem_helper.c | 19 ++++--------------- target/hppa/op_helper.c | 3 +-- 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index f247ad56d7..78ab0adcd0 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -371,8 +371,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, void hppa_cpu_do_interrupt(CPUState *cpu); bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, - int type, hwaddr *pphys, int *pprot, - HPPATLBEntry **tlb_entry); + int type, hwaddr *pphys, int *pprot); void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, unsigned size, MMUAccessType access_type, diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 97e5f0b9a7..b82f32fd12 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -167,7 +167,7 @@ void hppa_cpu_do_interrupt(CPUState *cs) vaddr = hppa_form_gva_psw(old_psw, env->iasq_f, vaddr); t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX, - 0, &paddr, &prot, NULL); + 0, &paddr, &prot); if (t >= 0) { /* We can't re-load the instruction. */ env->cr[CR_IIR] = 0; diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index ca7bbe0a7c..2929226874 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -197,18 +197,13 @@ static int match_prot_id64(CPUHPPAState *env, uint32_t access_id) } int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, - int type, hwaddr *pphys, int *pprot, - HPPATLBEntry **tlb_entry) + int type, hwaddr *pphys, int *pprot) { hwaddr phys; int prot, r_prot, w_prot, x_prot, priv; HPPATLBEntry *ent; int ret = -1; - if (tlb_entry) { - *tlb_entry = NULL; - } - /* Virtual translation disabled. Map absolute to physical. */ if (MMU_IDX_MMU_DISABLED(mmu_idx)) { switch (mmu_idx) { @@ -238,10 +233,6 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, goto egress; } - if (tlb_entry) { - *tlb_entry = ent; - } - /* We now know the physical address. */ phys = ent->pa + (addr - ent->itree.start); @@ -350,7 +341,7 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) cpu->env.psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0, - &phys, &prot, NULL); + &phys, &prot); /* Since we're translating for debugging, the only error that is a hard error is no translation at all. Otherwise, while a real cpu @@ -432,7 +423,6 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, { HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; - HPPATLBEntry *ent; int prot, excp, a_prot; hwaddr phys; @@ -448,8 +438,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, break; } - excp = hppa_get_physical_address(env, addr, mmu_idx, - a_prot, &phys, &prot, &ent); + excp = hppa_get_physical_address(env, addr, mmu_idx, a_prot, &phys, &prot); if (unlikely(excp >= 0)) { if (probe) { return false; @@ -690,7 +679,7 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr) int prot, excp; excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0, - &phys, &prot, NULL); + &phys, &prot); if (excp >= 0) { if (excp == EXCP_DTLB_MISS) { excp = EXCP_NA_DTLB_MISS; diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 66cad78a57..7f79196fff 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -334,8 +334,7 @@ target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr, } mmu_idx = PRIV_P_TO_MMU_IDX(level, env->psw & PSW_P); - excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, - &prot, NULL); + excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, &prot); if (excp >= 0) { cpu_restore_state(env_cpu(env), GETPC()); hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx)); From 804cd52d3a314799adfa7d931e00c85856c54206 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 20:39:15 -0700 Subject: [PATCH 40/43] target/hppa: Adjust priv for B,GATE at runtime Do not compile in the priv change based on the first translation; look up the PTE at execution time. This is required for CF_PCREL, where a page may be mapped multiple times with different attributes. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.h | 1 - target/hppa/helper.h | 1 + target/hppa/mem_helper.c | 34 +++++++++++++++++++++++++++------- target/hppa/translate.c | 36 +++++++++++++++++++----------------- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 78ab0adcd0..2bcb3b602b 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -380,7 +380,6 @@ void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, extern const MemoryRegionOps hppa_io_eir_ops; extern const VMStateDescription vmstate_hppa_cpu; void hppa_cpu_alarm_timer(void *); -int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr); #endif G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra); diff --git a/target/hppa/helper.h b/target/hppa/helper.h index c12b48a04a..de411923d9 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -86,6 +86,7 @@ DEF_HELPER_1(halt, noreturn, env) DEF_HELPER_1(reset, noreturn, env) DEF_HELPER_1(rfi, void, env) DEF_HELPER_1(rfi_r, void, env) +DEF_HELPER_FLAGS_2(b_gate_priv, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(write_eirr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(swap_system_mask, TCG_CALL_NO_RWG, tl, env, tl) diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 2929226874..b984f730aa 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -691,13 +691,6 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr) return phys; } -/* Return the ar_type of the TLB at VADDR, or -1. */ -int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr) -{ - HPPATLBEntry *ent = hppa_find_tlb(env, vaddr); - return ent ? ent->ar_type : -1; -} - /* * diag_btlb() emulates the PDC PDC_BLOCK_TLB firmware call to * allow operating systems to modify the Block TLB (BTLB) entries. @@ -793,3 +786,30 @@ void HELPER(diag_btlb)(CPUHPPAState *env) break; } } + +uint64_t HELPER(b_gate_priv)(CPUHPPAState *env, uint64_t iaoq_f) +{ + uint64_t gva = hppa_form_gva(env, env->iasq_f, iaoq_f); + HPPATLBEntry *ent = hppa_find_tlb(env, gva); + + if (ent == NULL) { + raise_exception_with_ior(env, EXCP_ITLB_MISS, GETPC(), gva, false); + } + + /* + * There should be no need to check page permissions, as that will + * already have been done by tb_lookup via get_page_addr_code. + * All we need at this point is to check the ar_type. + * + * No change for non-gateway pages or for priv decrease. + */ + if (ent->ar_type & 4) { + int old_priv = iaoq_f & 3; + int new_priv = ent->ar_type & 3; + + if (new_priv < old_priv) { + iaoq_f = (iaoq_f & -4) | new_priv; + } + } + return iaoq_f; +} diff --git a/target/hppa/translate.c b/target/hppa/translate.c index f40ac92e98..c2cee89a6a 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -3961,6 +3961,7 @@ static bool trans_bl(DisasContext *ctx, arg_bl *a) static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { int64_t disp = a->disp; + bool indirect = false; /* Trap if PSW[B] is set. */ if (ctx->psw_xb & PSW_B) { @@ -3970,24 +3971,22 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) nullify_over(ctx); #ifndef CONFIG_USER_ONLY - if (ctx->tb_flags & PSW_C) { - int type = hppa_artype_for_page(cpu_env(ctx->cs), ctx->base.pc_next); - /* If we could not find a TLB entry, then we need to generate an - ITLB miss exception so the kernel will provide it. - The resulting TLB fill operation will invalidate this TB and - we will re-translate, at which point we *will* be able to find - the TLB entry and determine if this is in fact a gateway page. */ - if (type < 0) { - gen_excp(ctx, EXCP_ITLB_MISS); - return true; - } - /* No change for non-gateway pages or for priv decrease. */ - if (type >= 4 && type - 4 < ctx->privilege) { - disp -= ctx->privilege; - disp += type - 4; - } + if (ctx->privilege == 0) { + /* Privilege cannot decrease. */ + } else if (!(ctx->tb_flags & PSW_C)) { + /* With paging disabled, priv becomes 0. */ + disp -= ctx->privilege; } else { - disp -= ctx->privilege; /* priv = 0 */ + /* Adjust the dest offset for the privilege change from the PTE. */ + TCGv_i64 off = tcg_temp_new_i64(); + + gen_helper_b_gate_priv(off, tcg_env, + tcg_constant_i64(ctx->iaoq_first + + ctx->iaq_f.disp)); + + ctx->iaq_j.base = off; + ctx->iaq_j.disp = disp + 8; + indirect = true; } #endif @@ -4000,6 +3999,9 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) save_gpr(ctx, a->l, tmp); } + if (indirect) { + return do_ibranch(ctx, 0, false, a->n); + } return do_dbranch(ctx, disp, 0, a->n); } From 6dd9b145f65ea425b220426f276b9074bbd429fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 13 Apr 2024 20:57:13 -0700 Subject: [PATCH 41/43] target/hppa: Implement CF_PCREL Now that the groundwork has been laid, enabling CF_PCREL within the translator proper is a simple matter of updating copy_iaoq_entry and install_iaq_entries. We also need to modify the unwind info, since we no longer have absolute addresses to install. As expected, this reduces the runtime overhead of compilation when running a Linux kernel with address space randomization enabled. Reviewed-by: Helge Deller Signed-off-by: Richard Henderson --- target/hppa/cpu.c | 19 ++++++------ target/hppa/translate.c | 68 ++++++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 32 deletions(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 5f0df0697a..f0507874ce 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -62,10 +62,6 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, *pc = hppa_cpu_get_pc(env_cpu(env)); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; - if (hppa_is_pa20(env)) { - cs_base = env->iaoq_f & MAKE_64BIT_MASK(32, 32); - } - /* * The only really interesting case is if IAQ_Back is on the same page * as IAQ_Front, so that we can use goto_tb between the blocks. In all @@ -113,19 +109,19 @@ static void hppa_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = cpu_env(cs); - cpu->env.iaoq_f = data[0]; - if (data[1] != (target_ulong)-1) { - cpu->env.iaoq_b = data[1]; + env->iaoq_f = (env->iaoq_f & TARGET_PAGE_MASK) | data[0]; + if (data[1] != INT32_MIN) { + env->iaoq_b = env->iaoq_f + data[1]; } - cpu->env.unwind_breg = data[2]; + env->unwind_breg = data[2]; /* * Since we were executing the instruction at IAOQ_F, and took some * sort of action that provoked the cpu_restore_state, we can infer * that the instruction was not nullified. */ - cpu->env.psw_n = 0; + env->psw_n = 0; } static bool hppa_cpu_has_work(CPUState *cs) @@ -191,6 +187,9 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) hppa_ptlbe(&cpu->env); } #endif + + /* Use pc-relative instructions always to simplify the translator. */ + tcg_cflags_set(cs, CF_PCREL); } static void hppa_cpu_initfn(Object *obj) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c2cee89a6a..c0920a3c29 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -47,7 +47,7 @@ typedef struct DisasIAQE { TCGv_i64 space; /* IAOQ base; may be null for relative address. */ TCGv_i64 base; - /* IAOQ addend; if base is null, relative to ctx->iaoq_first. */ + /* IAOQ addend; if base is null, relative to cpu_iaoq_f. */ int64_t disp; } DisasIAQE; @@ -664,11 +664,7 @@ static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var) static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, const DisasIAQE *src) { - if (src->base == NULL) { - tcg_gen_movi_i64(dest, ctx->iaoq_first + src->disp); - } else { - tcg_gen_addi_i64(dest, src->base, src->disp); - } + tcg_gen_addi_i64(dest, src->base ? : cpu_iaoq_f, src->disp); } static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f, @@ -680,8 +676,28 @@ static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f, b_next = iaqe_incr(f, 4); b = &b_next; } - copy_iaoq_entry(ctx, cpu_iaoq_f, f); - copy_iaoq_entry(ctx, cpu_iaoq_b, b); + + /* + * There is an edge case + * bv r0(rN) + * b,l disp,r0 + * for which F will use cpu_iaoq_b (from the indirect branch), + * and B will use cpu_iaoq_f (from the direct branch). + * In this case we need an extra temporary. + */ + if (f->base != cpu_iaoq_b) { + copy_iaoq_entry(ctx, cpu_iaoq_b, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + } else if (f->base == b->base) { + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_f, b->disp - f->disp); + } else { + TCGv_i64 tmp = tcg_temp_new_i64(); + copy_iaoq_entry(ctx, tmp, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + tcg_gen_mov_i64(cpu_iaoq_b, tmp); + } + if (f->space) { tcg_gen_mov_i64(cpu_iasq_f, f->space); } @@ -3980,9 +3996,8 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) /* Adjust the dest offset for the privilege change from the PTE. */ TCGv_i64 off = tcg_temp_new_i64(); - gen_helper_b_gate_priv(off, tcg_env, - tcg_constant_i64(ctx->iaoq_first - + ctx->iaq_f.disp)); + copy_iaoq_entry(ctx, off, &ctx->iaq_f); + gen_helper_b_gate_priv(off, tcg_env, off); ctx->iaq_j.base = off; ctx->iaq_j.disp = disp + 8; @@ -4603,7 +4618,7 @@ static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a) static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); - uint64_t cs_base, iaoq_f, iaoq_b; + uint64_t cs_base; int bound; ctx->cs = cs; @@ -4622,12 +4637,8 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) : ctx->tb_flags & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX); #endif - /* Recover the IAOQ values from the GVA + PRIV. */ cs_base = ctx->base.tb->cs_base; - iaoq_f = cs_base & MAKE_64BIT_MASK(32, 32); - iaoq_f |= ctx->base.pc_first & MAKE_64BIT_MASK(2, 30); - iaoq_f |= ctx->privilege; - ctx->iaoq_first = iaoq_f; + ctx->iaoq_first = ctx->base.pc_first + ctx->privilege; if (unlikely(cs_base & CS_BASE_DIFFSPACE)) { ctx->iaq_b.space = cpu_iasq_b; @@ -4635,8 +4646,9 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) } else if (unlikely(cs_base & CS_BASE_DIFFPAGE)) { ctx->iaq_b.base = cpu_iaoq_b; } else { - iaoq_b = (iaoq_f & TARGET_PAGE_MASK) | (cs_base & ~TARGET_PAGE_MASK); - ctx->iaq_b.disp = iaoq_b - iaoq_f; + uint64_t iaoq_f_pgofs = ctx->iaoq_first & ~TARGET_PAGE_MASK; + uint64_t iaoq_b_pgofs = cs_base & ~TARGET_PAGE_MASK; + ctx->iaq_b.disp = iaoq_b_pgofs - iaoq_f_pgofs; } ctx->zero = tcg_constant_i64(0); @@ -4663,11 +4675,23 @@ static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint64_t iaoq_f, iaoq_b; + int64_t diff; tcg_debug_assert(!iaqe_variable(&ctx->iaq_f)); - tcg_gen_insn_start(ctx->iaoq_first + ctx->iaq_f.disp, - (iaqe_variable(&ctx->iaq_b) ? -1 : - ctx->iaoq_first + ctx->iaq_b.disp), 0); + + iaoq_f = ctx->iaoq_first + ctx->iaq_f.disp; + if (iaqe_variable(&ctx->iaq_b)) { + diff = INT32_MIN; + } else { + iaoq_b = ctx->iaoq_first + ctx->iaq_b.disp; + diff = iaoq_b - iaoq_f; + /* Direct branches can only produce a 24-bit displacement. */ + tcg_debug_assert(diff == (int32_t)diff); + tcg_debug_assert(diff != INT32_MIN); + } + + tcg_gen_insn_start(iaoq_f & ~TARGET_PAGE_MASK, diff, 0); ctx->insn_start_updated = false; } From 12959fcdcf87dd97aabf844f57329d46e9c54da3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 21:27:56 -0700 Subject: [PATCH 42/43] target/hppa: Log cpu state at interrupt This contains all of the information logged before, plus more. Signed-off-by: Richard Henderson --- target/hppa/int_helper.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index b82f32fd12..391f32f27d 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -241,21 +241,22 @@ void hppa_cpu_do_interrupt(CPUState *cs) [EXCP_SYSCALL_LWS] = "syscall-lws", [EXCP_TOC] = "TOC (transfer of control)", }; - static int count; - const char *name = NULL; - char unknown[16]; - if (i >= 0 && i < ARRAY_SIZE(names)) { - name = names[i]; + FILE *logfile = qemu_log_trylock(); + if (logfile) { + const char *name = NULL; + + if (i >= 0 && i < ARRAY_SIZE(names)) { + name = names[i]; + } + if (name) { + fprintf(logfile, "INT: cpu %d %s\n", cs->cpu_index, name); + } else { + fprintf(logfile, "INT: cpu %d unknown %d\n", cs->cpu_index, i); + } + hppa_cpu_dump_state(cs, logfile, 0); + qemu_log_unlock(logfile); } - if (!name) { - snprintf(unknown, sizeof(unknown), "unknown %d", i); - name = unknown; - } - qemu_log("INT %6d: %s @ " TARGET_FMT_lx ":" TARGET_FMT_lx - " for " TARGET_FMT_lx ":" TARGET_FMT_lx "\n", - ++count, name, env->cr[CR_IIASQ], env->cr[CR_IIAOQ], - env->cr[CR_ISR], env->cr[CR_IOR]); } cs->exception_index = -1; } From 9e035f00788c52a6f51529c54371a611d9f8b089 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 16 Apr 2024 21:32:24 -0700 Subject: [PATCH 43/43] target/hppa: Log cpu state on return-from-interrupt Inverse of the logging on taking an interrupt. Signed-off-by: Richard Henderson --- target/hppa/sys_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 22d6c89964..9b43b556fd 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" @@ -93,6 +94,17 @@ void HELPER(rfi)(CPUHPPAState *env) env->iaoq_b = env->cr_back[1]; env->iasq_f = (env->cr[CR_IIASQ] << 32) & ~(env->iaoq_f & mask); env->iasq_b = (env->cr_back[0] << 32) & ~(env->iaoq_b & mask); + + if (qemu_loglevel_mask(CPU_LOG_INT)) { + FILE *logfile = qemu_log_trylock(); + if (logfile) { + CPUState *cs = env_cpu(env); + + fprintf(logfile, "RFI: cpu %d\n", cs->cpu_index); + hppa_cpu_dump_state(cs, logfile, 0); + qemu_log_unlock(logfile); + } + } } static void getshadowregs(CPUHPPAState *env)