diff options
Diffstat (limited to 'target/microblaze/translate.c')
| -rw-r--r-- | target/microblaze/translate.c | 2901 |
1 files changed, 1464 insertions, 1437 deletions
diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index a96cb21d96..a377818b5e 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -24,7 +24,6 @@ #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" -#include "microblaze-decode.h" #include "exec/cpu_ldst.h" #include "exec/helper-gen.h" #include "exec/translator.h" @@ -33,106 +32,99 @@ #include "trace-tcg.h" #include "exec/log.h" - -#define SIM_COMPAT 0 -#define DISAS_GNU 1 -#define DISAS_MB 1 -#if DISAS_MB && !SIM_COMPAT -# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) -#else -# define LOG_DIS(...) do { } while (0) -#endif - -#define D(x) - #define EXTRACT_FIELD(src, start, end) \ (((src) >> start) & ((1 << (end - start + 1)) - 1)) /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ -static TCGv_i32 env_debug; static TCGv_i32 cpu_R[32]; -static TCGv_i64 cpu_SR[14]; -static TCGv_i32 env_imm; -static TCGv_i32 env_btaken; -static TCGv_i64 env_btarget; -static TCGv_i32 env_iflags; -static TCGv env_res_addr; -static TCGv_i32 env_res_val; +static TCGv_i32 cpu_pc; +static TCGv_i32 cpu_msr; +static TCGv_i32 cpu_msr_c; +static TCGv_i32 cpu_imm; +static TCGv_i32 cpu_bvalue; +static TCGv_i32 cpu_btarget; +static TCGv_i32 cpu_iflags; +static TCGv cpu_res_addr; +static TCGv_i32 cpu_res_val; #include "exec/gen-icount.h" /* This is the state at translation time. */ typedef struct DisasContext { + DisasContextBase base; MicroBlazeCPU *cpu; - uint32_t pc; - /* Decoder. */ - int type_b; - uint32_t ir; - uint8_t opcode; - uint8_t rd, ra, rb; - uint16_t imm; + /* TCG op of the current insn_start. */ + TCGOp *insn_start; + + TCGv_i32 r0; + bool r0_set; + /* Decoder. */ + uint32_t ext_imm; unsigned int cpustate_changed; - unsigned int delayed_branch; - unsigned int tb_flags, synced_flags; /* tb dependent flags. */ - unsigned int clear_imm; - int is_jmp; - -#define JMP_NOJMP 0 -#define JMP_DIRECT 1 -#define JMP_DIRECT_CC 2 -#define JMP_INDIRECT 3 - unsigned int jmp; - uint32_t jmp_pc; - - int abort_at_next_insn; - struct TranslationBlock *tb; - int singlestep_enabled; + unsigned int tb_flags; + unsigned int tb_flags_to_set; + int mem_index; + + /* Condition under which to jump, including NEVER and ALWAYS. */ + TCGCond jmp_cond; + + /* Immediate branch-taken destination, or -1 for indirect. */ + uint32_t jmp_dest; } DisasContext; -static const char *regnames[] = +static int typeb_imm(DisasContext *dc, int x) { - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", - "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", - "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", -}; + if (dc->tb_flags & IMM_FLAG) { + return deposit32(dc->ext_imm, 0, 16, x); + } + return x; +} -static const char *special_regnames[] = -{ - "rpc", "rmsr", "sr2", "rear", "sr4", "resr", "sr6", "rfsr", - "sr8", "sr9", "sr10", "rbtr", "sr12", "redr" -}; +/* Include the auto-generated decoder. */ +#include "decode-insns.c.inc" -static inline void t_sync_flags(DisasContext *dc) +static void t_sync_flags(DisasContext *dc) { /* Synch the tb dependent flags between translator and runtime. */ - if (dc->tb_flags != dc->synced_flags) { - tcg_gen_movi_i32(env_iflags, dc->tb_flags); - dc->synced_flags = dc->tb_flags; + if ((dc->tb_flags ^ dc->base.tb->flags) & ~MSR_TB_MASK) { + tcg_gen_movi_i32(cpu_iflags, dc->tb_flags & ~MSR_TB_MASK); } } -static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) +static void gen_raise_exception(DisasContext *dc, uint32_t index) { TCGv_i32 tmp = tcg_const_i32(index); - t_sync_flags(dc); - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; +} + +static void gen_raise_exception_sync(DisasContext *dc, uint32_t index) +{ + t_sync_flags(dc); + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); + gen_raise_exception(dc, index); +} + +static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec) +{ + TCGv_i32 tmp = tcg_const_i32(esr_ec); + tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUMBState, esr)); + tcg_temp_free_i32(tmp); + + gen_raise_exception_sync(dc, EXCP_HW_EXCP); } static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) { #ifndef CONFIG_USER_ONLY - return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (dc->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -140,42 +132,20 @@ static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { - if (use_goto_tb(dc, dest)) { + if (dc->base.singlestep_enabled) { + TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); + tcg_gen_movi_i32(cpu_pc, dest); + gen_helper_raise_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); + } else if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_i64(cpu_SR[SR_PC], dest); - tcg_gen_exit_tb(dc->tb, n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb(dc->base.tb, n); } else { - tcg_gen_movi_i64(cpu_SR[SR_PC], dest); + tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_exit_tb(NULL, 0); } -} - -static void read_carry(DisasContext *dc, TCGv_i32 d) -{ - tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); - tcg_gen_shri_i32(d, d, 31); -} - -/* - * write_carry sets the carry bits in MSR based on bit 0 of v. - * v[31:1] are ignored. - */ -static void write_carry(DisasContext *dc, TCGv_i32 v) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(t0, v); - /* Deposit bit 0 into MSR_C and the alias MSR_CC. */ - tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 2, 1); - tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 31, 1); - tcg_temp_free_i64(t0); -} - -static void write_carryi(DisasContext *dc, bool carry) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_movi_i32(t0, carry); - write_carry(dc, t0); - tcg_temp_free_i32(t0); + dc->base.is_jmp = DISAS_NORETURN; } /* @@ -184,10 +154,9 @@ static void write_carryi(DisasContext *dc, bool carry) */ static bool trap_illegal(DisasContext *dc, bool cond) { - if (cond && (dc->tb_flags & MSR_EE_FLAG) + if (cond && (dc->tb_flags & MSR_EE) && dc->cpu->cfg.illegal_opcode_exception) { - tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + gen_raise_hw_excp(dc, ESR_EC_ILLEGAL_OP); } return cond; } @@ -198,759 +167,717 @@ static bool trap_illegal(DisasContext *dc, bool cond) */ static bool trap_userspace(DisasContext *dc, bool cond) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - bool cond_user = cond && mem_index == MMU_USER_IDX; + bool cond_user = cond && dc->mem_index == MMU_USER_IDX; - if (cond_user && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (cond_user && (dc->tb_flags & MSR_EE)) { + gen_raise_hw_excp(dc, ESR_EC_PRIVINSN); } return cond_user; } -/* True if ALU operand b is a small immediate that may deserve - faster treatment. */ -static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) +static TCGv_i32 reg_for_read(DisasContext *dc, int reg) { - /* Immediate insn without the imm prefix ? */ - return dc->type_b && !(dc->tb_flags & IMM_FLAG); + if (likely(reg != 0)) { + return cpu_R[reg]; + } + if (!dc->r0_set) { + if (dc->r0 == NULL) { + dc->r0 = tcg_temp_new_i32(); + } + tcg_gen_movi_i32(dc->r0, 0); + dc->r0_set = true; + } + return dc->r0; } -static inline TCGv_i32 *dec_alu_op_b(DisasContext *dc) +static TCGv_i32 reg_for_write(DisasContext *dc, int reg) { - if (dc->type_b) { - if (dc->tb_flags & IMM_FLAG) - tcg_gen_ori_i32(env_imm, env_imm, dc->imm); - else - tcg_gen_movi_i32(env_imm, (int32_t)((int16_t)dc->imm)); - return &env_imm; - } else - return &cpu_R[dc->rb]; + if (likely(reg != 0)) { + return cpu_R[reg]; + } + if (dc->r0 == NULL) { + dc->r0 = tcg_temp_new_i32(); + } + return dc->r0; } -static void dec_add(DisasContext *dc) +static bool do_typea(DisasContext *dc, arg_typea *arg, bool side_effects, + void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32)) { - unsigned int k, c; - TCGv_i32 cf; + TCGv_i32 rd, ra, rb; - k = dc->opcode & 4; - c = dc->opcode & 2; - - LOG_DIS("add%s%s%s r%d r%d r%d\n", - dc->type_b ? "i" : "", k ? "k" : "", c ? "c" : "", - dc->rd, dc->ra, dc->rb); + if (arg->rd == 0 && !side_effects) { + return true; + } - /* Take care of the easy cases first. */ - if (k) { - /* k - keep carry, no need to update MSR. */ - /* If rd == r0, it's a nop. */ - if (dc->rd) { - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + rb = reg_for_read(dc, arg->rb); + fn(rd, ra, rb); + return true; +} - if (c) { - /* c - Add carry into the result. */ - cf = tcg_temp_new_i32(); +static bool do_typea0(DisasContext *dc, arg_typea0 *arg, bool side_effects, + void (*fn)(TCGv_i32, TCGv_i32)) +{ + TCGv_i32 rd, ra; - read_carry(dc, cf); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free_i32(cf); - } - } - return; + if (arg->rd == 0 && !side_effects) { + return true; } - /* From now on, we can assume k is zero. So we need to update MSR. */ - /* Extract carry. */ - cf = tcg_temp_new_i32(); - if (c) { - read_carry(dc, cf); - } else { - tcg_gen_movi_i32(cf, 0); + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + fn(rd, ra); + return true; +} + +static bool do_typeb_imm(DisasContext *dc, arg_typeb *arg, bool side_effects, + void (*fni)(TCGv_i32, TCGv_i32, int32_t)) +{ + TCGv_i32 rd, ra; + + if (arg->rd == 0 && !side_effects) { + return true; } - if (dc->rd) { - TCGv_i32 ncf = tcg_temp_new_i32(); - gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - write_carry(dc, ncf); - tcg_temp_free_i32(ncf); - } else { - gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); - write_carry(dc, cf); + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + fni(rd, ra, arg->imm); + return true; +} + +static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects, + void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 rd, ra, imm; + + if (arg->rd == 0 && !side_effects) { + return true; } - tcg_temp_free_i32(cf); + + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + imm = tcg_const_i32(arg->imm); + + fn(rd, ra, imm); + + tcg_temp_free_i32(imm); + return true; } -static void dec_sub(DisasContext *dc) +#define DO_TYPEA(NAME, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea *a) \ + { return do_typea(dc, a, SE, FN); } + +#define DO_TYPEA_CFG(NAME, CFG, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea *a) \ + { return dc->cpu->cfg.CFG && do_typea(dc, a, SE, FN); } + +#define DO_TYPEA0(NAME, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea0 *a) \ + { return do_typea0(dc, a, SE, FN); } + +#define DO_TYPEA0_CFG(NAME, CFG, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea0 *a) \ + { return dc->cpu->cfg.CFG && do_typea0(dc, a, SE, FN); } + +#define DO_TYPEBI(NAME, SE, FNI) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ + { return do_typeb_imm(dc, a, SE, FNI); } + +#define DO_TYPEBI_CFG(NAME, CFG, SE, FNI) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ + { return dc->cpu->cfg.CFG && do_typeb_imm(dc, a, SE, FNI); } + +#define DO_TYPEBV(NAME, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ + { return do_typeb_val(dc, a, SE, FN); } + +#define ENV_WRAPPER2(NAME, HELPER) \ + static void NAME(TCGv_i32 out, TCGv_i32 ina) \ + { HELPER(out, cpu_env, ina); } + +#define ENV_WRAPPER3(NAME, HELPER) \ + static void NAME(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) \ + { HELPER(out, cpu_env, ina, inb); } + +/* No input carry, but output carry. */ +static void gen_add(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - unsigned int u, cmp, k, c; - TCGv_i32 cf, na; + TCGv_i32 zero = tcg_const_i32(0); - u = dc->imm & 2; - k = dc->opcode & 4; - c = dc->opcode & 2; - cmp = (dc->imm & 1) && (!dc->type_b) && k; + tcg_gen_add2_i32(out, cpu_msr_c, ina, zero, inb, zero); - if (cmp) { - LOG_DIS("cmp%s r%d, r%d ir=%x\n", u ? "u" : "", dc->rd, dc->ra, dc->ir); - if (dc->rd) { - if (u) - gen_helper_cmpu(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); - else - gen_helper_cmp(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); - } - return; - } + tcg_temp_free_i32(zero); +} - LOG_DIS("sub%s%s r%d, r%d r%d\n", - k ? "k" : "", c ? "c" : "", dc->rd, dc->ra, dc->rb); +/* Input and output carry. */ +static void gen_addc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 tmp = tcg_temp_new_i32(); - /* Take care of the easy cases first. */ - if (k) { - /* k - keep carry, no need to update MSR. */ - /* If rd == r0, it's a nop. */ - if (dc->rd) { - tcg_gen_sub_i32(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + tcg_gen_add2_i32(tmp, cpu_msr_c, ina, zero, cpu_msr_c, zero); + tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); - if (c) { - /* c - Add carry into the result. */ - cf = tcg_temp_new_i32(); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(zero); +} - read_carry(dc, cf); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free_i32(cf); - } - } - return; - } +/* Input carry, but no output carry. */ +static void gen_addkc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_add_i32(out, ina, inb); + tcg_gen_add_i32(out, out, cpu_msr_c); +} - /* From now on, we can assume k is zero. So we need to update MSR. */ - /* Extract carry. And complement a into na. */ - cf = tcg_temp_new_i32(); - na = tcg_temp_new_i32(); - if (c) { - read_carry(dc, cf); - } else { - tcg_gen_movi_i32(cf, 1); - } +DO_TYPEA(add, true, gen_add) +DO_TYPEA(addc, true, gen_addc) +DO_TYPEA(addk, false, tcg_gen_add_i32) +DO_TYPEA(addkc, true, gen_addkc) - /* d = b + ~a + c. carry defaults to 1. */ - tcg_gen_not_i32(na, cpu_R[dc->ra]); +DO_TYPEBV(addi, true, gen_add) +DO_TYPEBV(addic, true, gen_addc) +DO_TYPEBI(addik, false, tcg_gen_addi_i32) +DO_TYPEBV(addikc, true, gen_addkc) - if (dc->rd) { - TCGv_i32 ncf = tcg_temp_new_i32(); - gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf); - tcg_gen_add_i32(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - write_carry(dc, ncf); - tcg_temp_free_i32(ncf); - } else { - gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf); - write_carry(dc, cf); - } - tcg_temp_free_i32(cf); - tcg_temp_free_i32(na); +static void gen_andni(TCGv_i32 out, TCGv_i32 ina, int32_t imm) +{ + tcg_gen_andi_i32(out, ina, ~imm); } -static void dec_pattern(DisasContext *dc) +DO_TYPEA(and, false, tcg_gen_and_i32) +DO_TYPEBI(andi, false, tcg_gen_andi_i32) +DO_TYPEA(andn, false, tcg_gen_andc_i32) +DO_TYPEBI(andni, false, gen_andni) + +static void gen_bsra(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - unsigned int mode; + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, inb, 31); + tcg_gen_sar_i32(out, ina, tmp); + tcg_temp_free_i32(tmp); +} - if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { - return; - } +static void gen_bsrl(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, inb, 31); + tcg_gen_shr_i32(out, ina, tmp); + tcg_temp_free_i32(tmp); +} - mode = dc->opcode & 3; - switch (mode) { - case 0: - /* pcmpbf. */ - LOG_DIS("pcmpbf r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - if (dc->rd) - gen_helper_pcmpbf(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 2: - LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - if (dc->rd) { - tcg_gen_setcond_i32(TCG_COND_EQ, cpu_R[dc->rd], - cpu_R[dc->ra], cpu_R[dc->rb]); - } - break; - case 3: - LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - if (dc->rd) { - tcg_gen_setcond_i32(TCG_COND_NE, cpu_R[dc->rd], - cpu_R[dc->ra], cpu_R[dc->rb]); - } - break; - default: - cpu_abort(CPU(dc->cpu), - "unsupported pattern insn opcode=%x\n", dc->opcode); - break; - } +static void gen_bsll(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, inb, 31); + tcg_gen_shl_i32(out, ina, tmp); + tcg_temp_free_i32(tmp); } -static void dec_and(DisasContext *dc) +static void gen_bsefi(TCGv_i32 out, TCGv_i32 ina, int32_t imm) { - unsigned int not; + /* Note that decodetree has extracted and reassembled imm_w/imm_s. */ + int imm_w = extract32(imm, 5, 5); + int imm_s = extract32(imm, 0, 5); + + if (imm_w + imm_s > 32 || imm_w == 0) { + /* These inputs have an undefined behavior. */ + qemu_log_mask(LOG_GUEST_ERROR, "bsefi: Bad input w=%d s=%d\n", + imm_w, imm_s); + } else { + tcg_gen_extract_i32(out, ina, imm_s, imm_w); + } +} - if (!dc->type_b && (dc->imm & (1 << 10))) { - dec_pattern(dc); - return; +static void gen_bsifi(TCGv_i32 out, TCGv_i32 ina, int32_t imm) +{ + /* Note that decodetree has extracted and reassembled imm_w/imm_s. */ + int imm_w = extract32(imm, 5, 5); + int imm_s = extract32(imm, 0, 5); + int width = imm_w - imm_s + 1; + + if (imm_w < imm_s) { + /* These inputs have an undefined behavior. */ + qemu_log_mask(LOG_GUEST_ERROR, "bsifi: Bad input w=%d s=%d\n", + imm_w, imm_s); + } else { + tcg_gen_deposit_i32(out, out, ina, imm_s, width); } +} - not = dc->opcode & (1 << 1); - LOG_DIS("and%s\n", not ? "n" : ""); +DO_TYPEA_CFG(bsra, use_barrel, false, gen_bsra) +DO_TYPEA_CFG(bsrl, use_barrel, false, gen_bsrl) +DO_TYPEA_CFG(bsll, use_barrel, false, gen_bsll) - if (!dc->rd) - return; +DO_TYPEBI_CFG(bsrai, use_barrel, false, tcg_gen_sari_i32) +DO_TYPEBI_CFG(bsrli, use_barrel, false, tcg_gen_shri_i32) +DO_TYPEBI_CFG(bslli, use_barrel, false, tcg_gen_shli_i32) + +DO_TYPEBI_CFG(bsefi, use_barrel, false, gen_bsefi) +DO_TYPEBI_CFG(bsifi, use_barrel, false, gen_bsifi) - if (not) { - tcg_gen_andc_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - } else - tcg_gen_and_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +static void gen_clz(TCGv_i32 out, TCGv_i32 ina) +{ + tcg_gen_clzi_i32(out, ina, 32); } -static void dec_or(DisasContext *dc) +DO_TYPEA0_CFG(clz, use_pcmp_instr, false, gen_clz) + +static void gen_cmp(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - if (!dc->type_b && (dc->imm & (1 << 10))) { - dec_pattern(dc); - return; - } + TCGv_i32 lt = tcg_temp_new_i32(); - LOG_DIS("or r%d r%d r%d imm=%x\n", dc->rd, dc->ra, dc->rb, dc->imm); - if (dc->rd) - tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_setcond_i32(TCG_COND_LT, lt, inb, ina); + tcg_gen_sub_i32(out, inb, ina); + tcg_gen_deposit_i32(out, out, lt, 31, 1); + tcg_temp_free_i32(lt); } -static void dec_xor(DisasContext *dc) +static void gen_cmpu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - if (!dc->type_b && (dc->imm & (1 << 10))) { - dec_pattern(dc); - return; - } + TCGv_i32 lt = tcg_temp_new_i32(); - LOG_DIS("xor r%d\n", dc->rd); - if (dc->rd) - tcg_gen_xor_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_setcond_i32(TCG_COND_LTU, lt, inb, ina); + tcg_gen_sub_i32(out, inb, ina); + tcg_gen_deposit_i32(out, out, lt, 31, 1); + tcg_temp_free_i32(lt); } -static inline void msr_read(DisasContext *dc, TCGv_i32 d) +DO_TYPEA(cmp, false, gen_cmp) +DO_TYPEA(cmpu, false, gen_cmpu) + +ENV_WRAPPER3(gen_fadd, gen_helper_fadd) +ENV_WRAPPER3(gen_frsub, gen_helper_frsub) +ENV_WRAPPER3(gen_fmul, gen_helper_fmul) +ENV_WRAPPER3(gen_fdiv, gen_helper_fdiv) +ENV_WRAPPER3(gen_fcmp_un, gen_helper_fcmp_un) +ENV_WRAPPER3(gen_fcmp_lt, gen_helper_fcmp_lt) +ENV_WRAPPER3(gen_fcmp_eq, gen_helper_fcmp_eq) +ENV_WRAPPER3(gen_fcmp_le, gen_helper_fcmp_le) +ENV_WRAPPER3(gen_fcmp_gt, gen_helper_fcmp_gt) +ENV_WRAPPER3(gen_fcmp_ne, gen_helper_fcmp_ne) +ENV_WRAPPER3(gen_fcmp_ge, gen_helper_fcmp_ge) + +DO_TYPEA_CFG(fadd, use_fpu, true, gen_fadd) +DO_TYPEA_CFG(frsub, use_fpu, true, gen_frsub) +DO_TYPEA_CFG(fmul, use_fpu, true, gen_fmul) +DO_TYPEA_CFG(fdiv, use_fpu, true, gen_fdiv) +DO_TYPEA_CFG(fcmp_un, use_fpu, true, gen_fcmp_un) +DO_TYPEA_CFG(fcmp_lt, use_fpu, true, gen_fcmp_lt) +DO_TYPEA_CFG(fcmp_eq, use_fpu, true, gen_fcmp_eq) +DO_TYPEA_CFG(fcmp_le, use_fpu, true, gen_fcmp_le) +DO_TYPEA_CFG(fcmp_gt, use_fpu, true, gen_fcmp_gt) +DO_TYPEA_CFG(fcmp_ne, use_fpu, true, gen_fcmp_ne) +DO_TYPEA_CFG(fcmp_ge, use_fpu, true, gen_fcmp_ge) + +ENV_WRAPPER2(gen_flt, gen_helper_flt) +ENV_WRAPPER2(gen_fint, gen_helper_fint) +ENV_WRAPPER2(gen_fsqrt, gen_helper_fsqrt) + +DO_TYPEA0_CFG(flt, use_fpu >= 2, true, gen_flt) +DO_TYPEA0_CFG(fint, use_fpu >= 2, true, gen_fint) +DO_TYPEA0_CFG(fsqrt, use_fpu >= 2, true, gen_fsqrt) + +/* Does not use ENV_WRAPPER3, because arguments are swapped as well. */ +static void gen_idiv(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); + gen_helper_divs(out, cpu_env, inb, ina); } -static inline void msr_write(DisasContext *dc, TCGv_i32 v) +static void gen_idivu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i64 t; + gen_helper_divu(out, cpu_env, inb, ina); +} - t = tcg_temp_new_i64(); - dc->cpustate_changed = 1; - /* PVR bit is not writable. */ - tcg_gen_extu_i32_i64(t, v); - tcg_gen_andi_i64(t, t, ~MSR_PVR); - tcg_gen_andi_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); - tcg_gen_or_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); - tcg_temp_free_i64(t); -} - -static void dec_msr(DisasContext *dc) -{ - CPUState *cs = CPU(dc->cpu); - TCGv_i32 t0, t1; - unsigned int sr, rn; - bool to, clrset, extended = false; - - sr = extract32(dc->imm, 0, 14); - to = extract32(dc->imm, 14, 1); - clrset = extract32(dc->imm, 15, 1) == 0; - dc->type_b = 1; - if (to) { - dc->cpustate_changed = 1; - } +DO_TYPEA_CFG(idiv, use_div, true, gen_idiv) +DO_TYPEA_CFG(idivu, use_div, true, gen_idivu) - /* Extended MSRs are only available if addr_size > 32. */ - if (dc->cpu->cfg.addr_size > 32) { - /* The E-bit is encoded differently for To/From MSR. */ - static const unsigned int e_bit[] = { 19, 24 }; +static bool trans_imm(DisasContext *dc, arg_imm *arg) +{ + dc->ext_imm = arg->imm << 16; + tcg_gen_movi_i32(cpu_imm, dc->ext_imm); + dc->tb_flags_to_set = IMM_FLAG; + return true; +} - extended = extract32(dc->imm, e_bit[to], 1); - } +static void gen_mulh(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_muls2_i32(tmp, out, ina, inb); + tcg_temp_free_i32(tmp); +} - /* msrclr and msrset. */ - if (clrset) { - bool clr = extract32(dc->ir, 16, 1); +static void gen_mulhu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_mulu2_i32(tmp, out, ina, inb); + tcg_temp_free_i32(tmp); +} - LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set", - dc->rd, dc->imm); +static void gen_mulhsu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_mulsu2_i32(tmp, out, ina, inb); + tcg_temp_free_i32(tmp); +} - if (!dc->cpu->cfg.use_msr_instr) { - /* nop??? */ - return; - } +DO_TYPEA_CFG(mul, use_hw_mul, false, tcg_gen_mul_i32) +DO_TYPEA_CFG(mulh, use_hw_mul >= 2, false, gen_mulh) +DO_TYPEA_CFG(mulhu, use_hw_mul >= 2, false, gen_mulhu) +DO_TYPEA_CFG(mulhsu, use_hw_mul >= 2, false, gen_mulhsu) +DO_TYPEBI_CFG(muli, use_hw_mul, false, tcg_gen_muli_i32) - if (trap_userspace(dc, dc->imm != 4 && dc->imm != 0)) { - return; - } +DO_TYPEA(or, false, tcg_gen_or_i32) +DO_TYPEBI(ori, false, tcg_gen_ori_i32) - if (dc->rd) - msr_read(dc, cpu_R[dc->rd]); - - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - msr_read(dc, t0); - tcg_gen_mov_i32(t1, *(dec_alu_op_b(dc))); - - if (clr) { - tcg_gen_not_i32(t1, t1); - tcg_gen_and_i32(t0, t0, t1); - } else - tcg_gen_or_i32(t0, t0, t1); - msr_write(dc, t0); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); - dc->is_jmp = DISAS_UPDATE; - return; - } +static void gen_pcmpeq(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_setcond_i32(TCG_COND_EQ, out, ina, inb); +} - if (trap_userspace(dc, to)) { - return; - } +static void gen_pcmpne(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_setcond_i32(TCG_COND_NE, out, ina, inb); +} -#if !defined(CONFIG_USER_ONLY) - /* Catch read/writes to the mmu block. */ - if ((sr & ~0xff) == 0x1000) { - TCGv_i32 tmp_ext = tcg_const_i32(extended); - TCGv_i32 tmp_sr; +DO_TYPEA_CFG(pcmpbf, use_pcmp_instr, false, gen_helper_pcmpbf) +DO_TYPEA_CFG(pcmpeq, use_pcmp_instr, false, gen_pcmpeq) +DO_TYPEA_CFG(pcmpne, use_pcmp_instr, false, gen_pcmpne) - sr &= 7; - tmp_sr = tcg_const_i32(sr); - LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); - if (to) { - gen_helper_mmu_write(cpu_env, tmp_ext, tmp_sr, cpu_R[dc->ra]); - } else { - gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tmp_ext, tmp_sr); - } - tcg_temp_free_i32(tmp_sr); - tcg_temp_free_i32(tmp_ext); - return; - } -#endif +/* No input carry, but output carry. */ +static void gen_rsub(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_msr_c, inb, ina); + tcg_gen_sub_i32(out, inb, ina); +} - if (to) { - LOG_DIS("m%ss sr%x r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); - switch (sr) { - case 0: - break; - case 1: - msr_write(dc, cpu_R[dc->ra]); - break; - case SR_EAR: - case SR_ESR: - case SR_FSR: - tcg_gen_extu_i32_i64(cpu_SR[sr], cpu_R[dc->ra]); - break; - case 0x800: - tcg_gen_st_i32(cpu_R[dc->ra], - cpu_env, offsetof(CPUMBState, slr)); - break; - case 0x802: - tcg_gen_st_i32(cpu_R[dc->ra], - cpu_env, offsetof(CPUMBState, shr)); - break; - default: - cpu_abort(CPU(dc->cpu), "unknown mts reg %x\n", sr); - break; - } - } else { - LOG_DIS("m%ss r%d sr%x imm=%x\n", to ? "t" : "f", dc->rd, sr, dc->imm); - - switch (sr) { - case 0: - tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); - break; - case 1: - msr_read(dc, cpu_R[dc->rd]); - break; - case SR_EAR: - if (extended) { - tcg_gen_extrh_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); - break; - } - case SR_ESR: - case SR_FSR: - case SR_BTR: - case SR_EDR: - tcg_gen_extrl_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); - break; - case 0x800: - tcg_gen_ld_i32(cpu_R[dc->rd], - cpu_env, offsetof(CPUMBState, slr)); - break; - case 0x802: - tcg_gen_ld_i32(cpu_R[dc->rd], - cpu_env, offsetof(CPUMBState, shr)); - break; - case 0x2000 ... 0x200c: - rn = sr & 0xf; - tcg_gen_ld_i32(cpu_R[dc->rd], - cpu_env, offsetof(CPUMBState, pvr.regs[rn])); - break; - default: - cpu_abort(cs, "unknown mfs reg %x\n", sr); - break; - } - } +/* Input and output carry. */ +static void gen_rsubc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 tmp = tcg_temp_new_i32(); - if (dc->rd == 0) { - tcg_gen_movi_i32(cpu_R[0], 0); - } + tcg_gen_not_i32(tmp, ina); + tcg_gen_add2_i32(tmp, cpu_msr_c, tmp, zero, cpu_msr_c, zero); + tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); + + tcg_temp_free_i32(zero); + tcg_temp_free_i32(tmp); } -/* Multiplier unit. */ -static void dec_mul(DisasContext *dc) +/* No input or output carry. */ +static void gen_rsubk(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i32 tmp; - unsigned int subcode; + tcg_gen_sub_i32(out, inb, ina); +} - if (trap_illegal(dc, !dc->cpu->cfg.use_hw_mul)) { - return; - } +/* Input carry, no output carry. */ +static void gen_rsubkc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 nota = tcg_temp_new_i32(); - subcode = dc->imm & 3; + tcg_gen_not_i32(nota, ina); + tcg_gen_add_i32(out, inb, nota); + tcg_gen_add_i32(out, out, cpu_msr_c); - if (dc->type_b) { - LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm); - tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - return; - } + tcg_temp_free_i32(nota); +} - /* mulh, mulhsu and mulhu are not available if C_USE_HW_MUL is < 2. */ - if (subcode >= 1 && subcode <= 3 && dc->cpu->cfg.use_hw_mul < 2) { - /* nop??? */ - } +DO_TYPEA(rsub, true, gen_rsub) +DO_TYPEA(rsubc, true, gen_rsubc) +DO_TYPEA(rsubk, false, gen_rsubk) +DO_TYPEA(rsubkc, true, gen_rsubkc) + +DO_TYPEBV(rsubi, true, gen_rsub) +DO_TYPEBV(rsubic, true, gen_rsubc) +DO_TYPEBV(rsubik, false, gen_rsubk) +DO_TYPEBV(rsubikc, true, gen_rsubkc) + +DO_TYPEA0(sext8, false, tcg_gen_ext8s_i32) +DO_TYPEA0(sext16, false, tcg_gen_ext16s_i32) + +static void gen_sra(TCGv_i32 out, TCGv_i32 ina) +{ + tcg_gen_andi_i32(cpu_msr_c, ina, 1); + tcg_gen_sari_i32(out, ina, 1); +} + +static void gen_src(TCGv_i32 out, TCGv_i32 ina) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_mov_i32(tmp, cpu_msr_c); + tcg_gen_andi_i32(cpu_msr_c, ina, 1); + tcg_gen_extract2_i32(out, ina, tmp, 1); - tmp = tcg_temp_new_i32(); - switch (subcode) { - case 0: - LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 1: - LOG_DIS("mulh r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_muls2_i32(tmp, cpu_R[dc->rd], - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 2: - LOG_DIS("mulhsu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulsu2_i32(tmp, cpu_R[dc->rd], - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 3: - LOG_DIS("mulhu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulu2_i32(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); - break; - default: - cpu_abort(CPU(dc->cpu), "unknown MUL insn %x\n", subcode); - break; - } tcg_temp_free_i32(tmp); } -/* Div unit. */ -static void dec_div(DisasContext *dc) +static void gen_srl(TCGv_i32 out, TCGv_i32 ina) { - unsigned int u; + tcg_gen_andi_i32(cpu_msr_c, ina, 1); + tcg_gen_shri_i32(out, ina, 1); +} - u = dc->imm & 2; - LOG_DIS("div\n"); +DO_TYPEA0(sra, false, gen_sra) +DO_TYPEA0(src, false, gen_src) +DO_TYPEA0(srl, false, gen_srl) - if (trap_illegal(dc, !dc->cpu->cfg.use_div)) { - return; - } +static void gen_swaph(TCGv_i32 out, TCGv_i32 ina) +{ + tcg_gen_rotri_i32(out, ina, 16); +} + +DO_TYPEA0(swapb, false, tcg_gen_bswap32_i32) +DO_TYPEA0(swaph, false, gen_swaph) - if (u) - gen_helper_divu(cpu_R[dc->rd], cpu_env, *(dec_alu_op_b(dc)), - cpu_R[dc->ra]); - else - gen_helper_divs(cpu_R[dc->rd], cpu_env, *(dec_alu_op_b(dc)), - cpu_R[dc->ra]); - if (!dc->rd) - tcg_gen_movi_i32(cpu_R[dc->rd], 0); +static bool trans_wdic(DisasContext *dc, arg_wdic *a) +{ + /* Cache operations are nops: only check for supervisor mode. */ + trap_userspace(dc, true); + return true; } -static void dec_barrel(DisasContext *dc) +DO_TYPEA(xor, false, tcg_gen_xor_i32) +DO_TYPEBI(xori, false, tcg_gen_xori_i32) + +static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) { - TCGv_i32 t0; - unsigned int imm_w, imm_s; - bool s, t, e = false, i = false; + TCGv ret = tcg_temp_new(); - if (trap_illegal(dc, !dc->cpu->cfg.use_barrel)) { - return; + /* If any of the regs is r0, set t to the value of the other reg. */ + if (ra && rb) { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_add_i32(tmp, cpu_R[ra], cpu_R[rb]); + tcg_gen_extu_i32_tl(ret, tmp); + tcg_temp_free_i32(tmp); + } else if (ra) { + tcg_gen_extu_i32_tl(ret, cpu_R[ra]); + } else if (rb) { + tcg_gen_extu_i32_tl(ret, cpu_R[rb]); + } else { + tcg_gen_movi_tl(ret, 0); } - if (dc->type_b) { - /* Insert and extract are only available in immediate mode. */ - i = extract32(dc->imm, 15, 1); - e = extract32(dc->imm, 14, 1); + if ((ra == 1 || rb == 1) && dc->cpu->cfg.stackprot) { + gen_helper_stackprot(cpu_env, ret); } - s = extract32(dc->imm, 10, 1); - t = extract32(dc->imm, 9, 1); - imm_w = extract32(dc->imm, 6, 5); - imm_s = extract32(dc->imm, 0, 5); + return ret; +} - LOG_DIS("bs%s%s%s r%d r%d r%d\n", - e ? "e" : "", - s ? "l" : "r", t ? "a" : "l", dc->rd, dc->ra, dc->rb); +static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) +{ + TCGv ret = tcg_temp_new(); - if (e) { - if (imm_w + imm_s > 32 || imm_w == 0) { - /* These inputs have an undefined behavior. */ - qemu_log_mask(LOG_GUEST_ERROR, "bsefi: Bad input w=%d s=%d\n", - imm_w, imm_s); - } else { - tcg_gen_extract_i32(cpu_R[dc->rd], cpu_R[dc->ra], imm_s, imm_w); - } - } else if (i) { - int width = imm_w - imm_s + 1; + /* If any of the regs is r0, set t to the value of the other reg. */ + if (ra) { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_addi_i32(tmp, cpu_R[ra], imm); + tcg_gen_extu_i32_tl(ret, tmp); + tcg_temp_free_i32(tmp); + } else { + tcg_gen_movi_tl(ret, (uint32_t)imm); + } - if (imm_w < imm_s) { - /* These inputs have an undefined behavior. */ - qemu_log_mask(LOG_GUEST_ERROR, "bsifi: Bad input w=%d s=%d\n", - imm_w, imm_s); + if (ra == 1 && dc->cpu->cfg.stackprot) { + gen_helper_stackprot(cpu_env, ret); + } + return ret; +} + +#ifndef CONFIG_USER_ONLY +static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) +{ + int addr_size = dc->cpu->cfg.addr_size; + TCGv ret = tcg_temp_new(); + + if (addr_size == 32 || ra == 0) { + if (rb) { + tcg_gen_extu_i32_tl(ret, cpu_R[rb]); } else { - tcg_gen_deposit_i32(cpu_R[dc->rd], cpu_R[dc->rd], cpu_R[dc->ra], - imm_s, width); + tcg_gen_movi_tl(ret, 0); } } else { - t0 = tcg_temp_new_i32(); - - tcg_gen_mov_i32(t0, *(dec_alu_op_b(dc))); - tcg_gen_andi_i32(t0, t0, 31); - - if (s) { - tcg_gen_shl_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); + if (rb) { + tcg_gen_concat_i32_i64(ret, cpu_R[rb], cpu_R[ra]); } else { - if (t) { - tcg_gen_sar_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); - } else { - tcg_gen_shr_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); - } + tcg_gen_extu_i32_tl(ret, cpu_R[ra]); + tcg_gen_shli_tl(ret, ret, 32); + } + if (addr_size < 64) { + /* Mask off out of range bits. */ + tcg_gen_andi_i64(ret, ret, MAKE_64BIT_MASK(0, addr_size)); } - tcg_temp_free_i32(t0); } + return ret; } +#endif -static void dec_bit(DisasContext *dc) +static void record_unaligned_ess(DisasContext *dc, int rd, + MemOp size, bool store) { - CPUState *cs = CPU(dc->cpu); - TCGv_i32 t0; - unsigned int op; + uint32_t iflags = tcg_get_insn_start_param(dc->insn_start, 1); - op = dc->ir & ((1 << 9) - 1); - switch (op) { - case 0x21: - /* src. */ - t0 = tcg_temp_new_i32(); + iflags |= ESR_ESS_FLAG; + iflags |= rd << 5; + iflags |= store * ESR_S; + iflags |= (size == MO_32) * ESR_W; - LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); - tcg_gen_extrl_i64_i32(t0, cpu_SR[SR_MSR]); - tcg_gen_andi_i32(t0, t0, MSR_CC); - write_carry(dc, cpu_R[dc->ra]); - if (dc->rd) { - tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); - tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->rd], t0); - } - tcg_temp_free_i32(t0); - break; - - case 0x1: - case 0x41: - /* srl. */ - LOG_DIS("srl r%d r%d\n", dc->rd, dc->ra); - - /* Update carry. Note that write carry only looks at the LSB. */ - write_carry(dc, cpu_R[dc->ra]); - if (dc->rd) { - if (op == 0x41) - tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); - else - tcg_gen_sari_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); - } - break; - case 0x60: - LOG_DIS("ext8s r%d r%d\n", dc->rd, dc->ra); - tcg_gen_ext8s_i32(cpu_R[dc->rd], cpu_R[dc->ra]); - break; - case 0x61: - LOG_DIS("ext16s r%d r%d\n", dc->rd, dc->ra); - tcg_gen_ext16s_i32(cpu_R[dc->rd], cpu_R[dc->ra]); - break; - case 0x64: - case 0x66: - case 0x74: - case 0x76: - /* wdc. */ - LOG_DIS("wdc r%d\n", dc->ra); - trap_userspace(dc, true); - break; - case 0x68: - /* wic. */ - LOG_DIS("wic r%d\n", dc->ra); - trap_userspace(dc, true); - break; - case 0xe0: - if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { - return; - } - if (dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_clzi_i32(cpu_R[dc->rd], cpu_R[dc->ra], 32); - } - break; - case 0x1e0: - /* swapb */ - LOG_DIS("swapb r%d r%d\n", dc->rd, dc->ra); - tcg_gen_bswap32_i32(cpu_R[dc->rd], cpu_R[dc->ra]); - break; - case 0x1e2: - /*swaph */ - LOG_DIS("swaph r%d r%d\n", dc->rd, dc->ra); - tcg_gen_rotri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 16); - break; - default: - cpu_abort(cs, "unknown bit oc=%x op=%x rd=%d ra=%d rb=%d\n", - dc->pc, op, dc->rd, dc->ra, dc->rb); - break; - } + tcg_set_insn_start_param(dc->insn_start, 1, iflags); } -static inline void sync_jmpstate(DisasContext *dc) +static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, + int mem_index, bool rev) { - if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { - if (dc->jmp == JMP_DIRECT) { - tcg_gen_movi_i32(env_btaken, 1); + MemOp size = mop & MO_SIZE; + + /* + * When doing reverse accesses we need to do two things. + * + * 1. Reverse the address wrt endianness. + * 2. Byteswap the data lanes on the way back into the CPU core. + */ + if (rev) { + if (size > MO_8) { + mop ^= MO_BSWAP; + } + if (size < MO_32) { + tcg_gen_xori_tl(addr, addr, 3 - size); } - dc->jmp = JMP_INDIRECT; - tcg_gen_movi_i64(env_btarget, dc->jmp_pc); } + + if (size > MO_8 && + (dc->tb_flags & MSR_EE) && + dc->cpu->cfg.unaligned_exceptions) { + record_unaligned_ess(dc, rd, size, false); + mop |= MO_ALIGN; + } + + tcg_gen_qemu_ld_i32(reg_for_write(dc, rd), addr, mem_index, mop); + + tcg_temp_free(addr); + return true; } -static void dec_imm(DisasContext *dc) +static bool trans_lbu(DisasContext *dc, arg_typea *arg) { - LOG_DIS("imm %x\n", dc->imm << 16); - tcg_gen_movi_i32(env_imm, (dc->imm << 16)); - dc->tb_flags |= IMM_FLAG; - dc->clear_imm = 0; + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, false); } -static inline void compute_ldst_addr(DisasContext *dc, bool ea, TCGv t) +static bool trans_lbur(DisasContext *dc, arg_typea *arg) { - bool extimm = dc->tb_flags & IMM_FLAG; - /* Should be set to true if r1 is used by loadstores. */ - bool stackprot = false; - TCGv_i32 t32; + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, true); +} - /* All load/stores use ra. */ - if (dc->ra == 1 && dc->cpu->cfg.stackprot) { - stackprot = true; +static bool trans_lbuea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); +#endif +} - /* Treat the common cases first. */ - if (!dc->type_b) { - if (ea) { - int addr_size = dc->cpu->cfg.addr_size; +static bool trans_lbui(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, false); +} - if (addr_size == 32) { - tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); - return; - } +static bool trans_lhu(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); +} - tcg_gen_concat_i32_i64(t, cpu_R[dc->rb], cpu_R[dc->ra]); - if (addr_size < 64) { - /* Mask off out of range bits. */ - tcg_gen_andi_i64(t, t, MAKE_64BIT_MASK(0, addr_size)); - } - return; - } +static bool trans_lhur(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); +} - /* If any of the regs is r0, set t to the value of the other reg. */ - if (dc->ra == 0) { - tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); - return; - } else if (dc->rb == 0) { - tcg_gen_extu_i32_tl(t, cpu_R[dc->ra]); - return; - } +static bool trans_lhuea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; + } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); +#endif +} - if (dc->rb == 1 && dc->cpu->cfg.stackprot) { - stackprot = true; - } +static bool trans_lhui(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); +} - t32 = tcg_temp_new_i32(); - tcg_gen_add_i32(t32, cpu_R[dc->ra], cpu_R[dc->rb]); - tcg_gen_extu_i32_tl(t, t32); - tcg_temp_free_i32(t32); +static bool trans_lw(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - if (stackprot) { - gen_helper_stackprot(cpu_env, t); - } - return; - } - /* Immediate. */ - t32 = tcg_temp_new_i32(); - if (!extimm) { - tcg_gen_addi_i32(t32, cpu_R[dc->ra], (int16_t)dc->imm); - } else { - tcg_gen_add_i32(t32, cpu_R[dc->ra], *(dec_alu_op_b(dc))); - } - tcg_gen_extu_i32_tl(t, t32); - tcg_temp_free_i32(t32); +static bool trans_lwr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); +} - if (stackprot) { - gen_helper_stackprot(cpu_env, t); +static bool trans_lwea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } - return; +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); +#endif } -static void dec_load(DisasContext *dc) +static bool trans_lwi(DisasContext *dc, arg_typeb *arg) { - TCGv_i32 v; - TCGv addr; - unsigned int size; - bool rev = false, ex = false, ea = false; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - MemOp mop; + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - mop = dc->opcode & 3; - size = 1 << mop; - if (!dc->type_b) { - ea = extract32(dc->ir, 7, 1); - rev = extract32(dc->ir, 9, 1); - ex = extract32(dc->ir, 10, 1); - } - mop |= MO_TE; - if (rev) { - mop ^= MO_BSWAP; - } +static bool trans_lwx(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - if (trap_illegal(dc, size > 4)) { - return; - } + /* lwx does not throw unaligned access errors, so force alignment */ + tcg_gen_andi_tl(addr, addr, ~3); - if (trap_userspace(dc, ea)) { - return; + tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, MO_TEUL); + tcg_gen_mov_tl(cpu_res_addr, addr); + tcg_temp_free(addr); + + if (arg->rd) { + tcg_gen_mov_i32(cpu_R[arg->rd], cpu_res_val); } - LOG_DIS("l%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : "", - ea ? "ea" : ""); + /* No support for AXI exclusive so always clear C */ + tcg_gen_movi_i32(cpu_msr_c, 0); + return true; +} - t_sync_flags(dc); - addr = tcg_temp_new(); - compute_ldst_addr(dc, ea, addr); - /* Extended addressing bypasses the MMU. */ - mem_index = ea ? MMU_NOMMU_IDX : mem_index; +static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, + int mem_index, bool rev) +{ + MemOp size = mop & MO_SIZE; /* * When doing reverse accesses we need to do two things. @@ -958,925 +885,1025 @@ static void dec_load(DisasContext *dc) * 1. Reverse the address wrt endianness. * 2. Byteswap the data lanes on the way back into the CPU core. */ - if (rev && size != 4) { - /* Endian reverse the address. t is addr. */ - switch (size) { - case 1: - { - tcg_gen_xori_tl(addr, addr, 3); - break; - } - - case 2: - /* 00 -> 10 - 10 -> 00. */ - tcg_gen_xori_tl(addr, addr, 2); - break; - default: - cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); - break; + if (rev) { + if (size > MO_8) { + mop ^= MO_BSWAP; + } + if (size < MO_32) { + tcg_gen_xori_tl(addr, addr, 3 - size); } } - /* lwx does not throw unaligned access errors, so force alignment */ - if (ex) { - tcg_gen_andi_tl(addr, addr, ~3); + if (size > MO_8 && + (dc->tb_flags & MSR_EE) && + dc->cpu->cfg.unaligned_exceptions) { + record_unaligned_ess(dc, rd, size, true); + mop |= MO_ALIGN; } - /* If we get a fault on a dslot, the jmpstate better be in sync. */ - sync_jmpstate(dc); + tcg_gen_qemu_st_i32(reg_for_read(dc, rd), addr, mem_index, mop); - /* Verify alignment if needed. */ - /* - * Microblaze gives MMU faults priority over faults due to - * unaligned addresses. That's why we speculatively do the load - * into v. If the load succeeds, we verify alignment of the - * address and if that succeeds we write into the destination reg. - */ - v = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(v, addr, mem_index, mop); + tcg_temp_free(addr); + return true; +} - if (dc->cpu->cfg.unaligned_exceptions && size > 1) { - TCGv_i32 t0 = tcg_const_i32(0); - TCGv_i32 treg = tcg_const_i32(dc->rd); - TCGv_i32 tsize = tcg_const_i32(size - 1); +static bool trans_sb(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, false); +} - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); - gen_helper_memalign(cpu_env, addr, treg, t0, tsize); +static bool trans_sbr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, true); +} - tcg_temp_free_i32(t0); - tcg_temp_free_i32(treg); - tcg_temp_free_i32(tsize); +static bool trans_sbea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); +#endif +} - if (ex) { - tcg_gen_mov_tl(env_res_addr, addr); - tcg_gen_mov_i32(env_res_val, v); - } - if (dc->rd) { - tcg_gen_mov_i32(cpu_R[dc->rd], v); - } - tcg_temp_free_i32(v); +static bool trans_sbi(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, false); +} - if (ex) { /* lwx */ - /* no support for AXI exclusive so always clear C */ - write_carryi(dc, 0); +static bool trans_sh(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); +} + +static bool trans_shr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); +} + +static bool trans_shea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); +#endif +} - tcg_temp_free(addr); +static bool trans_shi(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); } -static void dec_store(DisasContext *dc) +static bool trans_sw(DisasContext *dc, arg_typea *arg) { - TCGv addr; - TCGLabel *swx_skip = NULL; - unsigned int size; - bool rev = false, ex = false, ea = false; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - MemOp mop; + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - mop = dc->opcode & 3; - size = 1 << mop; - if (!dc->type_b) { - ea = extract32(dc->ir, 7, 1); - rev = extract32(dc->ir, 9, 1); - ex = extract32(dc->ir, 10, 1); - } - mop |= MO_TE; - if (rev) { - mop ^= MO_BSWAP; - } +static bool trans_swr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); +} - if (trap_illegal(dc, size > 4)) { - return; +static bool trans_swea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); +#endif +} + +static bool trans_swi(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - trap_userspace(dc, ea); +static bool trans_swx(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGLabel *swx_done = gen_new_label(); + TCGLabel *swx_fail = gen_new_label(); + TCGv_i32 tval; - LOG_DIS("s%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : "", - ea ? "ea" : ""); - t_sync_flags(dc); - /* If we get a fault on a dslot, the jmpstate better be in sync. */ - sync_jmpstate(dc); - /* SWX needs a temp_local. */ - addr = ex ? tcg_temp_local_new() : tcg_temp_new(); - compute_ldst_addr(dc, ea, addr); - /* Extended addressing bypasses the MMU. */ - mem_index = ea ? MMU_NOMMU_IDX : mem_index; + /* swx does not throw unaligned access errors, so force alignment */ + tcg_gen_andi_tl(addr, addr, ~3); - if (ex) { /* swx */ - TCGv_i32 tval; + /* + * Compare the address vs the one we used during lwx. + * On mismatch, the operation fails. On match, addr dies at the + * branch, but we know we can use the equal version in the global. + * In either case, addr is no longer needed. + */ + tcg_gen_brcond_tl(TCG_COND_NE, cpu_res_addr, addr, swx_fail); + tcg_temp_free(addr); - /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_tl(addr, addr, ~3); + /* + * Compare the value loaded during lwx with current contents of + * the reserved location. + */ + tval = tcg_temp_new_i32(); - write_carryi(dc, 1); - swx_skip = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, addr, swx_skip); + tcg_gen_atomic_cmpxchg_i32(tval, cpu_res_addr, cpu_res_val, + reg_for_write(dc, arg->rd), + dc->mem_index, MO_TEUL); - /* - * Compare the value loaded at lwx with current contents of - * the reserved location. - */ - tval = tcg_temp_new_i32(); + tcg_gen_brcond_i32(TCG_COND_NE, cpu_res_val, tval, swx_fail); + tcg_temp_free_i32(tval); - tcg_gen_atomic_cmpxchg_i32(tval, addr, env_res_val, - cpu_R[dc->rd], mem_index, - mop); + /* Success */ + tcg_gen_movi_i32(cpu_msr_c, 0); + tcg_gen_br(swx_done); - tcg_gen_brcond_i32(TCG_COND_NE, env_res_val, tval, swx_skip); - write_carryi(dc, 0); - tcg_temp_free_i32(tval); - } + /* Failure */ + gen_set_label(swx_fail); + tcg_gen_movi_i32(cpu_msr_c, 1); - if (rev && size != 4) { - /* Endian reverse the address. t is addr. */ - switch (size) { - case 1: - { - tcg_gen_xori_tl(addr, addr, 3); - break; - } + gen_set_label(swx_done); - case 2: - /* 00 -> 10 - 10 -> 00. */ - /* Force addr into the temp. */ - tcg_gen_xori_tl(addr, addr, 2); - break; - default: - cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); - break; - } - } + /* + * Prevent the saved address from working again without another ldx. + * Akin to the pseudocode setting reservation = 0. + */ + tcg_gen_movi_tl(cpu_res_addr, -1); + return true; +} - if (!ex) { - tcg_gen_qemu_st_i32(cpu_R[dc->rd], addr, mem_index, mop); +static void setup_dslot(DisasContext *dc, bool type_b) +{ + dc->tb_flags_to_set |= D_FLAG; + if (type_b && (dc->tb_flags & IMM_FLAG)) { + dc->tb_flags_to_set |= BIMM_FLAG; } +} - /* Verify alignment if needed. */ - if (dc->cpu->cfg.unaligned_exceptions && size > 1) { - TCGv_i32 t1 = tcg_const_i32(1); - TCGv_i32 treg = tcg_const_i32(dc->rd); - TCGv_i32 tsize = tcg_const_i32(size - 1); - - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); - /* FIXME: if the alignment is wrong, we should restore the value - * in memory. One possible way to achieve this is to probe - * the MMU prior to the memaccess, thay way we could put - * the alignment checks in between the probe and the mem - * access. - */ - gen_helper_memalign(cpu_env, addr, treg, t1, tsize); +static bool do_branch(DisasContext *dc, int dest_rb, int dest_imm, + bool delay, bool abs, int link) +{ + uint32_t add_pc; - tcg_temp_free_i32(t1); - tcg_temp_free_i32(treg); - tcg_temp_free_i32(tsize); + if (delay) { + setup_dslot(dc, dest_rb < 0); } - if (ex) { - gen_set_label(swx_skip); + if (link) { + tcg_gen_movi_i32(cpu_R[link], dc->base.pc_next); } - tcg_temp_free(addr); + /* Store the branch taken destination into btarget. */ + add_pc = abs ? 0 : dc->base.pc_next; + if (dest_rb > 0) { + dc->jmp_dest = -1; + tcg_gen_addi_i32(cpu_btarget, cpu_R[dest_rb], add_pc); + } else { + dc->jmp_dest = add_pc + dest_imm; + tcg_gen_movi_i32(cpu_btarget, dc->jmp_dest); + } + dc->jmp_cond = TCG_COND_ALWAYS; + return true; } -static inline void eval_cc(DisasContext *dc, unsigned int cc, - TCGv_i32 d, TCGv_i32 a) +#define DO_BR(NAME, NAMEI, DELAY, ABS, LINK) \ + static bool trans_##NAME(DisasContext *dc, arg_typea_br *arg) \ + { return do_branch(dc, arg->rb, 0, DELAY, ABS, LINK ? arg->rd : 0); } \ + static bool trans_##NAMEI(DisasContext *dc, arg_typeb_br *arg) \ + { return do_branch(dc, -1, arg->imm, DELAY, ABS, LINK ? arg->rd : 0); } + +DO_BR(br, bri, false, false, false) +DO_BR(bra, brai, false, true, false) +DO_BR(brd, brid, true, false, false) +DO_BR(brad, braid, true, true, false) +DO_BR(brld, brlid, true, false, true) +DO_BR(brald, bralid, true, true, true) + +static bool do_bcc(DisasContext *dc, int dest_rb, int dest_imm, + TCGCond cond, int ra, bool delay) { - static const int mb_to_tcg_cc[] = { - [CC_EQ] = TCG_COND_EQ, - [CC_NE] = TCG_COND_NE, - [CC_LT] = TCG_COND_LT, - [CC_LE] = TCG_COND_LE, - [CC_GE] = TCG_COND_GE, - [CC_GT] = TCG_COND_GT, - }; + TCGv_i32 zero, next; - switch (cc) { - case CC_EQ: - case CC_NE: - case CC_LT: - case CC_LE: - case CC_GE: - case CC_GT: - tcg_gen_setcondi_i32(mb_to_tcg_cc[cc], d, a, 0); - break; - default: - cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); - break; + if (delay) { + setup_dslot(dc, dest_rb < 0); } -} -static void eval_cond_jmp(DisasContext *dc, TCGv_i64 pc_true, TCGv_i64 pc_false) -{ - TCGv_i64 tmp_btaken = tcg_temp_new_i64(); - TCGv_i64 tmp_zero = tcg_const_i64(0); + dc->jmp_cond = cond; - tcg_gen_extu_i32_i64(tmp_btaken, env_btaken); - tcg_gen_movcond_i64(TCG_COND_NE, cpu_SR[SR_PC], - tmp_btaken, tmp_zero, - pc_true, pc_false); + /* Cache the condition register in cpu_bvalue across any delay slot. */ + tcg_gen_mov_i32(cpu_bvalue, reg_for_read(dc, ra)); - tcg_temp_free_i64(tmp_btaken); - tcg_temp_free_i64(tmp_zero); + /* Store the branch taken destination into btarget. */ + if (dest_rb > 0) { + dc->jmp_dest = -1; + tcg_gen_addi_i32(cpu_btarget, cpu_R[dest_rb], dc->base.pc_next); + } else { + dc->jmp_dest = dc->base.pc_next + dest_imm; + tcg_gen_movi_i32(cpu_btarget, dc->jmp_dest); + } + + /* Compute the final destination into btarget. */ + zero = tcg_const_i32(0); + next = tcg_const_i32(dc->base.pc_next + (delay + 1) * 4); + tcg_gen_movcond_i32(dc->jmp_cond, cpu_btarget, + reg_for_read(dc, ra), zero, + cpu_btarget, next); + tcg_temp_free_i32(zero); + tcg_temp_free_i32(next); + + return true; } -static void dec_setup_dslot(DisasContext *dc) +#define DO_BCC(NAME, COND) \ + static bool trans_##NAME(DisasContext *dc, arg_typea_bc *arg) \ + { return do_bcc(dc, arg->rb, 0, COND, arg->ra, false); } \ + static bool trans_##NAME##d(DisasContext *dc, arg_typea_bc *arg) \ + { return do_bcc(dc, arg->rb, 0, COND, arg->ra, true); } \ + static bool trans_##NAME##i(DisasContext *dc, arg_typeb_bc *arg) \ + { return do_bcc(dc, -1, arg->imm, COND, arg->ra, false); } \ + static bool trans_##NAME##id(DisasContext *dc, arg_typeb_bc *arg) \ + { return do_bcc(dc, -1, arg->imm, COND, arg->ra, true); } + +DO_BCC(beq, TCG_COND_EQ) +DO_BCC(bge, TCG_COND_GE) +DO_BCC(bgt, TCG_COND_GT) +DO_BCC(ble, TCG_COND_LE) +DO_BCC(blt, TCG_COND_LT) +DO_BCC(bne, TCG_COND_NE) + +static bool trans_brk(DisasContext *dc, arg_typea_br *arg) { - TCGv_i32 tmp = tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)); - - dc->delayed_branch = 2; - dc->tb_flags |= D_FLAG; + if (trap_userspace(dc, true)) { + return true; + } + tcg_gen_mov_i32(cpu_pc, reg_for_read(dc, arg->rb)); + if (arg->rd) { + tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next); + } + tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_BIP); + tcg_gen_movi_tl(cpu_res_addr, -1); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUMBState, bimm)); - tcg_temp_free_i32(tmp); + dc->base.is_jmp = DISAS_UPDATE; + return true; } -static void dec_bcc(DisasContext *dc) +static bool trans_brki(DisasContext *dc, arg_typeb_br *arg) { - unsigned int cc; - unsigned int dslot; - - cc = EXTRACT_FIELD(dc->ir, 21, 23); - dslot = dc->ir & (1 << 25); - LOG_DIS("bcc%s r%d %x\n", dslot ? "d" : "", dc->ra, dc->imm); + uint32_t imm = arg->imm; - dc->delayed_branch = 1; - if (dslot) { - dec_setup_dslot(dc); + if (trap_userspace(dc, imm != 0x8 && imm != 0x18)) { + return true; } + tcg_gen_movi_i32(cpu_pc, imm); + if (arg->rd) { + tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next); + } + tcg_gen_movi_tl(cpu_res_addr, -1); - if (dec_alu_op_b_is_small_imm(dc)) { - int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ +#ifdef CONFIG_USER_ONLY + switch (imm) { + case 0x8: /* syscall trap */ + gen_raise_exception_sync(dc, EXCP_SYSCALL); + break; + case 0x18: /* debug trap */ + gen_raise_exception_sync(dc, EXCP_DEBUG); + break; + default: /* eliminated with trap_userspace check */ + g_assert_not_reached(); + } +#else + uint32_t msr_to_set = 0; - tcg_gen_movi_i64(env_btarget, dc->pc + offset); - dc->jmp = JMP_DIRECT_CC; - dc->jmp_pc = dc->pc + offset; - } else { - dc->jmp = JMP_INDIRECT; - tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); - tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); - tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); + if (imm != 0x18) { + msr_to_set |= MSR_BIP; } - eval_cc(dc, cc, env_btaken, cpu_R[dc->ra]); + if (imm == 0x8 || imm == 0x18) { + /* MSR_UM and MSR_VM are in tb_flags, so we know their value. */ + msr_to_set |= (dc->tb_flags & (MSR_UM | MSR_VM)) << 1; + tcg_gen_andi_i32(cpu_msr, cpu_msr, + ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM)); + } + tcg_gen_ori_i32(cpu_msr, cpu_msr, msr_to_set); + dc->base.is_jmp = DISAS_UPDATE; +#endif + + return true; } -static void dec_br(DisasContext *dc) +static bool trans_mbar(DisasContext *dc, arg_mbar *arg) { - unsigned int dslot, link, abs, mbar; - - dslot = dc->ir & (1 << 20); - abs = dc->ir & (1 << 19); - link = dc->ir & (1 << 18); + int mbar_imm = arg->imm; - /* Memory barrier. */ - mbar = (dc->ir >> 16) & 31; - if (mbar == 2 && dc->imm == 4) { - uint16_t mbar_imm = dc->rd; + /* Data access memory barrier. */ + if ((mbar_imm & 2) == 0) { + tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + } - LOG_DIS("mbar %d\n", mbar_imm); + /* Sleep. */ + if (mbar_imm & 16) { + TCGv_i32 tmp_1; - /* Data access memory barrier. */ - if ((mbar_imm & 2) == 0) { - tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + if (trap_userspace(dc, true)) { + /* Sleep is a privileged instruction. */ + return true; } - /* mbar IMM & 16 decodes to sleep. */ - if (mbar_imm & 16) { - TCGv_i32 tmp_hlt = tcg_const_i32(EXCP_HLT); - TCGv_i32 tmp_1 = tcg_const_i32(1); + t_sync_flags(dc); - LOG_DIS("sleep\n"); + tmp_1 = tcg_const_i32(1); + tcg_gen_st_i32(tmp_1, cpu_env, + -offsetof(MicroBlazeCPU, env) + +offsetof(CPUState, halted)); + tcg_temp_free_i32(tmp_1); - if (trap_userspace(dc, true)) { - /* Sleep is a privileged instruction. */ - return; - } + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next + 4); - t_sync_flags(dc); - tcg_gen_st_i32(tmp_1, cpu_env, - -offsetof(MicroBlazeCPU, env) - +offsetof(CPUState, halted)); - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); - gen_helper_raise_exception(cpu_env, tmp_hlt); - tcg_temp_free_i32(tmp_hlt); - tcg_temp_free_i32(tmp_1); - return; - } - /* Break the TB. */ - dc->cpustate_changed = 1; - return; + gen_raise_exception(dc, EXCP_HLT); } - LOG_DIS("br%s%s%s%s imm=%x\n", - abs ? "a" : "", link ? "l" : "", - dc->type_b ? "i" : "", dslot ? "d" : "", - dc->imm); + /* + * If !(mbar_imm & 1), this is an instruction access memory barrier + * and we need to end the TB so that we recognize self-modified + * code immediately. + * + * However, there are some data mbars that need the TB break + * (and return to main loop) to recognize interrupts right away. + * E.g. recognizing a change to an interrupt controller register. + * + * Therefore, choose to end the TB always. + */ + dc->cpustate_changed = 1; + return true; +} - dc->delayed_branch = 1; - if (dslot) { - dec_setup_dslot(dc); +static bool do_rts(DisasContext *dc, arg_typeb_bc *arg, int to_set) +{ + if (trap_userspace(dc, to_set)) { + return true; } - if (link && dc->rd) - tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); - - dc->jmp = JMP_INDIRECT; - if (abs) { - tcg_gen_movi_i32(env_btaken, 1); - tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); - if (link && !dslot) { - if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) - t_gen_raise_exception(dc, EXCP_BREAK); - if (dc->imm == 0) { - if (trap_userspace(dc, true)) { - return; - } + dc->tb_flags_to_set |= to_set; + setup_dslot(dc, true); - t_gen_raise_exception(dc, EXCP_DEBUG); - } - } - } else { - if (dec_alu_op_b_is_small_imm(dc)) { - dc->jmp = JMP_DIRECT; - dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); - } else { - tcg_gen_movi_i32(env_btaken, 1); - tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); - tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); - tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); - } - } + dc->jmp_cond = TCG_COND_ALWAYS; + dc->jmp_dest = -1; + tcg_gen_addi_i32(cpu_btarget, reg_for_read(dc, arg->ra), arg->imm); + return true; } -static inline void do_rti(DisasContext *dc) -{ - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); - tcg_gen_shri_i32(t0, t1, 1); - tcg_gen_ori_i32(t1, t1, MSR_IE); - tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); +#define DO_RTS(NAME, IFLAG) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb_bc *arg) \ + { return do_rts(dc, arg, IFLAG); } - tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_i32(t1, t1, t0); - msr_write(dc, t1); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - dc->tb_flags &= ~DRTI_FLAG; +DO_RTS(rtbd, DRTB_FLAG) +DO_RTS(rtid, DRTI_FLAG) +DO_RTS(rted, DRTE_FLAG) +DO_RTS(rtsd, 0) + +static bool trans_zero(DisasContext *dc, arg_zero *arg) +{ + /* If opcode_0_illegal, trap. */ + if (dc->cpu->cfg.opcode_0_illegal) { + trap_illegal(dc, true); + return true; + } + /* + * Otherwise, this is "add r0, r0, r0". + * Continue to trans_add so that MSR[C] gets cleared. + */ + return false; } -static inline void do_rtb(DisasContext *dc) +static void msr_read(DisasContext *dc, TCGv_i32 d) { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); - tcg_gen_andi_i32(t1, t1, ~MSR_BIP); - tcg_gen_shri_i32(t0, t1, 1); - tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); + TCGv_i32 t; - tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_i32(t1, t1, t0); - msr_write(dc, t1); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - dc->tb_flags &= ~DRTB_FLAG; + /* Replicate the cpu_msr_c boolean into the proper bit and the copy. */ + t = tcg_temp_new_i32(); + tcg_gen_muli_i32(t, cpu_msr_c, MSR_C | MSR_CC); + tcg_gen_or_i32(d, cpu_msr, t); + tcg_temp_free_i32(t); } -static inline void do_rte(DisasContext *dc) +#ifndef CONFIG_USER_ONLY +static void msr_write(DisasContext *dc, TCGv_i32 v) { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); + dc->cpustate_changed = 1; - tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); - tcg_gen_ori_i32(t1, t1, MSR_EE); - tcg_gen_andi_i32(t1, t1, ~MSR_EIP); - tcg_gen_shri_i32(t0, t1, 1); - tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); + /* Install MSR_C. */ + tcg_gen_extract_i32(cpu_msr_c, v, 2, 1); - tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_i32(t1, t1, t0); - msr_write(dc, t1); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - dc->tb_flags &= ~DRTE_FLAG; + /* Clear MSR_C and MSR_CC; MSR_PVR is not writable, and is always clear. */ + tcg_gen_andi_i32(cpu_msr, v, ~(MSR_C | MSR_CC | MSR_PVR)); } +#endif -static void dec_rts(DisasContext *dc) +static bool do_msrclrset(DisasContext *dc, arg_type_msr *arg, bool set) { - unsigned int b_bit, i_bit, e_bit; - TCGv_i64 tmp64; + uint32_t imm = arg->imm; - i_bit = dc->ir & (1 << 21); - b_bit = dc->ir & (1 << 22); - e_bit = dc->ir & (1 << 23); - - if (trap_userspace(dc, i_bit || b_bit || e_bit)) { - return; + if (trap_userspace(dc, imm != MSR_C)) { + return true; } - dec_setup_dslot(dc); + if (arg->rd) { + msr_read(dc, cpu_R[arg->rd]); + } - if (i_bit) { - LOG_DIS("rtid ir=%x\n", dc->ir); - dc->tb_flags |= DRTI_FLAG; - } else if (b_bit) { - LOG_DIS("rtbd ir=%x\n", dc->ir); - dc->tb_flags |= DRTB_FLAG; - } else if (e_bit) { - LOG_DIS("rted ir=%x\n", dc->ir); - dc->tb_flags |= DRTE_FLAG; - } else - LOG_DIS("rts ir=%x\n", dc->ir); + /* + * Handle the carry bit separately. + * This is the only bit that userspace can modify. + */ + if (imm & MSR_C) { + tcg_gen_movi_i32(cpu_msr_c, set); + } - dc->jmp = JMP_INDIRECT; - tcg_gen_movi_i32(env_btaken, 1); + /* + * MSR_C and MSR_CC set above. + * MSR_PVR is not writable, and is always clear. + */ + imm &= ~(MSR_C | MSR_CC | MSR_PVR); - tmp64 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); - tcg_gen_extu_i32_i64(tmp64, cpu_R[dc->ra]); - tcg_gen_add_i64(env_btarget, env_btarget, tmp64); - tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); - tcg_temp_free_i64(tmp64); + if (imm != 0) { + if (set) { + tcg_gen_ori_i32(cpu_msr, cpu_msr, imm); + } else { + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~imm); + } + dc->cpustate_changed = 1; + } + return true; } -static int dec_check_fpuv2(DisasContext *dc) +static bool trans_msrclr(DisasContext *dc, arg_type_msr *arg) { - if ((dc->cpu->cfg.use_fpu != 2) && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_FPU); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } - return (dc->cpu->cfg.use_fpu == 2) ? PVR2_USE_FPU2_MASK : 0; + return do_msrclrset(dc, arg, false); } -static void dec_fpu(DisasContext *dc) +static bool trans_msrset(DisasContext *dc, arg_type_msr *arg) { - unsigned int fpu_insn; + return do_msrclrset(dc, arg, true); +} - if (trap_illegal(dc, !dc->cpu->cfg.use_fpu)) { - return; +static bool trans_mts(DisasContext *dc, arg_mts *arg) +{ + if (trap_userspace(dc, true)) { + return true; } - fpu_insn = (dc->ir >> 7) & 7; - - switch (fpu_insn) { - case 0: - gen_helper_fadd(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], - cpu_R[dc->rb]); - break; +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + if (arg->e && arg->rs != 0x1003) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid extended mts reg 0x%x\n", arg->rs); + return true; + } - case 1: - gen_helper_frsub(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], - cpu_R[dc->rb]); - break; + TCGv_i32 src = reg_for_read(dc, arg->ra); + switch (arg->rs) { + case SR_MSR: + msr_write(dc, src); + break; + case SR_FSR: + tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, fsr)); + break; + case 0x800: + tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, slr)); + break; + case 0x802: + tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, shr)); + break; - case 2: - gen_helper_fmul(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], - cpu_R[dc->rb]); - break; + case 0x1000: /* PID */ + case 0x1001: /* ZPR */ + case 0x1002: /* TLBX */ + case 0x1003: /* TLBLO */ + case 0x1004: /* TLBHI */ + case 0x1005: /* TLBSX */ + { + TCGv_i32 tmp_ext = tcg_const_i32(arg->e); + TCGv_i32 tmp_reg = tcg_const_i32(arg->rs & 7); + + gen_helper_mmu_write(cpu_env, tmp_ext, tmp_reg, src); + tcg_temp_free_i32(tmp_reg); + tcg_temp_free_i32(tmp_ext); + } + break; - case 3: - gen_helper_fdiv(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], - cpu_R[dc->rb]); - break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid mts reg 0x%x\n", arg->rs); + return true; + } + dc->cpustate_changed = 1; + return true; +#endif +} - case 4: - switch ((dc->ir >> 4) & 7) { - case 0: - gen_helper_fcmp_un(cpu_R[dc->rd], cpu_env, - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 1: - gen_helper_fcmp_lt(cpu_R[dc->rd], cpu_env, - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 2: - gen_helper_fcmp_eq(cpu_R[dc->rd], cpu_env, - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 3: - gen_helper_fcmp_le(cpu_R[dc->rd], cpu_env, - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 4: - gen_helper_fcmp_gt(cpu_R[dc->rd], cpu_env, - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 5: - gen_helper_fcmp_ne(cpu_R[dc->rd], cpu_env, - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - case 6: - gen_helper_fcmp_ge(cpu_R[dc->rd], cpu_env, - cpu_R[dc->ra], cpu_R[dc->rb]); - break; - default: - qemu_log_mask(LOG_UNIMP, - "unimplemented fcmp fpu_insn=%x pc=%x" - " opc=%x\n", - fpu_insn, dc->pc, dc->opcode); - dc->abort_at_next_insn = 1; - break; - } - break; +static bool trans_mfs(DisasContext *dc, arg_mfs *arg) +{ + TCGv_i32 dest = reg_for_write(dc, arg->rd); - case 5: - if (!dec_check_fpuv2(dc)) { - return; + if (arg->e) { + switch (arg->rs) { + case SR_EAR: + { + TCGv_i64 t64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(t64, cpu_env, offsetof(CPUMBState, ear)); + tcg_gen_extrh_i64_i32(dest, t64); + tcg_temp_free_i64(t64); } - gen_helper_flt(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); + return true; +#ifndef CONFIG_USER_ONLY + case 0x1003: /* TLBLO */ + /* Handled below. */ break; +#endif + case 0x2006 ... 0x2009: + /* High bits of PVR6-9 not implemented. */ + tcg_gen_movi_i32(dest, 0); + return true; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid extended mfs reg 0x%x\n", arg->rs); + return true; + } + } - case 6: - if (!dec_check_fpuv2(dc)) { - return; - } - gen_helper_fint(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); - break; + switch (arg->rs) { + case SR_PC: + tcg_gen_movi_i32(dest, dc->base.pc_next); + break; + case SR_MSR: + msr_read(dc, dest); + break; + case SR_EAR: + { + TCGv_i64 t64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(t64, cpu_env, offsetof(CPUMBState, ear)); + tcg_gen_extrl_i64_i32(dest, t64); + tcg_temp_free_i64(t64); + } + break; + case SR_ESR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, esr)); + break; + case SR_FSR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, fsr)); + break; + case SR_BTR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, btr)); + break; + case SR_EDR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, edr)); + break; + case 0x800: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, slr)); + break; + case 0x802: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, shr)); + break; - case 7: - if (!dec_check_fpuv2(dc)) { - return; - } - gen_helper_fsqrt(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); - break; +#ifndef CONFIG_USER_ONLY + case 0x1000: /* PID */ + case 0x1001: /* ZPR */ + case 0x1002: /* TLBX */ + case 0x1003: /* TLBLO */ + case 0x1004: /* TLBHI */ + case 0x1005: /* TLBSX */ + { + TCGv_i32 tmp_ext = tcg_const_i32(arg->e); + TCGv_i32 tmp_reg = tcg_const_i32(arg->rs & 7); + + gen_helper_mmu_read(dest, cpu_env, tmp_ext, tmp_reg); + tcg_temp_free_i32(tmp_reg); + tcg_temp_free_i32(tmp_ext); + } + break; +#endif - default: - qemu_log_mask(LOG_UNIMP, "unimplemented FPU insn fpu_insn=%x pc=%x" - " opc=%x\n", - fpu_insn, dc->pc, dc->opcode); - dc->abort_at_next_insn = 1; - break; + case 0x2000 ... 0x200c: + tcg_gen_ld_i32(dest, cpu_env, + offsetof(CPUMBState, pvr.regs[arg->rs - 0x2000])); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid mfs reg 0x%x\n", arg->rs); + break; } + return true; } -static void dec_null(DisasContext *dc) +static void do_rti(DisasContext *dc) { - if (trap_illegal(dc, true)) { - return; - } - qemu_log_mask(LOG_GUEST_ERROR, "unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); - dc->abort_at_next_insn = 1; + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_shri_i32(tmp, cpu_msr, 1); + tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_IE); + tcg_gen_andi_i32(tmp, tmp, MSR_VM | MSR_UM); + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); + + tcg_temp_free_i32(tmp); + dc->tb_flags &= ~DRTI_FLAG; +} + +static void do_rtb(DisasContext *dc) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_shri_i32(tmp, cpu_msr, 1); + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM | MSR_BIP)); + tcg_gen_andi_i32(tmp, tmp, (MSR_VM | MSR_UM)); + tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); + + tcg_temp_free_i32(tmp); + dc->tb_flags &= ~DRTB_FLAG; +} + +static void do_rte(DisasContext *dc) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_shri_i32(tmp, cpu_msr, 1); + tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_EE); + tcg_gen_andi_i32(tmp, tmp, (MSR_VM | MSR_UM)); + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM | MSR_EIP)); + tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); + + tcg_temp_free_i32(tmp); + dc->tb_flags &= ~DRTE_FLAG; } /* Insns connected to FSL or AXI stream attached devices. */ -static void dec_stream(DisasContext *dc) +static bool do_get(DisasContext *dc, int rd, int rb, int imm, int ctrl) { TCGv_i32 t_id, t_ctrl; - int ctrl; - - LOG_DIS("%s%s imm=%x\n", dc->rd ? "get" : "put", - dc->type_b ? "" : "d", dc->imm); if (trap_userspace(dc, true)) { - return; + return true; } t_id = tcg_temp_new_i32(); - if (dc->type_b) { - tcg_gen_movi_i32(t_id, dc->imm & 0xf); - ctrl = dc->imm >> 10; + if (rb) { + tcg_gen_andi_i32(t_id, cpu_R[rb], 0xf); } else { - tcg_gen_andi_i32(t_id, cpu_R[dc->rb], 0xf); - ctrl = dc->imm >> 5; + tcg_gen_movi_i32(t_id, imm); } t_ctrl = tcg_const_i32(ctrl); + gen_helper_get(reg_for_write(dc, rd), t_id, t_ctrl); + tcg_temp_free_i32(t_id); + tcg_temp_free_i32(t_ctrl); + return true; +} - if (dc->rd == 0) { - gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]); +static bool trans_get(DisasContext *dc, arg_get *arg) +{ + return do_get(dc, arg->rd, 0, arg->imm, arg->ctrl); +} + +static bool trans_getd(DisasContext *dc, arg_getd *arg) +{ + return do_get(dc, arg->rd, arg->rb, 0, arg->ctrl); +} + +static bool do_put(DisasContext *dc, int ra, int rb, int imm, int ctrl) +{ + TCGv_i32 t_id, t_ctrl; + + if (trap_userspace(dc, true)) { + return true; + } + + t_id = tcg_temp_new_i32(); + if (rb) { + tcg_gen_andi_i32(t_id, cpu_R[rb], 0xf); } else { - gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl); + tcg_gen_movi_i32(t_id, imm); } + + t_ctrl = tcg_const_i32(ctrl); + gen_helper_put(t_id, t_ctrl, reg_for_read(dc, ra)); tcg_temp_free_i32(t_id); tcg_temp_free_i32(t_ctrl); + return true; } -static struct decoder_info { - struct { - uint32_t bits; - uint32_t mask; - }; - void (*dec)(DisasContext *dc); -} decinfo[] = { - {DEC_ADD, dec_add}, - {DEC_SUB, dec_sub}, - {DEC_AND, dec_and}, - {DEC_XOR, dec_xor}, - {DEC_OR, dec_or}, - {DEC_BIT, dec_bit}, - {DEC_BARREL, dec_barrel}, - {DEC_LD, dec_load}, - {DEC_ST, dec_store}, - {DEC_IMM, dec_imm}, - {DEC_BR, dec_br}, - {DEC_BCC, dec_bcc}, - {DEC_RTS, dec_rts}, - {DEC_FPU, dec_fpu}, - {DEC_MUL, dec_mul}, - {DEC_DIV, dec_div}, - {DEC_MSR, dec_msr}, - {DEC_STREAM, dec_stream}, - {{0, 0}, dec_null} -}; +static bool trans_put(DisasContext *dc, arg_put *arg) +{ + return do_put(dc, arg->ra, 0, arg->imm, arg->ctrl); +} -static inline void decode(DisasContext *dc, uint32_t ir) +static bool trans_putd(DisasContext *dc, arg_putd *arg) { - int i; + return do_put(dc, arg->ra, arg->rb, 0, arg->ctrl); +} - dc->ir = ir; - LOG_DIS("%8.8x\t", dc->ir); +static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + int bound; - if (ir == 0) { - trap_illegal(dc, dc->cpu->cfg.opcode_0_illegal); - /* Don't decode nop/zero instructions any further. */ - return; - } + dc->cpu = cpu; + dc->tb_flags = dc->base.tb->flags; + dc->cpustate_changed = 0; + dc->ext_imm = dc->base.tb->cs_base; + dc->r0 = NULL; + dc->r0_set = false; + dc->mem_index = cpu_mmu_index(&cpu->env, false); + dc->jmp_cond = dc->tb_flags & D_FLAG ? TCG_COND_ALWAYS : TCG_COND_NEVER; + dc->jmp_dest = -1; + + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); +} - /* bit 2 seems to indicate insn type. */ - dc->type_b = ir & (1 << 29); +static void mb_tr_tb_start(DisasContextBase *dcb, CPUState *cs) +{ +} - dc->opcode = EXTRACT_FIELD(ir, 26, 31); - dc->rd = EXTRACT_FIELD(ir, 21, 25); - dc->ra = EXTRACT_FIELD(ir, 16, 20); - dc->rb = EXTRACT_FIELD(ir, 11, 15); - dc->imm = EXTRACT_FIELD(ir, 0, 15); +static void mb_tr_insn_start(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); - /* Large switch for all insns. */ - for (i = 0; i < ARRAY_SIZE(decinfo); i++) { - if ((dc->opcode & decinfo[i].mask) == decinfo[i].bits) { - decinfo[i].dec(dc); - break; - } - } + tcg_gen_insn_start(dc->base.pc_next, dc->tb_flags & ~MSR_TB_MASK); + dc->insn_start = tcg_last_op(); } -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +static bool mb_tr_breakpoint_check(DisasContextBase *dcb, CPUState *cs, + const CPUBreakpoint *bp) { + DisasContext *dc = container_of(dcb, DisasContext, base); + + gen_raise_exception_sync(dc, EXCP_DEBUG); + + /* + * The address covered by the breakpoint must be included in + * [tb->pc, tb->pc + tb->size) in order to for it to be + * properly cleared -- thus we increment the PC here so that + * the logic setting tb->size below does the right thing. + */ + dc->base.pc_next += 4; + return true; +} + +static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); CPUMBState *env = cs->env_ptr; - MicroBlazeCPU *cpu = env_archcpu(env); - uint32_t pc_start; - struct DisasContext ctx; - struct DisasContext *dc = &ctx; - uint32_t page_start, org_flags; - uint32_t npc; - int num_insns; - - pc_start = tb->pc; - dc->cpu = cpu; - dc->tb = tb; - org_flags = dc->synced_flags = dc->tb_flags = tb->flags; + uint32_t ir; - dc->is_jmp = DISAS_NEXT; - dc->jmp = 0; - dc->delayed_branch = !!(dc->tb_flags & D_FLAG); - if (dc->delayed_branch) { - dc->jmp = JMP_INDIRECT; + /* TODO: This should raise an exception, not terminate qemu. */ + if (dc->base.pc_next & 3) { + cpu_abort(cs, "Microblaze: unaligned PC=%x\n", + (uint32_t)dc->base.pc_next); } - dc->pc = pc_start; - dc->singlestep_enabled = cs->singlestep_enabled; - dc->cpustate_changed = 0; - dc->abort_at_next_insn = 0; - if (pc_start & 3) { - cpu_abort(cs, "Microblaze: unaligned PC=%x\n", pc_start); + dc->tb_flags_to_set = 0; + + ir = cpu_ldl_code(env, dc->base.pc_next); + if (!decode(dc, ir)) { + trap_illegal(dc, true); } - page_start = pc_start & TARGET_PAGE_MASK; - num_insns = 0; + if (dc->r0) { + tcg_temp_free_i32(dc->r0); + dc->r0 = NULL; + dc->r0_set = false; + } - gen_tb_start(tb); - do - { - tcg_gen_insn_start(dc->pc); - num_insns++; + /* Discard the imm global when its contents cannot be used. */ + if ((dc->tb_flags & ~dc->tb_flags_to_set) & IMM_FLAG) { + tcg_gen_discard_i32(cpu_imm); + } -#if SIM_COMPAT - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); - gen_helper_debug(); - } -#endif + dc->tb_flags &= ~(IMM_FLAG | BIMM_FLAG | D_FLAG); + dc->tb_flags |= dc->tb_flags_to_set; + dc->base.pc_next += 4; - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - t_gen_raise_exception(dc, EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc->pc += 4; - break; + if (dc->jmp_cond != TCG_COND_NEVER && !(dc->tb_flags & D_FLAG)) { + if (dc->tb_flags & DRTI_FLAG) { + do_rti(dc); + } else if (dc->tb_flags & DRTB_FLAG) { + do_rtb(dc); + } else if (dc->tb_flags & DRTE_FLAG) { + do_rte(dc); } + dc->base.is_jmp = DISAS_JUMP; + } - /* Pretty disas. */ - LOG_DIS("%8.8x:\t", dc->pc); + /* Force an exit if the per-tb cpu state has changed. */ + if (dc->base.is_jmp == DISAS_NEXT && dc->cpustate_changed) { + dc->base.is_jmp = DISAS_UPDATE; + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); + } +} - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } +static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); - dc->clear_imm = 1; - decode(dc, cpu_ldl_code(env, dc->pc)); - if (dc->clear_imm) - dc->tb_flags &= ~IMM_FLAG; - dc->pc += 4; - - if (dc->delayed_branch) { - dc->delayed_branch--; - if (!dc->delayed_branch) { - if (dc->tb_flags & DRTI_FLAG) - do_rti(dc); - if (dc->tb_flags & DRTB_FLAG) - do_rtb(dc); - if (dc->tb_flags & DRTE_FLAG) - do_rte(dc); - /* Clear the delay slot flag. */ - dc->tb_flags &= ~D_FLAG; - /* If it is a direct jump, try direct chaining. */ - if (dc->jmp == JMP_INDIRECT) { - TCGv_i64 tmp_pc = tcg_const_i64(dc->pc); - eval_cond_jmp(dc, env_btarget, tmp_pc); - tcg_temp_free_i64(tmp_pc); - - dc->is_jmp = DISAS_JUMP; - } else if (dc->jmp == JMP_DIRECT) { - t_sync_flags(dc); - gen_goto_tb(dc, 0, dc->jmp_pc); - dc->is_jmp = DISAS_TB_JUMP; - } else if (dc->jmp == JMP_DIRECT_CC) { - TCGLabel *l1 = gen_new_label(); - t_sync_flags(dc); - /* Conditional jmp. */ - tcg_gen_brcondi_i32(TCG_COND_NE, env_btaken, 0, l1); - gen_goto_tb(dc, 1, dc->pc); - gen_set_label(l1); - gen_goto_tb(dc, 0, dc->jmp_pc); - - dc->is_jmp = DISAS_TB_JUMP; - } - break; - } - } - if (cs->singlestep_enabled) { - break; - } - } while (!dc->is_jmp && !dc->cpustate_changed - && !tcg_op_buf_full() - && !singlestep - && (dc->pc - page_start < TARGET_PAGE_SIZE) - && num_insns < max_insns); - - npc = dc->pc; - if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { - if (dc->tb_flags & D_FLAG) { - dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_i64(cpu_SR[SR_PC], npc); - sync_jmpstate(dc); - } else - npc = dc->jmp_pc; - } - - /* Force an update if the per-tb cpu state has changed. */ - if (dc->is_jmp == DISAS_NEXT - && (dc->cpustate_changed || org_flags != dc->tb_flags)) { - dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_i64(cpu_SR[SR_PC], npc); + if (dc->base.is_jmp == DISAS_NORETURN) { + /* We have already exited the TB. */ + return; } + t_sync_flags(dc); - if (unlikely(cs->singlestep_enabled)) { - TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); + switch (dc->base.is_jmp) { + case DISAS_TOO_MANY: + gen_goto_tb(dc, 0, dc->base.pc_next); + return; - if (dc->is_jmp != DISAS_JUMP) { - tcg_gen_movi_i64(cpu_SR[SR_PC], npc); + case DISAS_UPDATE: + if (unlikely(cs->singlestep_enabled)) { + gen_raise_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); } - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); - } else { - switch(dc->is_jmp) { - case DISAS_NEXT: - gen_goto_tb(dc, 1, npc); - break; - default: - case DISAS_JUMP: - case DISAS_UPDATE: - /* indicate that the hash table must be used - to find the next TB */ - tcg_gen_exit_tb(NULL, 0); - break; - case DISAS_TB_JUMP: - /* nothing more to generate */ - break; + return; + + case DISAS_JUMP: + if (dc->jmp_dest != -1 && !cs->singlestep_enabled) { + /* Direct jump. */ + tcg_gen_discard_i32(cpu_btarget); + + if (dc->jmp_cond != TCG_COND_ALWAYS) { + /* Conditional direct jump. */ + TCGLabel *taken = gen_new_label(); + TCGv_i32 tmp = tcg_temp_new_i32(); + + /* + * Copy bvalue to a temp now, so we can discard bvalue. + * This can avoid writing bvalue to memory when the + * delay slot cannot raise an exception. + */ + tcg_gen_mov_i32(tmp, cpu_bvalue); + tcg_gen_discard_i32(cpu_bvalue); + + tcg_gen_brcondi_i32(dc->jmp_cond, tmp, 0, taken); + gen_goto_tb(dc, 1, dc->base.pc_next); + gen_set_label(taken); + } + gen_goto_tb(dc, 0, dc->jmp_dest); + return; } - } - gen_tb_end(tb, num_insns); - tb->size = dc->pc - pc_start; - tb->icount = num_insns; + /* Indirect jump (or direct jump w/ singlestep) */ + tcg_gen_mov_i32(cpu_pc, cpu_btarget); + tcg_gen_discard_i32(cpu_btarget); + + if (unlikely(cs->singlestep_enabled)) { + gen_raise_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); + } + return; -#ifdef DEBUG_DISAS -#if !SIM_COMPAT - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - FILE *logfile = qemu_log_lock(); - qemu_log("--------------\n"); - log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log_unlock(logfile); + default: + g_assert_not_reached(); } -#endif -#endif - assert(!dc->abort_at_next_insn); +} + +static void mb_tr_disas_log(const DisasContextBase *dcb, CPUState *cs) +{ + qemu_log("IN: %s\n", lookup_symbol(dcb->pc_first)); + log_target_disas(cs, dcb->pc_first, dcb->tb->size); +} + +static const TranslatorOps mb_tr_ops = { + .init_disas_context = mb_tr_init_disas_context, + .tb_start = mb_tr_tb_start, + .insn_start = mb_tr_insn_start, + .breakpoint_check = mb_tr_breakpoint_check, + .translate_insn = mb_tr_translate_insn, + .tb_stop = mb_tr_tb_stop, + .disas_log = mb_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +{ + DisasContext dc; + translator_loop(&mb_tr_ops, &dc.base, cpu, tb, max_insns); } void mb_cpu_dump_state(CPUState *cs, FILE *f, int flags) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; + uint32_t iflags; int i; - if (!env) { - return; + qemu_fprintf(f, "pc=0x%08x msr=0x%05x mode=%s(saved=%s) eip=%d ie=%d\n", + env->pc, env->msr, + (env->msr & MSR_UM) ? "user" : "kernel", + (env->msr & MSR_UMS) ? "user" : "kernel", + (bool)(env->msr & MSR_EIP), + (bool)(env->msr & MSR_IE)); + + iflags = env->iflags; + qemu_fprintf(f, "iflags: 0x%08x", iflags); + if (iflags & IMM_FLAG) { + qemu_fprintf(f, " IMM(0x%08x)", env->imm); + } + if (iflags & BIMM_FLAG) { + qemu_fprintf(f, " BIMM"); + } + if (iflags & D_FLAG) { + qemu_fprintf(f, " D(btarget=0x%08x)", env->btarget); } + if (iflags & DRTI_FLAG) { + qemu_fprintf(f, " DRTI"); + } + if (iflags & DRTE_FLAG) { + qemu_fprintf(f, " DRTE"); + } + if (iflags & DRTB_FLAG) { + qemu_fprintf(f, " DRTB"); + } + if (iflags & ESR_ESS_FLAG) { + qemu_fprintf(f, " ESR_ESS(0x%04x)", iflags & ESR_ESS_MASK); + } + + qemu_fprintf(f, "\nesr=0x%04x fsr=0x%02x btr=0x%08x edr=0x%x\n" + "ear=0x" TARGET_FMT_lx " slr=0x%x shr=0x%x\n", + env->esr, env->fsr, env->btr, env->edr, + env->ear, env->slr, env->shr); - qemu_fprintf(f, "IN: PC=%" PRIx64 " %s\n", - env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); - qemu_fprintf(f, "rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " - "debug=%x imm=%x iflags=%x fsr=%" PRIx64 " " - "rbtr=%" PRIx64 "\n", - env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], - env->debug, env->imm, env->iflags, env->sregs[SR_FSR], - env->sregs[SR_BTR]); - qemu_fprintf(f, "btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) " - "eip=%d ie=%d\n", - env->btaken, env->btarget, - (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", - (bool)(env->sregs[SR_MSR] & MSR_EIP), - (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 12; i++) { - qemu_fprintf(f, "rpvr%2.2d=%8.8x ", i, env->pvr.regs[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } + qemu_fprintf(f, "rpvr%-2d=%08x%c", + i, env->pvr.regs[i], i % 4 == 3 ? '\n' : ' '); } - /* Registers that aren't modeled are reported as 0 */ - qemu_fprintf(f, "redr=%" PRIx64 " rpid=0 rzpr=0 rtlbx=0 rtlbsx=0 " - "rtlblo=0 rtlbhi=0\n", env->sregs[SR_EDR]); - qemu_fprintf(f, "slr=%x shr=%x\n", env->slr, env->shr); for (i = 0; i < 32; i++) { - qemu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); - if ((i + 1) % 4 == 0) - qemu_fprintf(f, "\n"); - } - qemu_fprintf(f, "\n\n"); + qemu_fprintf(f, "r%2.2d=%08x%c", + i, env->regs[i], i % 4 == 3 ? '\n' : ' '); + } + qemu_fprintf(f, "\n"); } void mb_tcg_init(void) { - int i; +#define R(X) { &cpu_R[X], offsetof(CPUMBState, regs[X]), "r" #X } +#define SP(X) { &cpu_##X, offsetof(CPUMBState, X), #X } + + static const struct { + TCGv_i32 *var; int ofs; char name[8]; + } i32s[] = { + /* + * Note that r0 is handled specially in reg_for_read + * and reg_for_write. Nothing should touch cpu_R[0]. + * Leave that element NULL, which will assert quickly + * inside the tcg generator functions. + */ + R(1), R(2), R(3), R(4), R(5), R(6), R(7), + R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), + R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23), + R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31), + + SP(pc), + SP(msr), + SP(msr_c), + SP(imm), + SP(iflags), + SP(bvalue), + SP(btarget), + SP(res_val), + }; - env_debug = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, debug), - "debug0"); - env_iflags = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, iflags), - "iflags"); - env_imm = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, imm), - "imm"); - env_btarget = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUMBState, btarget), - "btarget"); - env_btaken = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, btaken), - "btaken"); - env_res_addr = tcg_global_mem_new(cpu_env, - offsetof(CPUMBState, res_addr), - "res_addr"); - env_res_val = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, res_val), - "res_val"); - for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { - cpu_R[i] = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, regs[i]), - regnames[i]); - } - for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { - cpu_SR[i] = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUMBState, sregs[i]), - special_regnames[i]); +#undef R +#undef SP + + for (int i = 0; i < ARRAY_SIZE(i32s); ++i) { + *i32s[i].var = + tcg_global_mem_new_i32(cpu_env, i32s[i].ofs, i32s[i].name); } + + cpu_res_addr = + tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); } void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, target_ulong *data) { - env->sregs[SR_PC] = data[0]; + env->pc = data[0]; + env->iflags = data[1]; } |