Patchwork [037/147] target-s390: Send signals for divide

login
register
mail settings
Submitter Richard Henderson
Date Sept. 27, 2012, 11:15 p.m.
Message ID <1348787702-24115-1-git-send-email-rth@twiddle.net>
Download mbox | patch
Permalink /patch/187541/
State New
Headers show

Comments

Richard Henderson - Sept. 27, 2012, 11:15 p.m.
Signed-off-by: Richard Henderson <rth@twiddle.net>
---
 target-s390x/cpu.h         |  2 ++
 target-s390x/int_helper.c  | 51 ++++++++++++++++++++++++++++++++++++++++------
 target-s390x/misc_helper.c | 23 +++++++++++++++++++++
 3 files changed, 70 insertions(+), 6 deletions(-)

Patch

diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index 630198f..5b6df0e 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -940,5 +940,7 @@  uint32_t set_cc_nz_f64(float64 v);
 
 /* misc_helper.c */
 void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
+void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
+                                     uintptr_t retaddr);
 
 #endif
diff --git a/target-s390x/int_helper.c b/target-s390x/int_helper.c
index fe1cb87..f2f420c 100644
--- a/target-s390x/int_helper.c
+++ b/target-s390x/int_helper.c
@@ -38,22 +38,54 @@  uint64_t HELPER(mul128)(CPUS390XState *env, uint64_t v1, uint64_t v2)
 }
 
 /* 64/32 -> 32 signed division */
-int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b)
+int64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64)
 {
-    env->retxl = a % (int32_t)b;
-    return a / (int32_t)b;
+    int32_t ret, b = b64;
+    int64_t q;
+
+    if (b == 0) {
+        runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC());
+    }
+
+    ret = q = a / b;
+    env->retxl = a % b;
+
+    /* Catch non-representable quotient.  */
+    if (ret != q) {
+        runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC());
+    }
+
+    return ret;
 }
 
 /* 64/32 -> 32 unsigned division */
-uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b)
+uint64_t HELPER(divu32)(CPUS390XState *env, uint64_t a, uint64_t b64)
 {
-    env->retxl = a % (uint32_t)b;
-    return a / (uint32_t)b;
+    uint32_t ret, b = b64;
+    uint64_t q;
+
+    if (b == 0) {
+        runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC());
+    }
+
+    ret = q = a / b;
+    env->retxl = a % b;
+
+    /* Catch non-representable quotient.  */
+    if (ret != q) {
+        runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC());
+    }
+
+    return ret;
 }
 
 /* 64/64 -> 64 signed division */
 int64_t HELPER(divs64)(CPUS390XState *env, int64_t a, int64_t b)
 {
+    /* Catch divide by zero, and non-representable quotient (MIN / -1).  */
+    if (b == 0 || (b == -1 && a == (1ll << 63))) {
+        runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC());
+    }
     env->retxl = a % b;
     return a / b;
 }
@@ -63,6 +95,10 @@  uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al,
                         uint64_t b)
 {
     uint64_t ret;
+    /* Signal divide by zero.  */
+    if (b == 0) {
+        runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC());
+    }
     if (ah == 0) {
         /* 64 -> 64/64 case */
         env->retxl = al % b;
@@ -75,6 +111,9 @@  uint64_t HELPER(divu64)(CPUS390XState *env, uint64_t ah, uint64_t al,
         __uint128_t q = a / b;
         env->retxl = a % b;
         ret = q;
+        if (ret != q) {
+            runtime_exception(env, PGM_FIXPT_DIVIDE, GETPC());
+        }
 #else
         /* 32-bit hosts would need special wrapper functionality - just abort if
            we encounter such a case; it's very unlikely anyways. */
diff --git a/target-s390x/misc_helper.c b/target-s390x/misc_helper.c
index bd5fd20..aec222c 100644
--- a/target-s390x/misc_helper.c
+++ b/target-s390x/misc_helper.c
@@ -42,6 +42,29 @@ 
 #define HELPER_LOG(x...)
 #endif
 
+/* Raise an exception dynamically from a helper function.  */
+void QEMU_NORETURN runtime_exception(CPUS390XState *env, int excp,
+                                     uintptr_t retaddr)
+{
+    TranslationBlock *tb;
+    int t;
+
+    env->exception_index = EXCP_PGM;
+    env->int_pgm_code = excp;
+
+    /* Use the (ultimate) callers address to find the insn that trapped.  */
+    tb = tb_find_pc(retaddr);
+    assert(tb != NULL);
+    cpu_restore_state(tb, env, retaddr);
+
+    /* Advance past the insn.  */
+    t = cpu_ldub_code(env, env->psw.addr);
+    env->int_pgm_ilen = t = get_ilen(t);
+    env->psw.addr += 2 * t;
+
+    cpu_loop_exit(env);
+}
+
 /* Raise an exception statically from a TB.  */
 void HELPER(exception)(CPUS390XState *env, uint32_t excp)
 {