target/microblaze: Convert dec_add to decodetree

Adds infrastrucure for translation of instructions, which could
not be added before their first use.  Cache a temporary which
represents r0 as the immediate 0 value, or a sink.

Move the special case of opcode_0_illegal from old_decode()
into decodetree as well, lest this get interpreted as add.

Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2020-08-17 22:17:58 -07:00
parent 44d1432ba2
commit 2080017965
2 changed files with 178 additions and 43 deletions

View File

@ -16,3 +16,27 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
&typea rd ra rb
&typeb rd ra imm
# Include any IMM prefix in the value reported.
%extimm 0:s16 !function=typeb_imm
@typea ...... rd:5 ra:5 rb:5 ... .... .... &typea
@typeb ...... rd:5 ra:5 ................ &typeb imm=%extimm
###
{
zero 000000 00000 00000 00000 000 0000 0000
add 000000 ..... ..... ..... 000 0000 0000 @typea
}
addc 000010 ..... ..... ..... 000 0000 0000 @typea
addk 000100 ..... ..... ..... 000 0000 0000 @typea
addkc 000110 ..... ..... ..... 000 0000 0000 @typea
addi 001000 ..... ..... ................ @typeb
addic 001010 ..... ..... ................ @typeb
addik 001100 ..... ..... ................ @typeb
addikc 001110 ..... ..... ................ @typeb

View File

@ -58,6 +58,9 @@ typedef struct DisasContext {
DisasContextBase base;
MicroBlazeCPU *cpu;
TCGv_i32 r0;
bool r0_set;
/* Decoder. */
int type_b;
uint32_t ir;
@ -81,6 +84,14 @@ typedef struct DisasContext {
int abort_at_next_insn;
} DisasContext;
static int typeb_imm(DisasContext *dc, int x)
{
if (dc->tb_flags & IMM_FLAG) {
return deposit32(dc->ext_imm, 0, 16, x);
}
return x;
}
/* Include the auto-generated decoder. */
#include "decode-insns.c.inc"
@ -176,11 +187,7 @@ static bool trap_userspace(DisasContext *dc, bool cond)
static int32_t dec_alu_typeb_imm(DisasContext *dc)
{
tcg_debug_assert(dc->type_b);
if (dc->tb_flags & IMM_FLAG) {
return dc->ext_imm | dc->imm;
} else {
return (int16_t)dc->imm;
}
return typeb_imm(dc, (int16_t)dc->imm);
}
static inline TCGv_i32 *dec_alu_op_b(DisasContext *dc)
@ -192,44 +199,146 @@ static inline TCGv_i32 *dec_alu_op_b(DisasContext *dc)
return &cpu_R[dc->rb];
}
static void dec_add(DisasContext *dc)
static TCGv_i32 reg_for_read(DisasContext *dc, int reg)
{
unsigned int k, c;
TCGv_i32 cf;
k = dc->opcode & 4;
c = dc->opcode & 2;
/* 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)));
if (c) {
/* c - Add carry into the result. */
tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cpu_msr_c);
}
if (likely(reg != 0)) {
return cpu_R[reg];
}
if (!dc->r0_set) {
if (dc->r0 == NULL) {
dc->r0 = tcg_temp_new_i32();
}
return;
tcg_gen_movi_i32(dc->r0, 0);
dc->r0_set = true;
}
return dc->r0;
}
static TCGv_i32 reg_for_write(DisasContext *dc, int reg)
{
if (likely(reg != 0)) {
return cpu_R[reg];
}
if (dc->r0 == NULL) {
dc->r0 = tcg_temp_new_i32();
}
return dc->r0;
}
static bool do_typea(DisasContext *dc, arg_typea *arg, bool side_effects,
void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32))
{
TCGv_i32 rd, ra, rb;
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) {
tcg_gen_mov_i32(cf, cpu_msr_c);
} else {
tcg_gen_movi_i32(cf, 0);
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;
}
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;
}
gen_helper_carry(cpu_msr_c, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf);
if (dc->rd) {
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);
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;
}
#define DO_TYPEA(NAME, SE, FN) \
static bool trans_##NAME(DisasContext *dc, arg_typea *a) \
{ return do_typea(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_TYPEBV(NAME, SE, FN) \
static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \
{ return do_typeb_val(dc, a, SE, FN); }
/* No input carry, but output carry. */
static void gen_add(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb)
{
TCGv_i32 zero = tcg_const_i32(0);
tcg_gen_add2_i32(out, cpu_msr_c, ina, zero, inb, zero);
tcg_temp_free_i32(zero);
}
/* 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();
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);
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(zero);
}
/* 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);
}
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)
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)
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 void dec_sub(DisasContext *dc)
@ -1488,7 +1597,6 @@ static struct decoder_info {
};
void (*dec)(DisasContext *dc);
} decinfo[] = {
{DEC_ADD, dec_add},
{DEC_SUB, dec_sub},
{DEC_AND, dec_and},
{DEC_XOR, dec_xor},
@ -1515,12 +1623,6 @@ static void old_decode(DisasContext *dc, uint32_t ir)
dc->ir = ir;
if (ir == 0) {
trap_illegal(dc, dc->cpu->cfg.opcode_0_illegal);
/* Don't decode nop/zero instructions any further. */
return;
}
/* bit 2 seems to indicate insn type. */
dc->type_b = ir & (1 << 29);
@ -1552,6 +1654,8 @@ static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs)
dc->cpustate_changed = 0;
dc->abort_at_next_insn = 0;
dc->ext_imm = dc->base.tb->cs_base;
dc->r0 = NULL;
dc->r0_set = false;
bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;
dc->base.max_insns = MIN(dc->base.max_insns, bound);
@ -1600,6 +1704,13 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs)
if (!decode(dc, ir)) {
old_decode(dc, ir);
}
if (dc->r0) {
tcg_temp_free_i32(dc->r0);
dc->r0 = NULL;
dc->r0_set = false;
}
if (dc->clear_imm && (dc->tb_flags & IMM_FLAG)) {
dc->tb_flags &= ~IMM_FLAG;
tcg_gen_discard_i32(cpu_imm);