target/hppa: Fix overflow computation for shladd
Overflow indicator should include the effect of the shift step. We had previously left ??? comments about the issue. Tested-by: Helge Deller <deller@gmx.de> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
fe2d066a9e
commit
f8f5986edc
@ -994,7 +994,8 @@ static TCGv_i64 get_psw_carry(DisasContext *ctx, bool d)
|
||||
|
||||
/* Compute signed overflow for addition. */
|
||||
static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res,
|
||||
TCGv_i64 in1, TCGv_i64 in2)
|
||||
TCGv_i64 in1, TCGv_i64 in2,
|
||||
TCGv_i64 orig_in1, int shift, bool d)
|
||||
{
|
||||
TCGv_i64 sv = tcg_temp_new_i64();
|
||||
TCGv_i64 tmp = tcg_temp_new_i64();
|
||||
@ -1003,9 +1004,49 @@ static TCGv_i64 do_add_sv(DisasContext *ctx, TCGv_i64 res,
|
||||
tcg_gen_xor_i64(tmp, in1, in2);
|
||||
tcg_gen_andc_i64(sv, sv, tmp);
|
||||
|
||||
switch (shift) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
/* Shift left by one and compare the sign. */
|
||||
tcg_gen_add_i64(tmp, orig_in1, orig_in1);
|
||||
tcg_gen_xor_i64(tmp, tmp, orig_in1);
|
||||
/* Incorporate into the overflow. */
|
||||
tcg_gen_or_i64(sv, sv, tmp);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
int sign_bit = d ? 63 : 31;
|
||||
|
||||
/* Compare the sign against all lower bits. */
|
||||
tcg_gen_sextract_i64(tmp, orig_in1, sign_bit, 1);
|
||||
tcg_gen_xor_i64(tmp, tmp, orig_in1);
|
||||
/*
|
||||
* If one of the bits shifting into or through the sign
|
||||
* differs, then we have overflow.
|
||||
*/
|
||||
tcg_gen_extract_i64(tmp, tmp, sign_bit - shift, shift);
|
||||
tcg_gen_movcond_i64(TCG_COND_NE, sv, tmp, ctx->zero,
|
||||
tcg_constant_i64(-1), sv);
|
||||
}
|
||||
}
|
||||
return sv;
|
||||
}
|
||||
|
||||
/* Compute unsigned overflow for addition. */
|
||||
static TCGv_i64 do_add_uv(DisasContext *ctx, TCGv_i64 cb, TCGv_i64 cb_msb,
|
||||
TCGv_i64 in1, int shift, bool d)
|
||||
{
|
||||
if (shift == 0) {
|
||||
return get_carry(ctx, d, cb, cb_msb);
|
||||
} else {
|
||||
TCGv_i64 tmp = tcg_temp_new_i64();
|
||||
tcg_gen_extract_i64(tmp, in1, (d ? 63 : 31) - shift, shift);
|
||||
tcg_gen_or_i64(tmp, tmp, get_carry(ctx, d, cb, cb_msb));
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute signed overflow for subtraction. */
|
||||
static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res,
|
||||
TCGv_i64 in1, TCGv_i64 in2)
|
||||
@ -1020,19 +1061,19 @@ static TCGv_i64 do_sub_sv(DisasContext *ctx, TCGv_i64 res,
|
||||
return sv;
|
||||
}
|
||||
|
||||
static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
|
||||
static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1,
|
||||
TCGv_i64 in2, unsigned shift, bool is_l,
|
||||
bool is_tsv, bool is_tc, bool is_c, unsigned cf, bool d)
|
||||
{
|
||||
TCGv_i64 dest, cb, cb_msb, cb_cond, sv, tmp;
|
||||
TCGv_i64 dest, cb, cb_msb, in1, uv, sv, tmp;
|
||||
unsigned c = cf >> 1;
|
||||
DisasCond cond;
|
||||
|
||||
dest = tcg_temp_new_i64();
|
||||
cb = NULL;
|
||||
cb_msb = NULL;
|
||||
cb_cond = NULL;
|
||||
|
||||
in1 = orig_in1;
|
||||
if (shift) {
|
||||
tmp = tcg_temp_new_i64();
|
||||
tcg_gen_shli_i64(tmp, in1, shift);
|
||||
@ -1050,9 +1091,6 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
|
||||
}
|
||||
tcg_gen_xor_i64(cb, in1, in2);
|
||||
tcg_gen_xor_i64(cb, cb, dest);
|
||||
if (cond_need_cb(c)) {
|
||||
cb_cond = get_carry(ctx, d, cb, cb_msb);
|
||||
}
|
||||
} else {
|
||||
tcg_gen_add_i64(dest, in1, in2);
|
||||
if (is_c) {
|
||||
@ -1063,18 +1101,23 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
|
||||
/* Compute signed overflow if required. */
|
||||
sv = NULL;
|
||||
if (is_tsv || cond_need_sv(c)) {
|
||||
sv = do_add_sv(ctx, dest, in1, in2);
|
||||
sv = do_add_sv(ctx, dest, in1, in2, orig_in1, shift, d);
|
||||
if (is_tsv) {
|
||||
if (!d) {
|
||||
tcg_gen_ext32s_i64(sv, sv);
|
||||
}
|
||||
/* ??? Need to include overflow from shift. */
|
||||
gen_helper_tsv(tcg_env, sv);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute unsigned overflow if required. */
|
||||
uv = NULL;
|
||||
if (cond_need_cb(c)) {
|
||||
uv = do_add_uv(ctx, cb, cb_msb, orig_in1, shift, d);
|
||||
}
|
||||
|
||||
/* Emit any conditional trap before any writeback. */
|
||||
cond = do_cond(ctx, cf, d, dest, cb_cond, sv);
|
||||
cond = do_cond(ctx, cf, d, dest, uv, sv);
|
||||
if (is_tc) {
|
||||
tmp = tcg_temp_new_i64();
|
||||
tcg_gen_setcond_i64(cond.c, tmp, cond.a0, cond.a1);
|
||||
@ -2843,7 +2886,6 @@ static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf_d *a)
|
||||
static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a)
|
||||
{
|
||||
TCGv_i64 dest, add1, add2, addc, in1, in2;
|
||||
TCGv_i64 cout;
|
||||
|
||||
nullify_over(ctx);
|
||||
|
||||
@ -2880,19 +2922,23 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a)
|
||||
tcg_gen_xor_i64(cpu_psw_cb, add1, add2);
|
||||
tcg_gen_xor_i64(cpu_psw_cb, cpu_psw_cb, dest);
|
||||
|
||||
/* Write back PSW[V] for the division step. */
|
||||
cout = get_psw_carry(ctx, false);
|
||||
tcg_gen_neg_i64(cpu_psw_v, cout);
|
||||
/*
|
||||
* Write back PSW[V] for the division step.
|
||||
* Shift cb{8} from where it lives in bit 32 to bit 31,
|
||||
* so that it overlaps r2{32} in bit 31.
|
||||
*/
|
||||
tcg_gen_shri_i64(cpu_psw_v, cpu_psw_cb, 1);
|
||||
tcg_gen_xor_i64(cpu_psw_v, cpu_psw_v, in2);
|
||||
|
||||
/* Install the new nullification. */
|
||||
if (a->cf) {
|
||||
TCGv_i64 sv = NULL;
|
||||
TCGv_i64 sv = NULL, uv = NULL;
|
||||
if (cond_need_sv(a->cf >> 1)) {
|
||||
/* ??? The lshift is supposed to contribute to overflow. */
|
||||
sv = do_add_sv(ctx, dest, add1, add2);
|
||||
sv = do_add_sv(ctx, dest, add1, add2, in1, 1, false);
|
||||
} else if (cond_need_cb(a->cf >> 1)) {
|
||||
uv = do_add_uv(ctx, cpu_psw_cb, NULL, in1, 1, false);
|
||||
}
|
||||
ctx->null_cond = do_cond(ctx, a->cf, false, dest, cout, sv);
|
||||
ctx->null_cond = do_cond(ctx, a->cf, false, dest, uv, sv);
|
||||
}
|
||||
|
||||
return nullify_end(ctx);
|
||||
@ -3419,7 +3465,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_i64 in1,
|
||||
tcg_gen_add_i64(dest, in1, in2);
|
||||
}
|
||||
if (cond_need_sv(c)) {
|
||||
sv = do_add_sv(ctx, dest, in1, in2);
|
||||
sv = do_add_sv(ctx, dest, in1, in2, in1, 0, d);
|
||||
}
|
||||
|
||||
cond = do_cond(ctx, c * 2 + f, d, dest, cb_cond, sv);
|
||||
|
Loading…
Reference in New Issue
Block a user