6997311: SIGFPE in new long division asm code
authorkvn
Sat, 06 Nov 2010 18:52:07 -0700
changeset 7121 69928525c55c
parent 7120 2d1dbf14fe62
child 7122 23b82ce71b4e
6997311: SIGFPE in new long division asm code Summary: use unsigned DIV instruction Reviewed-by: never
hotspot/src/cpu/x86/vm/assembler_x86.cpp
hotspot/src/cpu/x86/vm/assembler_x86.hpp
hotspot/src/cpu/x86/vm/x86_32.ad
hotspot/test/compiler/6603011/Test.java
--- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp	Sat Nov 06 02:53:59 2010 -0700
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp	Sat Nov 06 18:52:07 2010 -0700
@@ -1275,6 +1275,12 @@
   emit_byte(0xF8 | encode);
 }
 
+void Assembler::divl(Register src) { // Unsigned
+  int encode = prefix_and_encode(src->encoding());
+  emit_byte(0xF7);
+  emit_byte(0xF0 | encode);
+}
+
 void Assembler::imull(Register dst, Register src) {
   int encode = prefix_and_encode(dst->encoding(), src->encoding());
   emit_byte(0x0F);
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp	Sat Nov 06 02:53:59 2010 -0700
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp	Sat Nov 06 18:52:07 2010 -0700
@@ -1011,6 +1011,7 @@
   void hlt();
 
   void idivl(Register src);
+  void divl(Register src); // Unsigned division
 
   void idivq(Register src);
 
--- a/hotspot/src/cpu/x86/vm/x86_32.ad	Sat Nov 06 02:53:59 2010 -0700
+++ b/hotspot/src/cpu/x86/vm/x86_32.ad	Sat Nov 06 18:52:07 2010 -0700
@@ -8863,48 +8863,64 @@
   effect( TEMP tmp, TEMP tmp2, KILL cr );
   ins_cost(1000);
   format %{ "MOV    $tmp,abs($imm) # ldiv EDX:EAX,$imm\n\t"
+            "XOR    $tmp2,$tmp2\n\t"
             "CMP    $tmp,EDX\n\t"
             "JA,s   fast\n\t"
             "MOV    $tmp2,EAX\n\t"
             "MOV    EAX,EDX\n\t"
-            "SAR    EDX,31\n\t"
-            "IDIV   $tmp\n\t"
-            "XCHG   EAX,$tmp2 \n\t"
-            "IDIV   $tmp\n\t"
-            "CDQ\n\t"
-            "ADD    EDX,$tmp2\n\t"
+            "MOV    EDX,0\n\t"
+            "JLE,s  pos\n\t"
+            "LNEG   EAX : $tmp2\n\t"
+            "DIV    $tmp # unsigned division\n\t"
+            "XCHG   EAX,$tmp2\n\t"
+            "DIV    $tmp\n\t"
+            "LNEG   $tmp2 : EAX\n\t"
             "JMP,s  done\n"
+    "pos:\n\t"
+            "DIV    $tmp\n\t"
+            "XCHG   EAX,$tmp2\n"
     "fast:\n\t"
-            "IDIV   $tmp\n\t"
-            "XOR    EDX,EDX\n"
+            "DIV    $tmp\n"
     "done:\n\t"
+            "MOV    EDX,$tmp2\n\t"
             "NEG    EDX:EAX # if $imm < 0" %}
   ins_encode %{
     int con = (int)$imm$$constant;
     assert(con != 0 && con != -1 && con != min_jint, "wrong divisor");
     int pcon = (con > 0) ? con : -con;
-    Label Lfast, Ldone;
+    Label Lfast, Lpos, Ldone;
 
     __ movl($tmp$$Register, pcon);
+    __ xorl($tmp2$$Register,$tmp2$$Register);
     __ cmpl($tmp$$Register, HIGH_FROM_LOW($dst$$Register));
-    __ jccb(Assembler::above, Lfast);
+    __ jccb(Assembler::above, Lfast); // result fits into 32 bit
 
     __ movl($tmp2$$Register, $dst$$Register); // save
     __ movl($dst$$Register, HIGH_FROM_LOW($dst$$Register));
-    __ sarl(HIGH_FROM_LOW($dst$$Register), 31); // src sign
-    __ idivl($tmp$$Register);
+    __ movl(HIGH_FROM_LOW($dst$$Register),0); // preserve flags
+    __ jccb(Assembler::lessEqual, Lpos); // result is positive
+
+    // Negative dividend.
+    // convert value to positive to use unsigned division
+    __ lneg($dst$$Register, $tmp2$$Register);
+    __ divl($tmp$$Register);
     __ xchgl($dst$$Register, $tmp2$$Register);
-    __ idivl($tmp$$Register);
-    __ cdql();
-    __ addl(HIGH_FROM_LOW($dst$$Register),$tmp2$$Register);
+    __ divl($tmp$$Register);
+    // revert result back to negative
+    __ lneg($tmp2$$Register, $dst$$Register);
     __ jmpb(Ldone);
 
+    __ bind(Lpos);
+    __ divl($tmp$$Register); // Use unsigned division
+    __ xchgl($dst$$Register, $tmp2$$Register);
+    // Fallthrow for final divide, tmp2 has 32 bit hi result
+
     __ bind(Lfast);
-    // fast path: src is positive and result fits into 32 bit
-    __ idivl($tmp$$Register);
-    __ xorl(HIGH_FROM_LOW($dst$$Register),HIGH_FROM_LOW($dst$$Register));
+    // fast path: src is positive
+    __ divl($tmp$$Register); // Use unsigned division
 
     __ bind(Ldone);
+    __ movl(HIGH_FROM_LOW($dst$$Register),$tmp2$$Register);
     if (con < 0) {
       __ lneg(HIGH_FROM_LOW($dst$$Register), $dst$$Register);
     }
@@ -8922,18 +8938,27 @@
             "JA,s   fast\n\t"
             "MOV    $tmp2,EAX\n\t"
             "MOV    EAX,EDX\n\t"
-            "SAR    EDX,31\n\t"
-            "IDIV   $tmp\n\t"
+            "MOV    EDX,0\n\t"
+            "JLE,s  pos\n\t"
+            "LNEG   EAX : $tmp2\n\t"
+            "DIV    $tmp # unsigned division\n\t"
+            "MOV    EAX,$tmp2\n\t"
+            "DIV    $tmp\n\t"
+            "NEG    EDX\n\t"
+            "JMP,s  done\n"
+    "pos:\n\t"
+            "DIV    $tmp\n\t"
             "MOV    EAX,$tmp2\n"
     "fast:\n\t"
-            "IDIV   $tmp\n\t"
+            "DIV    $tmp\n"
+    "done:\n\t"
             "MOV    EAX,EDX\n\t"
             "SAR    EDX,31\n\t" %}
   ins_encode %{
     int con = (int)$imm$$constant;
     assert(con != 0 && con != -1 && con != min_jint, "wrong divisor");
     int pcon = (con > 0) ? con : -con;
-    Label  Lfast;
+    Label  Lfast, Lpos, Ldone;
 
     __ movl($tmp$$Register, pcon);
     __ cmpl($tmp$$Register, HIGH_FROM_LOW($dst$$Register));
@@ -8941,12 +8966,28 @@
 
     __ movl($tmp2$$Register, $dst$$Register); // save
     __ movl($dst$$Register, HIGH_FROM_LOW($dst$$Register));
-    __ sarl(HIGH_FROM_LOW($dst$$Register), 31); // src sign
-    __ idivl($tmp$$Register);
+    __ movl(HIGH_FROM_LOW($dst$$Register),0); // preserve flags
+    __ jccb(Assembler::lessEqual, Lpos); // result is positive
+
+    // Negative dividend.
+    // convert value to positive to use unsigned division
+    __ lneg($dst$$Register, $tmp2$$Register);
+    __ divl($tmp$$Register);
+    __ movl($dst$$Register, $tmp2$$Register);
+    __ divl($tmp$$Register);
+    // revert remainder back to negative
+    __ negl(HIGH_FROM_LOW($dst$$Register));
+    __ jmpb(Ldone);
+
+    __ bind(Lpos);
+    __ divl($tmp$$Register);
     __ movl($dst$$Register, $tmp2$$Register);
 
     __ bind(Lfast);
-    __ idivl($tmp$$Register);
+    // fast path: src is positive
+    __ divl($tmp$$Register);
+
+    __ bind(Ldone);
     __ movl($dst$$Register, HIGH_FROM_LOW($dst$$Register));
     __ sarl(HIGH_FROM_LOW($dst$$Register), 31); // result sign
 
--- a/hotspot/test/compiler/6603011/Test.java	Sat Nov 06 02:53:59 2010 -0700
+++ b/hotspot/test/compiler/6603011/Test.java	Sat Nov 06 18:52:07 2010 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -108,8 +108,10 @@
 
     if (quo != quo0 || rem != rem0) {
       if (VERBOSE) {
-        System.out.println("  " + dividend + " / " + divisor() + " = " +
-                           quo + ", " + dividend + " % " + divisor() + " = " + rem);
+        System.out.println("Computed: " + dividend + " / " + divisor() + " = " +
+                           quo  + ", " + dividend + " % " + divisor() + " = " + rem );
+        System.out.println("expected: " + dividend + " / " + divisor() + " = " +
+                           quo0 + ", " + dividend + " % " + divisor() + " = " + rem0);
         // Report sign of rem failure
         if (rem != 0 && (rem ^ dividend) < 0) {
           System.out.println("  rem & dividend have different signs");
@@ -168,7 +170,7 @@
     for (int i = start; i <= end; i++) {
       for (int s = 0; s < 64; s += 4) {
         total++;
-        long dividend = i << s;
+        long dividend = ((long)i) << s;
         if (!checkL(dividend)) {
           wrong++;
           // Stop on the first failure