#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,
#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;
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);
#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 */
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);
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,
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;
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 {
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;
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");
}