diff mbox series

[v5,42/48] target/nios2: Implement rdprs, wrprs

Message ID 20220310112725.570053-43-richard.henderson@linaro.org
State Superseded
Headers show
Series target/nios2: Shadow register set, EIC and VIC | expand

Commit Message

Richard Henderson March 10, 2022, 11:27 a.m. UTC
Implement these out of line, so that tcg global temps
(aka the architectural registers) are synced back to
tcg storage as required.  This makes sure that we get
the proper results when status.PRS == status.CRS.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 target/nios2/cpu.h       |  1 +
 target/nios2/helper.h    |  2 ++
 target/nios2/op_helper.c | 12 ++++++++++
 target/nios2/translate.c | 47 ++++++++++++++++++++++++++++++++++++++--
 4 files changed, 60 insertions(+), 2 deletions(-)

Comments

Amir Gonnen March 15, 2022, 4:26 p.m. UTC | #1
Something is wrong when translating rdprs in an interrupt handler when CRS is 0x1.
I'm hitting "../tcg/tcg.c:3466: tcg_reg_alloc_mov: Assertion `ts->val_type == TEMP_VAL_REG' failed."

When stopped on that assertion I can see that :
- ts->val_type  = TEMP_VAL_DEAD
- op->opc = INDEX_op_mov_i32
- ots->name = "pc"
- cpu->ctrl[0] = 0x5f9 (that's STATUS so CRS = 1)
- pc = 0xa2d5c

so, it looks related to an assignment to PC a little after rdprs.

When running with -d in_asm,op_ind,op_opt:
----------------
IN: 
0x000a2d5c:  ldw	r16,4(et)
0x000a2d60:  rdprs	sp,sp,0
0x000a2d64:  ldw	r4,8(et)
0x000a2d68:  callr	r16

OP before indirect lowering:
 ld_i32 tmp0,env,$0xfffffffffffffff0
 brcond_i32 tmp0,$0x0,lt,$L0              dead: 0

 ---- 000a2d5c
 add_i32 tmp0,et,$0x4                     dead: 2
 qemu_ld_i32 r16,tmp0,leul,0              sync: 0  dead: 1

 ---- 000a2d60
 call rdprs,$0x2,$1,sp,env,$0x1b          sync: 0  dead: 0 2

 ---- 000a2d64
 add_i32 tmp0,et,$0x8                     dead: 1 2
 qemu_ld_i32 r4,tmp0,leul,0               sync: 0  dead: 0 1

 ---- 000a2d68
 and_i32 tmp0,r16,$0x3                    dead: 2
 brcond_i32 tmp0,$0x0,ne,$L1              dead: 0 1
 mov_i32 pc,r16                           sync: 0  dead: 0 1
 mov_i32 ra,$0xa2d6c                      sync: 0  dead: 0 1
 call lookup_tb_ptr,$0x6,$1,tmp7,env      dead: 1
 goto_ptr tmp7                            dead: 0
 set_label $L1
 st_i32 r16,env,$0x2038                   dead: 0
 mov_i32 pc,$0xa2d68                      sync: 0  dead: 0 1
 call raise_exception,$0xa,$0,env,$0x7    dead: 0 1
 set_label $L0
 exit_tb $0x7f1878027e43

OP after optimization and liveness analysis:
 ld_i32 tmp0,env,$0xfffffffffffffff0      pref=0xffff
 brcond_i32 tmp0,$0x0,lt,$L0              dead: 0

 ---- 000a2d5c                         
 ld_i32 tmp34,crs,$0x60                   pref=0xf038
 add_i32 tmp0,tmp34,$0x4                  dead: 2  pref=0xff3f
 qemu_ld_i32 tmp26,tmp0,leul,0            dead: 1  pref=0xf038
 st_i32 tmp26,crs,$0x40                 

 ---- 000a2d60                         
 call rdprs,$0x2,$1,tmp37,env,$0x1b       dead: 2  pref=none
 st_i32 tmp37,crs,$0x6c                   dead: 0

 ---- 000a2d64                         
 add_i32 tmp0,tmp34,$0x8                  dead: 1 2  pref=0xff3f
 qemu_ld_i32 tmp14,tmp0,leul,0            dead: 1  pref=0xffff
 st_i32 tmp14,crs,$0x10                   dead: 0

 ---- 000a2d68                         
 and_i32 tmp0,tmp26,$0x3                  dead: 1 2  pref=0xffff
 brcond_i32 tmp0,$0x0,ne,$L1              dead: 0 1
 mov_i32 pc,tmp26                         sync: 0  dead: 0 1  pref=0xffff
 st_i32 $0xa2d6c,crs,$0x7c                dead: 0 1
 call lookup_tb_ptr,$0x6,$1,tmp7,env      dead: 1  pref=none
 goto_ptr tmp7                            dead: 0
 set_label $L1                          
 ld_i32 tmp26,crs,$0x40                   dead: 1  pref=0xffff
 st_i32 tmp26,env,$0x2038                 dead: 0
 mov_i32 pc,$0xa2d68                      sync: 0  dead: 0 1  pref=0xffff
 call raise_exception,$0xa,$0,env,$0x7    dead: 0 1
 set_label $L0                          
 exit_tb $0x7f1878027e43
Richard Henderson March 15, 2022, 7:12 p.m. UTC | #2
On 3/15/22 09:26, Amir Gonnen wrote:
> Something is wrong when translating rdprs in an interrupt handler when CRS is 0x1.
> I'm hitting "../tcg/tcg.c:3466: tcg_reg_alloc_mov: Assertion `ts->val_type == TEMP_VAL_REG' failed."
> 
> When stopped on that assertion I can see that :
> - ts->val_type  = TEMP_VAL_DEAD
> - op->opc = INDEX_op_mov_i32
> - ots->name = "pc"
> - cpu->ctrl[0] = 0x5f9 (that's STATUS so CRS = 1)
> - pc = 0xa2d5c
> 
> so, it looks related to an assignment to PC a little after rdprs.

Ok, thanks for the report.  Yes, it's a bug in the indirection lowering.


r~
diff mbox series

Patch

diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h
index e32bebe9b7..26d4dcfe12 100644
--- a/target/nios2/cpu.h
+++ b/target/nios2/cpu.h
@@ -220,6 +220,7 @@  struct ArchCPU {
 
     bool diverr_present;
     bool mmu_present;
+    bool eic_present;
 
     uint32_t pid_num_bits;
     uint32_t tlb_num_ways;
diff --git a/target/nios2/helper.h b/target/nios2/helper.h
index 6f5ec60b0d..1648d76ade 100644
--- a/target/nios2/helper.h
+++ b/target/nios2/helper.h
@@ -24,6 +24,8 @@  DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32)
 
 #if !defined(CONFIG_USER_ONLY)
 DEF_HELPER_3(eret, noreturn, env, i32, i32)
+DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32)
+DEF_HELPER_3(wrprs, void, env, i32, i32)
 DEF_HELPER_2(mmu_write_tlbacc, void, env, i32)
 DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32)
 DEF_HELPER_2(mmu_write_pteaddr, void, env, i32)
diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c
index 849867becd..e5e70268da 100644
--- a/target/nios2/op_helper.c
+++ b/target/nios2/op_helper.c
@@ -83,4 +83,16 @@  void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
     env->pc = new_pc;
     cpu_loop_exit(cs);
 }
+
+uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno)
+{
+    unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+    return env->shadow_regs[prs][regno];
+}
+
+void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val)
+{
+    unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+    env->shadow_regs[prs][regno] = val;
+}
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/nios2/translate.c b/target/nios2/translate.c
index 525df7b023..2b2f528e00 100644
--- a/target/nios2/translate.c
+++ b/target/nios2/translate.c
@@ -103,6 +103,7 @@  typedef struct DisasContext {
     bool              crs0;
     TCGv              sink;
     const ControlRegState *cr_state;
+    bool              eic_present;
 } DisasContext;
 
 static TCGv cpu_R[NUM_GP_REGS];
@@ -326,6 +327,27 @@  gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16)
 gen_i_math_logic(orhi , ori,  1, instr.imm16.u << 16)
 gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16)
 
+/* rB <- prs.rA + sigma(IMM16) */
+static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    if (!dc->eic_present) {
+        t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+        return;
+    }
+    if (!gen_check_supervisor(dc)) {
+        return;
+    }
+
+#ifdef CONFIG_USER_ONLY
+    g_assert_not_reached();
+#else
+    I_TYPE(instr, code);
+    TCGv dest = dest_gpr(dc, instr.b);
+    gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a));
+    tcg_gen_addi_tl(dest, dest, instr.imm16.s);
+#endif
+}
+
 /* Prototype only, defined below */
 static void handle_r_type_instr(DisasContext *dc, uint32_t code,
                                 uint32_t flags);
@@ -387,7 +409,7 @@  static const Nios2Instruction i_type_instructions[] = {
     INSTRUCTION_FLG(gen_stx, MO_SL),                  /* stwio */
     INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU),           /* bltu */
     INSTRUCTION_FLG(gen_ldx, MO_UL),                  /* ldwio */
-    INSTRUCTION_UNIMPLEMENTED(),                      /* rdprs */
+    INSTRUCTION(rdprs),                               /* rdprs */
     INSTRUCTION_ILLEGAL(),
     INSTRUCTION_FLG(handle_r_type_instr, 0),          /* R-Type */
     INSTRUCTION_NOP(),                                /* flushd */
@@ -587,6 +609,26 @@  static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
 #endif
 }
 
+/* prs.rC <- rA */
+static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+    if (!dc->eic_present) {
+        t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+        return;
+    }
+    if (!gen_check_supervisor(dc)) {
+        return;
+    }
+
+#ifdef CONFIG_USER_ONLY
+    g_assert_not_reached();
+#else
+    R_TYPE(instr, code);
+    gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c),
+                     load_gpr(dc, instr.a));
+#endif
+}
+
 /* Comparison instructions */
 static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags)
 {
@@ -711,7 +753,7 @@  static const Nios2Instruction r_type_instructions[] = {
     INSTRUCTION_ILLEGAL(),
     INSTRUCTION(slli),                                /* slli */
     INSTRUCTION(sll),                                 /* sll */
-    INSTRUCTION_UNIMPLEMENTED(),                      /* wrprs */
+    INSTRUCTION(wrprs),                               /* wrprs */
     INSTRUCTION_ILLEGAL(),
     INSTRUCTION(or),                                  /* or */
     INSTRUCTION(mulxsu),                              /* mulxsu */
@@ -812,6 +854,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->eic_present = cpu->eic_present;
     dc->crs0 = FIELD_EX32(dc->base.tb->flags, TBFLAGS, CRS0);
 
     /* Bound the number of insns to execute to those left on the page.  */