target/nios2: Introduce shadow register sets

Do not actually enable them so far, in terms of being able
to change the current register set, but add all of the
plumbing to address them.  Do not enable them for user-only.

Add an env->regs pointer that handles the indirection to
the current register set.  The naming of the pointer hides
the difference between old and new, user-only and sysemu.

From the notes on wrprs, which states that r0 must be initialized
before use in shadow register sets, infer that R_ZERO is *not*
hardwired to zero in shadow register sets, but that it is still
read-only.  Introduce tbflags bit R0_0 to track that it has been
properly set to zero.  Adjust load_gpr to reflect this.

At the same time we might as well special case crs == 0 to avoid
the indirection through env->regs during translation as well; this
is intended to be the most common case for non-interrupt handlers.

Init env->regs at reset.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20220421151735.31996-54-richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-04-21 08:17:24 -07:00
parent 410c6aaa3b
commit 945a5bd3f8
3 changed files with 83 additions and 11 deletions

View file

@ -48,15 +48,17 @@ static void nios2_cpu_reset(DeviceState *dev)
ncc->parent_reset(dev);
memset(env->regs, 0, sizeof(env->regs));
memset(env->ctrl, 0, sizeof(env->ctrl));
env->pc = cpu->reset_addr;
#if defined(CONFIG_USER_ONLY)
/* Start in user mode with interrupts enabled. */
env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE;
memset(env->regs, 0, sizeof(env->regs));
#else
env->ctrl[CR_STATUS] = CR_STATUS_RSIE;
nios2_update_crs(env);
memset(env->shadow_regs, 0, sizeof(env->shadow_regs));
#endif
}

View file

@ -60,6 +60,11 @@ struct Nios2CPUClass {
#define NUM_GP_REGS 32
#define NUM_CR_REGS 32
#ifndef CONFIG_USER_ONLY
/* 63 shadow register sets; index 0 is the primary register set. */
#define NUM_REG_SETS 64
#endif
/* General purpose register aliases */
enum {
R_ZERO = 0,
@ -178,7 +183,13 @@ FIELD(CR_TLBMISC, EE, 24, 1)
#define EXCP_MPUD 17
struct CPUArchState {
#ifdef CONFIG_USER_ONLY
uint32_t regs[NUM_GP_REGS];
#else
uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS];
/* Pointer into shadow_regs for the current register set. */
uint32_t *regs;
#endif
uint32_t ctrl[NUM_CR_REGS];
uint32_t pc;
@ -229,6 +240,14 @@ static inline bool nios2_cr_reserved(const ControlRegState *s)
return (s->writable | s->readonly) == 0;
}
static inline void nios2_update_crs(CPUNios2State *env)
{
#ifndef CONFIG_USER_ONLY
unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
env->regs = env->shadow_regs[crs];
#endif
}
void nios2_tcg_init(void);
void nios2_cpu_do_interrupt(CPUState *cs);
void dump_mmu(CPUNios2State *env);
@ -267,12 +286,20 @@ typedef Nios2CPU ArchCPU;
#include "exec/cpu-all.h"
FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */
FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */
FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */
static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *flags)
{
unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
*pc = env->pc;
*cs_base = 0;
*flags = env->ctrl[CR_STATUS] & CR_STATUS_U;
*flags = (env->ctrl[CR_STATUS] & CR_STATUS_U)
| (crs ? 0 : R_TBFLAGS_CRS0_MASK)
| (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK);
}
#endif /* NIOS2_CPU_H */

View file

@ -127,12 +127,16 @@ typedef struct DisasContext {
DisasContextBase base;
target_ulong pc;
int mem_idx;
uint32_t tb_flags;
TCGv sink;
const ControlRegState *cr_state;
} DisasContext;
static TCGv cpu_R[NUM_GP_REGS];
static TCGv cpu_pc;
#ifndef CONFIG_USER_ONLY
static TCGv cpu_crs_R[NUM_GP_REGS];
#endif
typedef struct Nios2Instruction {
void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags);
@ -154,22 +158,47 @@ static uint8_t get_opxcode(uint32_t code)
static TCGv load_gpr(DisasContext *dc, unsigned reg)
{
assert(reg < NUM_GP_REGS);
if (unlikely(reg == R_ZERO)) {
/*
* With shadow register sets, register r0 does not necessarily contain 0,
* but it is overwhelmingly likely that it does -- software is supposed
* to have set r0 to 0 in every shadow register set before use.
*/
if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) {
return tcg_constant_tl(0);
}
return cpu_R[reg];
if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
return cpu_R[reg];
}
#ifdef CONFIG_USER_ONLY
g_assert_not_reached();
#else
return cpu_crs_R[reg];
#endif
}
static TCGv dest_gpr(DisasContext *dc, unsigned reg)
{
assert(reg < NUM_GP_REGS);
/*
* The spec for shadow register sets isn't clear, but we assume that
* writes to r0 are discarded regardless of CRS.
*/
if (unlikely(reg == R_ZERO)) {
if (dc->sink == NULL) {
dc->sink = tcg_temp_new();
}
return dc->sink;
}
return cpu_R[reg];
if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) {
return cpu_R[reg];
}
#ifdef CONFIG_USER_ONLY
g_assert_not_reached();
#else
return cpu_crs_R[reg];
#endif
}
static void t_gen_helper_raise_exception(DisasContext *dc,
@ -225,7 +254,7 @@ static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags)
static bool gen_check_supervisor(DisasContext *dc)
{
if (dc->base.tb->flags & CR_STATUS_U) {
if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) {
/* CPU in user mode, privileged instruction called, stop. */
t_gen_helper_raise_exception(dc, EXCP_SUPERI);
return false;
@ -335,7 +364,7 @@ static void do_i_math_logic(DisasContext *dc, uint32_t insn,
val = imm(&instr);
if (instr.a == R_ZERO) {
if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) {
/* This catches the canonical expansions of movi and movhi. */
tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0);
} else {
@ -865,6 +894,7 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->mem_idx = cpu_mmu_index(env, false);
dc->cr_state = cpu->cr_state;
dc->tb_flags = dc->base.tb->flags;
/* Bound the number of insns to execute to those left on the page. */
page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
@ -999,13 +1029,26 @@ void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags)
void nios2_tcg_init(void)
{
int i;
#ifndef CONFIG_USER_ONLY
TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env,
offsetof(CPUNios2State, regs), "crs");
for (i = 0; i < NUM_GP_REGS; i++) {
cpu_R[i] = tcg_global_mem_new(cpu_env,
offsetof(CPUNios2State, regs[i]),
for (int i = 0; i < NUM_GP_REGS; i++) {
cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]);
}
#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N])
#else
#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N])
#endif
for (int i = 0; i < NUM_GP_REGS; i++) {
cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof_regs0(i),
gr_regnames[i]);
}
#undef offsetof_regs0
cpu_pc = tcg_global_mem_new(cpu_env,
offsetof(CPUNios2State, pc), "pc");
}