target-s390: Split out disas_jcc

Lots of duplicated code replaced with a couple of tables.  We no longer
attempt to manually invert the logic operation: the comments now match
the code.  In the fully general test, constant propagate (1 << (3 - cc))
into (8 >> cc).

The new function will be usable by non-branch insns as well.

Signed-off-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Richard Henderson 2012-09-05 13:31:01 -07:00
parent ad044d09de
commit 3fde06f5fb

View File

@ -58,6 +58,18 @@ struct DisasContext {
int is_jmp;
};
/* Information carried about a condition to be evaluated. */
typedef struct {
TCGCond cond:8;
bool is_64;
bool g1;
bool g2;
union {
struct { TCGv_i64 a, b; } s64;
struct { TCGv_i32 a, b; } s32;
} u;
} DisasCompare;
#define DISAS_EXCP 4
static void gen_op_calc_cc(DisasContext *s);
@ -840,357 +852,281 @@ static inline void account_noninline_branch(DisasContext *s, int cc_op)
#endif
}
static inline void account_inline_branch(DisasContext *s)
static inline void account_inline_branch(DisasContext *s, int cc_op)
{
#ifdef DEBUG_INLINE_BRANCHES
inline_branch_hit[s->cc_op]++;
inline_branch_hit[cc_op]++;
#endif
}
/* Table of mask values to comparison codes, given a comparison as input.
For a true comparison CC=3 will never be set, but we treat this
conservatively for possible use when CC=3 indicates overflow. */
static const TCGCond ltgt_cond[16] = {
TCG_COND_NEVER, TCG_COND_NEVER, /* | | | x */
TCG_COND_GT, TCG_COND_NEVER, /* | | GT | x */
TCG_COND_LT, TCG_COND_NEVER, /* | LT | | x */
TCG_COND_NE, TCG_COND_NEVER, /* | LT | GT | x */
TCG_COND_EQ, TCG_COND_NEVER, /* EQ | | | x */
TCG_COND_GE, TCG_COND_NEVER, /* EQ | | GT | x */
TCG_COND_LE, TCG_COND_NEVER, /* EQ | LT | | x */
TCG_COND_ALWAYS, TCG_COND_ALWAYS, /* EQ | LT | GT | x */
};
/* Table of mask values to comparison codes, given a logic op as input.
For such, only CC=0 and CC=1 should be possible. */
static const TCGCond nz_cond[16] = {
/* | | x | x */
TCG_COND_NEVER, TCG_COND_NEVER, TCG_COND_NEVER, TCG_COND_NEVER,
/* | NE | x | x */
TCG_COND_NE, TCG_COND_NE, TCG_COND_NE, TCG_COND_NE,
/* EQ | | x | x */
TCG_COND_EQ, TCG_COND_EQ, TCG_COND_EQ, TCG_COND_EQ,
/* EQ | NE | x | x */
TCG_COND_ALWAYS, TCG_COND_ALWAYS, TCG_COND_ALWAYS, TCG_COND_ALWAYS,
};
/* Interpret MASK in terms of S->CC_OP, and fill in C with all the
details required to generate a TCG comparison. */
static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask)
{
TCGCond cond;
enum cc_op old_cc_op = s->cc_op;
if (mask == 15 || mask == 0) {
c->cond = (mask ? TCG_COND_ALWAYS : TCG_COND_NEVER);
c->u.s32.a = cc_op;
c->u.s32.b = cc_op;
c->g1 = c->g2 = true;
c->is_64 = false;
return;
}
/* Find the TCG condition for the mask + cc op. */
switch (old_cc_op) {
case CC_OP_LTGT0_32:
case CC_OP_LTGT0_64:
case CC_OP_LTGT_32:
case CC_OP_LTGT_64:
cond = ltgt_cond[mask];
if (cond == TCG_COND_NEVER) {
goto do_dynamic;
}
account_inline_branch(s, old_cc_op);
break;
case CC_OP_LTUGTU_32:
case CC_OP_LTUGTU_64:
cond = tcg_unsigned_cond(ltgt_cond[mask]);
if (cond == TCG_COND_NEVER) {
goto do_dynamic;
}
account_inline_branch(s, old_cc_op);
break;
case CC_OP_NZ:
cond = nz_cond[mask];
if (cond == TCG_COND_NEVER) {
goto do_dynamic;
}
account_inline_branch(s, old_cc_op);
break;
case CC_OP_TM_32:
case CC_OP_TM_64:
switch (mask) {
case 8:
cond = TCG_COND_EQ;
break;
case 4 | 2 | 1:
cond = TCG_COND_NE;
break;
default:
goto do_dynamic;
}
account_inline_branch(s, old_cc_op);
break;
case CC_OP_ICM:
switch (mask) {
case 8:
cond = TCG_COND_EQ;
break;
case 4 | 2 | 1:
case 4 | 2:
cond = TCG_COND_NE;
break;
default:
goto do_dynamic;
}
account_inline_branch(s, old_cc_op);
break;
default:
do_dynamic:
/* Calculate cc value. */
gen_op_calc_cc(s);
/* FALLTHRU */
case CC_OP_STATIC:
/* Jump based on CC. We'll load up the real cond below;
the assignment here merely avoids a compiler warning. */
account_noninline_branch(s, old_cc_op);
old_cc_op = CC_OP_STATIC;
cond = TCG_COND_NEVER;
break;
}
/* Load up the arguments of the comparison. */
c->is_64 = true;
c->g1 = c->g2 = false;
switch (old_cc_op) {
case CC_OP_LTGT0_32:
c->is_64 = false;
c->u.s32.a = tcg_temp_new_i32();
tcg_gen_trunc_i64_i32(c->u.s32.a, cc_dst);
c->u.s32.b = tcg_const_i32(0);
break;
case CC_OP_LTGT_32:
case CC_OP_LTUGTU_32:
c->is_64 = false;
c->u.s32.a = tcg_temp_new_i32();
tcg_gen_trunc_i64_i32(c->u.s32.a, cc_src);
c->u.s32.b = tcg_temp_new_i32();
tcg_gen_trunc_i64_i32(c->u.s32.b, cc_dst);
break;
case CC_OP_LTGT0_64:
case CC_OP_NZ:
case CC_OP_ICM:
c->u.s64.a = cc_dst;
c->u.s64.b = tcg_const_i64(0);
c->g1 = true;
break;
case CC_OP_LTGT_64:
case CC_OP_LTUGTU_64:
c->u.s64.a = cc_src;
c->u.s64.b = cc_dst;
c->g1 = c->g2 = true;
break;
case CC_OP_TM_32:
case CC_OP_TM_64:
c->u.s64.a = tcg_temp_new_i64();
c->u.s64.b = tcg_const_i64(0);
tcg_gen_and_i64(c->u.s64.a, cc_src, cc_dst);
break;
case CC_OP_STATIC:
c->is_64 = false;
c->u.s32.a = cc_op;
c->g1 = true;
switch (mask) {
case 0x8 | 0x4 | 0x2: /* cc != 3 */
cond = TCG_COND_NE;
c->u.s32.b = tcg_const_i32(3);
break;
case 0x8 | 0x4 | 0x1: /* cc != 2 */
cond = TCG_COND_NE;
c->u.s32.b = tcg_const_i32(2);
break;
case 0x8 | 0x2 | 0x1: /* cc != 1 */
cond = TCG_COND_NE;
c->u.s32.b = tcg_const_i32(1);
break;
case 0x8 | 0x2: /* cc == 0 || cc == 2 => (cc & 1) == 0 */
cond = TCG_COND_EQ;
c->g1 = false;
c->u.s32.a = tcg_temp_new_i32();
c->u.s32.b = tcg_const_i32(0);
tcg_gen_andi_i32(c->u.s32.a, cc_op, 1);
break;
case 0x8 | 0x4: /* cc < 2 */
cond = TCG_COND_LTU;
c->u.s32.b = tcg_const_i32(2);
break;
case 0x8: /* cc == 0 */
cond = TCG_COND_EQ;
c->u.s32.b = tcg_const_i32(0);
break;
case 0x4 | 0x2 | 0x1: /* cc != 0 */
cond = TCG_COND_NE;
c->u.s32.b = tcg_const_i32(0);
break;
case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */
cond = TCG_COND_NE;
c->g1 = false;
c->u.s32.a = tcg_temp_new_i32();
c->u.s32.b = tcg_const_i32(0);
tcg_gen_andi_i32(c->u.s32.a, cc_op, 1);
break;
case 0x4: /* cc == 1 */
cond = TCG_COND_EQ;
c->u.s32.b = tcg_const_i32(1);
break;
case 0x2 | 0x1: /* cc > 1 */
cond = TCG_COND_GTU;
c->u.s32.b = tcg_const_i32(1);
break;
case 0x2: /* cc == 2 */
cond = TCG_COND_EQ;
c->u.s32.b = tcg_const_i32(2);
break;
case 0x1: /* cc == 3 */
cond = TCG_COND_EQ;
c->u.s32.b = tcg_const_i32(3);
break;
default:
/* CC is masked by something else: (8 >> cc) & mask. */
cond = TCG_COND_NE;
c->g1 = false;
c->u.s32.a = tcg_const_i32(8);
c->u.s32.b = tcg_const_i32(0);
tcg_gen_shr_i32(c->u.s32.a, c->u.s32.a, cc_op);
tcg_gen_andi_i32(c->u.s32.a, c->u.s32.a, mask);
break;
}
break;
default:
abort();
}
c->cond = cond;
}
static void free_compare(DisasCompare *c)
{
if (!c->g1) {
if (c->is_64) {
tcg_temp_free_i64(c->u.s64.a);
} else {
tcg_temp_free_i32(c->u.s32.a);
}
}
if (!c->g2) {
if (c->is_64) {
tcg_temp_free_i64(c->u.s64.b);
} else {
tcg_temp_free_i32(c->u.s32.b);
}
}
}
static void gen_jcc(DisasContext *s, uint32_t mask, int skip)
{
TCGv_i32 tmp, tmp2, r;
TCGv_i64 tmp64;
int old_cc_op;
DisasCompare c;
TCGCond cond;
switch (s->cc_op) {
case CC_OP_LTGT0_32:
tmp = tcg_temp_new_i32();
tcg_gen_trunc_i64_i32(tmp, cc_dst);
switch (mask) {
case 0x8 | 0x4: /* dst <= 0 */
tcg_gen_brcondi_i32(TCG_COND_GT, tmp, 0, skip);
break;
case 0x8 | 0x2: /* dst >= 0 */
tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, skip);
break;
case 0x8: /* dst == 0 */
tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip);
break;
case 0x7: /* dst != 0 */
case 0x6: /* dst != 0 */
tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip);
break;
case 0x4: /* dst < 0 */
tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, skip);
break;
case 0x2: /* dst > 0 */
tcg_gen_brcondi_i32(TCG_COND_LE, tmp, 0, skip);
break;
default:
tcg_temp_free_i32(tmp);
goto do_dynamic;
}
account_inline_branch(s);
tcg_temp_free_i32(tmp);
break;
case CC_OP_LTGT0_64:
switch (mask) {
case 0x8 | 0x4: /* dst <= 0 */
tcg_gen_brcondi_i64(TCG_COND_GT, cc_dst, 0, skip);
break;
case 0x8 | 0x2: /* dst >= 0 */
tcg_gen_brcondi_i64(TCG_COND_LT, cc_dst, 0, skip);
break;
case 0x8: /* dst == 0 */
tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip);
break;
case 0x7: /* dst != 0 */
case 0x6: /* dst != 0 */
tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip);
break;
case 0x4: /* dst < 0 */
tcg_gen_brcondi_i64(TCG_COND_GE, cc_dst, 0, skip);
break;
case 0x2: /* dst > 0 */
tcg_gen_brcondi_i64(TCG_COND_LE, cc_dst, 0, skip);
break;
default:
goto do_dynamic;
}
account_inline_branch(s);
break;
case CC_OP_LTGT_32:
tmp = tcg_temp_new_i32();
tmp2 = tcg_temp_new_i32();
tcg_gen_trunc_i64_i32(tmp, cc_src);
tcg_gen_trunc_i64_i32(tmp2, cc_dst);
switch (mask) {
case 0x8 | 0x4: /* src <= dst */
tcg_gen_brcond_i32(TCG_COND_GT, tmp, tmp2, skip);
break;
case 0x8 | 0x2: /* src >= dst */
tcg_gen_brcond_i32(TCG_COND_LT, tmp, tmp2, skip);
break;
case 0x8: /* src == dst */
tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip);
break;
case 0x7: /* src != dst */
case 0x6: /* src != dst */
tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip);
break;
case 0x4: /* src < dst */
tcg_gen_brcond_i32(TCG_COND_GE, tmp, tmp2, skip);
break;
case 0x2: /* src > dst */
tcg_gen_brcond_i32(TCG_COND_LE, tmp, tmp2, skip);
break;
default:
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
goto do_dynamic;
}
account_inline_branch(s);
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
break;
case CC_OP_LTGT_64:
switch (mask) {
case 0x8 | 0x4: /* src <= dst */
tcg_gen_brcond_i64(TCG_COND_GT, cc_src, cc_dst, skip);
break;
case 0x8 | 0x2: /* src >= dst */
tcg_gen_brcond_i64(TCG_COND_LT, cc_src, cc_dst, skip);
break;
case 0x8: /* src == dst */
tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip);
break;
case 0x7: /* src != dst */
case 0x6: /* src != dst */
tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip);
break;
case 0x4: /* src < dst */
tcg_gen_brcond_i64(TCG_COND_GE, cc_src, cc_dst, skip);
break;
case 0x2: /* src > dst */
tcg_gen_brcond_i64(TCG_COND_LE, cc_src, cc_dst, skip);
break;
default:
goto do_dynamic;
}
account_inline_branch(s);
break;
case CC_OP_LTUGTU_32:
tmp = tcg_temp_new_i32();
tmp2 = tcg_temp_new_i32();
tcg_gen_trunc_i64_i32(tmp, cc_src);
tcg_gen_trunc_i64_i32(tmp2, cc_dst);
switch (mask) {
case 0x8 | 0x4: /* src <= dst */
tcg_gen_brcond_i32(TCG_COND_GTU, tmp, tmp2, skip);
break;
case 0x8 | 0x2: /* src >= dst */
tcg_gen_brcond_i32(TCG_COND_LTU, tmp, tmp2, skip);
break;
case 0x8: /* src == dst */
tcg_gen_brcond_i32(TCG_COND_NE, tmp, tmp2, skip);
break;
case 0x7: /* src != dst */
case 0x6: /* src != dst */
tcg_gen_brcond_i32(TCG_COND_EQ, tmp, tmp2, skip);
break;
case 0x4: /* src < dst */
tcg_gen_brcond_i32(TCG_COND_GEU, tmp, tmp2, skip);
break;
case 0x2: /* src > dst */
tcg_gen_brcond_i32(TCG_COND_LEU, tmp, tmp2, skip);
break;
default:
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
goto do_dynamic;
}
account_inline_branch(s);
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
break;
case CC_OP_LTUGTU_64:
switch (mask) {
case 0x8 | 0x4: /* src <= dst */
tcg_gen_brcond_i64(TCG_COND_GTU, cc_src, cc_dst, skip);
break;
case 0x8 | 0x2: /* src >= dst */
tcg_gen_brcond_i64(TCG_COND_LTU, cc_src, cc_dst, skip);
break;
case 0x8: /* src == dst */
tcg_gen_brcond_i64(TCG_COND_NE, cc_src, cc_dst, skip);
break;
case 0x7: /* src != dst */
case 0x6: /* src != dst */
tcg_gen_brcond_i64(TCG_COND_EQ, cc_src, cc_dst, skip);
break;
case 0x4: /* src < dst */
tcg_gen_brcond_i64(TCG_COND_GEU, cc_src, cc_dst, skip);
break;
case 0x2: /* src > dst */
tcg_gen_brcond_i64(TCG_COND_LEU, cc_src, cc_dst, skip);
break;
default:
goto do_dynamic;
}
account_inline_branch(s);
break;
case CC_OP_NZ:
switch (mask) {
/* dst == 0 || dst != 0 */
case 0x8 | 0x4:
case 0x8 | 0x4 | 0x2:
case 0x8 | 0x4 | 0x2 | 0x1:
case 0x8 | 0x4 | 0x1:
break;
/* dst == 0 */
case 0x8:
case 0x8 | 0x2:
case 0x8 | 0x2 | 0x1:
case 0x8 | 0x1:
tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip);
break;
/* dst != 0 */
case 0x4:
case 0x4 | 0x2:
case 0x4 | 0x2 | 0x1:
case 0x4 | 0x1:
tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip);
break;
default:
goto do_dynamic;
}
account_inline_branch(s);
break;
case CC_OP_TM_32:
tmp = tcg_temp_new_i32();
tmp2 = tcg_temp_new_i32();
disas_jcc(s, &c, mask);
cond = tcg_invert_cond(c.cond);
tcg_gen_trunc_i64_i32(tmp, cc_src);
tcg_gen_trunc_i64_i32(tmp2, cc_dst);
tcg_gen_and_i32(tmp, tmp, tmp2);
switch (mask) {
case 0x8: /* val & mask == 0 */
tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip);
break;
case 0x4 | 0x2 | 0x1: /* val & mask != 0 */
tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip);
break;
default:
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
goto do_dynamic;
}
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
account_inline_branch(s);
break;
case CC_OP_TM_64:
tmp64 = tcg_temp_new_i64();
tcg_gen_and_i64(tmp64, cc_src, cc_dst);
switch (mask) {
case 0x8: /* val & mask == 0 */
tcg_gen_brcondi_i64(TCG_COND_NE, tmp64, 0, skip);
break;
case 0x4 | 0x2 | 0x1: /* val & mask != 0 */
tcg_gen_brcondi_i64(TCG_COND_EQ, tmp64, 0, skip);
break;
default:
tcg_temp_free_i64(tmp64);
goto do_dynamic;
}
tcg_temp_free_i64(tmp64);
account_inline_branch(s);
break;
case CC_OP_ICM:
switch (mask) {
case 0x8: /* val == 0 */
tcg_gen_brcondi_i64(TCG_COND_NE, cc_dst, 0, skip);
break;
case 0x4 | 0x2 | 0x1: /* val != 0 */
case 0x4 | 0x2: /* val != 0 */
tcg_gen_brcondi_i64(TCG_COND_EQ, cc_dst, 0, skip);
break;
default:
goto do_dynamic;
}
account_inline_branch(s);
break;
case CC_OP_STATIC:
old_cc_op = s->cc_op;
goto do_dynamic_nocccalc;
case CC_OP_DYNAMIC:
default:
do_dynamic:
old_cc_op = s->cc_op;
/* calculate cc value */
gen_op_calc_cc(s);
do_dynamic_nocccalc:
/* jump based on cc */
account_noninline_branch(s, old_cc_op);
switch (mask) {
case 0x8 | 0x4 | 0x2 | 0x1:
/* always true */
break;
case 0x8 | 0x4 | 0x2: /* cc != 3 */
tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 3, skip);
break;
case 0x8 | 0x4 | 0x1: /* cc != 2 */
tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 2, skip);
break;
case 0x8 | 0x2 | 0x1: /* cc != 1 */
tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 1, skip);
break;
case 0x8 | 0x2: /* cc == 0 || cc == 2 */
tmp = tcg_temp_new_i32();
tcg_gen_andi_i32(tmp, cc_op, 1);
tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, skip);
tcg_temp_free_i32(tmp);
break;
case 0x8 | 0x4: /* cc < 2 */
tcg_gen_brcondi_i32(TCG_COND_GEU, cc_op, 2, skip);
break;
case 0x8: /* cc == 0 */
tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 0, skip);
break;
case 0x4 | 0x2 | 0x1: /* cc != 0 */
tcg_gen_brcondi_i32(TCG_COND_EQ, cc_op, 0, skip);
break;
case 0x4 | 0x1: /* cc == 1 || cc == 3 */
tmp = tcg_temp_new_i32();
tcg_gen_andi_i32(tmp, cc_op, 1);
tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, skip);
tcg_temp_free_i32(tmp);
break;
case 0x4: /* cc == 1 */
tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 1, skip);
break;
case 0x2 | 0x1: /* cc > 1 */
tcg_gen_brcondi_i32(TCG_COND_LEU, cc_op, 1, skip);
break;
case 0x2: /* cc == 2 */
tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 2, skip);
break;
case 0x1: /* cc == 3 */
tcg_gen_brcondi_i32(TCG_COND_NE, cc_op, 3, skip);
break;
default: /* cc is masked by something else */
tmp = tcg_const_i32(3);
/* 3 - cc */
tcg_gen_sub_i32(tmp, tmp, cc_op);
tmp2 = tcg_const_i32(1);
/* 1 << (3 - cc) */
tcg_gen_shl_i32(tmp2, tmp2, tmp);
r = tcg_const_i32(mask);
/* mask & (1 << (3 - cc)) */
tcg_gen_and_i32(r, r, tmp2);
tcg_temp_free_i32(tmp);
tcg_temp_free_i32(tmp2);
tcg_gen_brcondi_i32(TCG_COND_EQ, r, 0, skip);
tcg_temp_free_i32(r);
break;
}
break;
if (c.is_64) {
tcg_gen_brcond_i64(cond, c.u.s64.a, c.u.s64.b, skip);
} else {
tcg_gen_brcond_i32(cond, c.u.s32.a, c.u.s32.b, skip);
}
free_compare(&c);
}
static void gen_bcr(DisasContext *s, uint32_t mask, TCGv_i64 target,