diff mbox series

[3/3] target/hppa: Fix overflow computation for shladd

Message ID 20240326064405.320551-4-richard.henderson@linaro.org
State Superseded
Headers show
Series target/hppa: Fix overflow computation for shladd | expand

Commit Message

Richard Henderson March 26, 2024, 6:44 a.m. UTC
Overflow indicator should include the effect of the shift step.
We had previously left ??? comments about the issue.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
 target/hppa/translate.c | 85 ++++++++++++++++++++++++++++++++---------
 1 file changed, 66 insertions(+), 19 deletions(-)

Comments

Helge Deller March 26, 2024, 9:52 a.m. UTC | #1
On 3/26/24 07:44, Richard Henderson wrote:
> Overflow indicator should include the effect of the shift step.
> We had previously left ??? comments about the issue.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>


Tested-by: Helge Deller <deller@gmx.de>

Helge

> ---
>   target/hppa/translate.c | 85 ++++++++++++++++++++++++++++++++---------
>   1 file changed, 66 insertions(+), 19 deletions(-)
>
> diff --git a/target/hppa/translate.c b/target/hppa/translate.c
> index 9d31ef5764..0976372d16 100644
> --- a/target/hppa/translate.c
> +++ b/target/hppa/translate.c
> @@ -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,50 @@ 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;
> +            uint64_t mask = MAKE_64BIT_MASK(sign_bit - shift, shift);
> +
> +            /* 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_movcond_i64(TCG_COND_TSTNE, sv,
> +                                tmp, tcg_constant_i64(mask),
> +                                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 +1062,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 +1092,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 +1102,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 +2887,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 +2923,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 +3466,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);
diff mbox series

Patch

diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index 9d31ef5764..0976372d16 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -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,50 @@  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;
+            uint64_t mask = MAKE_64BIT_MASK(sign_bit - shift, shift);
+
+            /* 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_movcond_i64(TCG_COND_TSTNE, sv,
+                                tmp, tcg_constant_i64(mask),
+                                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 +1062,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 +1092,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 +1102,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 +2887,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 +2923,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 +3466,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);