[aarch64-port-dev ] simulator: multiplication/divsion for LONG/INT_MIN and -1

D.Sturm D.Sturm42 at gmail.com
Sat Apr 26 18:03:22 UTC 2014


The simulator does not correctly handle multiplications/divisions in the
case where we have the smallest negative value and -1. This is undefined in
C/C++ anyhow, but works correctly for me in the case of multiplication
(INT_MIN * -1 == INT_MIN), and is actually guaranteed to work for the int
division due to the way the code is written but causes an arithmetic
exception for LONG_MIN / -1 under Linux Mint 16 gcc 4.8.1.

That exception is caught by the signal handler in the JVM and then
correctly handled there in most situations - I ran into some throwing
assertion when it was simulating graal code but haven't looked further into
it.

The following patch should take care of this. The smulh instruction uses
inline assembly but I think x86 does the right thing(TM) there.

-- Daniel

# HG changeset patch
# User Daniel Sturm <d.sturm42 at gmail.com>
# Date 1398525661 -7200
#      Sat Apr 26 17:21:01 2014 +0200
# Node ID 31460830078ecdb590d602ccfa12f93c8e022ba9
# Parent  d70a2050eb423046283bc8f1280fd9d9e1fabc21
fixed signed multiply and division in case of MIN_VAL and -1 as operands

diff -r d70a2050eb42 -r 31460830078e simulator.cpp
--- a/simulator.cpp Fri Jan 03 13:42:36 2014 +0000
+++ b/simulator.cpp Sat Apr 26 17:21:01 2014 +0200
@@ -44,6 +44,7 @@
 #include <syscall.h>
 #include <signal.h>
 #include <assert.h>
+#include <limits>
 #define DEBUG
 // #include <shared_ptr.h>

@@ -7073,6 +7074,16 @@

 // multiply

+template <class T>
+static T mul(T a, T b) {
+  if (a == -1 || b == -1) {
+    if (a == std::numeric_limits<T>::min() || b ==
std::numeric_limits<T>::min()) {
+      return std::numeric_limits<T>::min();
+    }
+  }
+  return a * b;
+}
+
 // 32 bit multiply and add
 void AArch64Simulator::madd32()
 {
@@ -7080,7 +7091,7 @@
   // instr[14,10] = ra : may not be SP
   // instr[9,5] = rn : may not be SP
   // instr[4,0] = rd : may not be SP
-  xreg(0, NO_SP) = wreg(10, NO_SP) + wreg(5, NO_SP) * wreg(16, NO_SP);
+  xreg(0, NO_SP) = wreg(10, NO_SP) + mul(wreg(5, NO_SP), wreg(16, NO_SP));
 }

 // 64 bit multiply and add
@@ -7090,7 +7101,7 @@
   // instr[14,10] = ra : may not be SP
   // instr[9,5] = rn : may not be SP
   // instr[4,0] = rd : may not be SP
-  xreg(0, NO_SP) = xreg(10, NO_SP) + xreg(5, NO_SP) * xreg(16, NO_SP);
+  xreg(0, NO_SP) = xreg(10, NO_SP) + mul(xreg(5, NO_SP), xreg(16, NO_SP));
 }

 // 32 bit multiply and sub
@@ -7100,7 +7111,7 @@
   // instr[14,10] = ra : may not be SP
   // instr[9,5] = rn : may not be SP
   // instr[4,0] = rd : may not be SP
-  xreg(0, NO_SP) = wreg(10, NO_SP) - wreg(5, NO_SP) * wreg(16, NO_SP);
+  xreg(0, NO_SP) = wreg(10, NO_SP) - mul(wreg(5, NO_SP), wreg(16, NO_SP));
 }

 // 64 bit multiply and sub
@@ -7110,7 +7121,7 @@
   // instr[14,10] = ra : may not be SP
   // instr[9,5] = rn : may not be SP
   // instr[4,0] = rd : may not be SP
-  xreg(0, NO_SP) = xreg(10, NO_SP) - xreg(5, NO_SP) * xreg(16, NO_SP);
+  xreg(0, NO_SP) = xreg(10, NO_SP) - mul(xreg(5, NO_SP), xreg(16, NO_SP));
 }

 // signed multiply add long -- source, source2 : 32 bit, source3 : 64 bit
@@ -7122,7 +7133,7 @@
   // instr[4,0] = rd : may not be SP
   // n.b. we need to multiply the signed 32 bit values in rn, rm to
   // obtain a 64 bit product
-  xregs(0, NO_SP) = xregs(10, NO_SP) + ((int64_t)wregs(5, NO_SP)) *
((int64_t)wregs(16, NO_SP));
+  xregs(0, NO_SP) = xregs(10, NO_SP) + mul((int64_t)wregs(5, NO_SP),
(int64_t)wregs(16, NO_SP));
 }

 // signed multiply sub long -- source, source2 : 32 bit, source3 : 64 bit
@@ -7134,7 +7145,7 @@
   // instr[4,0] = rd : may not be SP
   // n.b. we need to multiply the signed 32 bit values in rn, rm to
   // obtain a 64 bit product
-  xregs(0, NO_SP) = xregs(10, NO_SP) - ((int64_t)wregs(5, NO_SP)) *
((int64_t)wregs(16, NO_SP));
+  xregs(0, NO_SP) = xregs(10, NO_SP) - mul((int64_t)wregs(5, NO_SP),
 (int64_t)wregs(16, NO_SP));
 }

 // signed multiply high, source, source2 : 64 bit, dest <-- high 64-bit of
result
@@ -7225,8 +7236,13 @@
   // instr[9,5] = rn : may not be SP
   // instr[4,0] = rd : may not be SP
   // TODO : check that this rounds towards zero as required
+  int64_t dividend = xregs(5, NO_SP);
   int64_t divisor = xregs(16, NO_SP);
-  xregs(0, NO_SP) = (divisor ? (xregs(5, NO_SP) / divisor) : 0);
+  if (divisor == -1 && dividend == std::numeric_limits<int64_t>::min()) {
+    xregs(0, NO_SP) = std::numeric_limits<int64_t>::min();
+  } else {
+    xregs(0, NO_SP) = (divisor ? (dividend / divisor) : 0);
+  }
 }

 // 32 bit unsigned divide


More information about the aarch64-port-dev mailing list