Revision: 3116
Author: [email protected]
Date: Fri Oct 23 02:18:19 2009
Log: X64/Win64: Alternative implementation of fmod in general.

Review URL: http://codereview.chromium.org/303034

http://code.google.com/p/v8/source/detail?r=3116

Modified:
  /branches/bleeding_edge/src/conversions-inl.h
  /branches/bleeding_edge/src/conversions.cc
  /branches/bleeding_edge/src/conversions.h
  /branches/bleeding_edge/src/platform-nullos.cc
  /branches/bleeding_edge/src/platform-posix.cc
  /branches/bleeding_edge/src/platform-win32.cc
  /branches/bleeding_edge/src/platform.h
  /branches/bleeding_edge/src/runtime.cc
  /branches/bleeding_edge/src/x64/assembler-x64.cc
  /branches/bleeding_edge/src/x64/assembler-x64.h
  /branches/bleeding_edge/src/x64/codegen-x64.cc
  /branches/bleeding_edge/src/x64/disasm-x64.cc
  /branches/bleeding_edge/test/mjsunit/div-mod.js

=======================================
--- /branches/bleeding_edge/src/conversions-inl.h       Mon May 25 03:05:56 2009
+++ /branches/bleeding_edge/src/conversions-inl.h       Fri Oct 23 02:18:19 2009
@@ -84,7 +84,7 @@
    static const double two32 = 4294967296.0;
    static const double two31 = 2147483648.0;
    if (!isfinite(x) || x == 0) return 0;
-  if (x < 0 || x >= two32) x = fmod(x, two32);
+  if (x < 0 || x >= two32) x = modulo(x, two32);
    x = (x >= 0) ? floor(x) : ceil(x) + two32;
    return (int32_t) ((x >= two31) ? x - two32 : x);
  }
=======================================
--- /branches/bleeding_edge/src/conversions.cc  Wed Jun 24 10:25:46 2009
+++ /branches/bleeding_edge/src/conversions.cc  Fri Oct 23 02:18:19 2009
@@ -664,7 +664,7 @@
    int integer_pos = kBufferSize - 2;
    do {
      integer_buffer[integer_pos--] =
-        chars[static_cast<int>(fmod(integer_part, radix))];
+        chars[static_cast<int>(modulo(integer_part, radix))];
      integer_part /= radix;
    } while (integer_part >= 1.0);
    // Sanity check.
=======================================
--- /branches/bleeding_edge/src/conversions.h   Mon May 25 03:05:56 2009
+++ /branches/bleeding_edge/src/conversions.h   Fri Oct 23 02:18:19 2009
@@ -31,6 +31,7 @@
  namespace v8 {
  namespace internal {

+
  // The fast double-to-int conversion routine does not guarantee
  // rounding towards zero.
  // The result is unspecified if x is infinite or NaN, or if the rounded
=======================================
--- /branches/bleeding_edge/src/platform-nullos.cc      Fri Jul 31 06:17:59 2009
+++ /branches/bleeding_edge/src/platform-nullos.cc      Fri Oct 23 02:18:19 2009
@@ -45,6 +45,13 @@
    UNIMPLEMENTED();
    return 0;
  }
+
+
+// Give V8 the opportunity to override the default fmod behavior.
+double modulo(double x, double y) {
+  UNIMPLEMENTED();
+  return 0;
+}


  // Initialize OS class early in the V8 startup.
=======================================
--- /branches/bleeding_edge/src/platform-posix.cc       Tue Aug  4 02:41:18 2009
+++ /branches/bleeding_edge/src/platform-posix.cc       Fri Oct 23 02:18:19 2009
@@ -54,6 +54,12 @@
  namespace v8 {
  namespace internal {

+//  
----------------------------------------------------------------------------
+// Math functions
+
+double modulo(double x, double y) {
+  return fmod(x, y);
+}

  //  
----------------------------------------------------------------------------
  // POSIX date/time support.
=======================================
--- /branches/bleeding_edge/src/platform-win32.cc       Tue Oct 13 03:56:13 2009
+++ /branches/bleeding_edge/src/platform-win32.cc       Fri Oct 23 02:18:19 2009
@@ -222,6 +222,31 @@
  double ceiling(double x) {
    return ceil(x);
  }
+
+#ifdef _WIN64
+typedef double (*ModuloFunction)(double, double);
+
+// Defined in codegen-x64.cc.
+ModuloFunction CreateModuloFunction();
+
+double modulo(double x, double y) {
+  static ModuloFunction function = CreateModuloFunction();
+  return function(x, y);
+}
+#else  // Win32
+
+double modulo(double x, double y) {
+  // Workaround MS fmod bugs. ECMA-262 says:
+  // dividend is finite and divisor is an infinity => result equals  
dividend
+  // dividend is a zero and divisor is nonzero finite => result equals  
dividend
+  if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
+      !(x == 0 && (y != 0 && isfinite(y)))) {
+    x = fmod(x, y);
+  }
+  return x;
+}
+
+#endif  // _WIN64

  //  
----------------------------------------------------------------------------
  // The Time class represents time on win32. A timestamp is represented as
=======================================
--- /branches/bleeding_edge/src/platform.h      Fri Jul 31 06:17:59 2009
+++ /branches/bleeding_edge/src/platform.h      Fri Oct 23 02:18:19 2009
@@ -111,6 +111,7 @@
  class Semaphore;

  double ceiling(double x);
+double modulo(double x, double y);

  // Forward declarations.
  class Socket;
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Thu Oct 22 07:49:00 2009
+++ /branches/bleeding_edge/src/runtime.cc      Fri Oct 23 02:18:19 2009
@@ -3742,14 +3742,7 @@
    CONVERT_DOUBLE_CHECKED(x, args[0]);
    CONVERT_DOUBLE_CHECKED(y, args[1]);

-#if defined WIN32 || defined _WIN64
-  // Workaround MS fmod bugs. ECMA-262 says:
-  // dividend is finite and divisor is an infinity => result equals  
dividend
-  // dividend is a zero and divisor is nonzero finite => result equals  
dividend
-  if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
-      !(x == 0 && (y != 0 && isfinite(y))))
-#endif
-  x = fmod(x, y);
+  x = modulo(x, y);
    // NewNumberFromDouble may return a Smi instead of a Number object
    return Heap::NewNumberFromDouble(x);
  }
=======================================
--- /branches/bleeding_edge/src/x64/assembler-x64.cc    Thu Oct 22 07:49:00  
2009
+++ /branches/bleeding_edge/src/x64/assembler-x64.cc    Fri Oct 23 02:18:19  
2009
@@ -2007,6 +2007,14 @@
    emit(0xDD);
    emit_operand(3, adr);
  }
+
+
+void Assembler::fstp(int index) {
+  ASSERT(is_uint3(index));
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_farith(0xDD, 0xD8, index);
+}


  void Assembler::fild_s(const Operand& adr) {
@@ -2313,18 +2321,7 @@
  }


-void Assembler::movsd(Register dst, XMMRegister src) {
-  EnsureSpace ensure_space(this);
-  last_pc_ = pc_;
-  emit(0xF2);  // double
-  emit_optional_rex_32(src, dst);
-  emit(0x0F);
-  emit(0x11);  // store
-  emit_sse_operand(src, dst);
-}
-
-
-void Assembler::movsd(XMMRegister dst, Register src) {
+void Assembler::movsd(XMMRegister dst, XMMRegister src) {
    EnsureSpace ensure_space(this);
    last_pc_ = pc_;
    emit(0xF2);  // double
=======================================
--- /branches/bleeding_edge/src/x64/assembler-x64.h     Thu Oct 22 07:49:00 2009
+++ /branches/bleeding_edge/src/x64/assembler-x64.h     Fri Oct 23 02:18:19 2009
@@ -1013,6 +1013,7 @@

    void fstp_s(const Operand& adr);
    void fstp_d(const Operand& adr);
+  void fstp(int index);

    void fild_s(const Operand& adr);
    void fild_d(const Operand& adr);
@@ -1066,8 +1067,7 @@

    // SSE2 instructions
    void movsd(const Operand& dst, XMMRegister src);
-  void movsd(Register src, XMMRegister dst);
-  void movsd(XMMRegister dst, Register src);
+  void movsd(XMMRegister src, XMMRegister dst);
    void movsd(XMMRegister src, const Operand& dst);

    void cvttss2si(Register dst, const Operand& src);
=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc      Thu Oct 22 03:07:45 2009
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc      Fri Oct 23 02:18:19 2009
@@ -7643,6 +7643,98 @@
    return (static_cast<unsigned>(cc_) << 1) | (strict_ ? 1 : 0);
  }

+#undef __
+
+#define __ masm.
+
+#ifdef _WIN64
+typedef double (*ModuloFunction)(double, double);
+// Define custom fmod implementation.
+ModuloFunction CreateModuloFunction() {
+  size_t actual_size;
+  byte* buffer =  
static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+                                                 &actual_size,
+                                                 true));
+  CHECK(buffer);
+  Assembler masm(buffer, actual_size);
+  // Generated code is put into a fixed, unmovable, buffer, and not into
+  // the V8 heap. We can't, and don't, refer to any relocatable addresses
+  // (e.g. the JavaScript nan-object).
+
+  // Windows 64 ABI passes double arguments in xmm0, xmm1 and
+  // returns result in xmm0.
+  // Argument backing space is allocated on the stack above
+  // the return address.
+
+  // Compute x mod y.
+  // Load y and x (use argument backing store as temporary storage).
+  __ movsd(Operand(rsp, kPointerSize * 2), xmm1);
+  __ movsd(Operand(rsp, kPointerSize), xmm0);
+  __ fld_d(Operand(rsp, kPointerSize * 2));
+  __ fld_d(Operand(rsp, kPointerSize));
+
+  // Clear exception flags before operation.
+  {
+    Label no_exceptions;
+    __ fwait();
+    __ fnstsw_ax();
+    // Clear if Illegal Operand or Zero Division exceptions are set.
+    __ testb(rax, Immediate(5));
+    __ j(zero, &no_exceptions);
+    __ fnclex();
+    __ bind(&no_exceptions);
+  }
+
+  // Compute st(0) % st(1)
+  {
+    Label partial_remainder_loop;
+    __ bind(&partial_remainder_loop);
+    __ fprem();
+    __ fwait();
+    __ fnstsw_ax();
+    __ testl(rax, Immediate(0x400 /* C2 */));
+    // If C2 is set, computation only has partial result. Loop to
+    // continue computation.
+    __ j(not_zero, &partial_remainder_loop);
+  }
+
+  Label valid_result;
+  Label return_result;
+  // If Invalid Operand or Zero Division exceptions are set,
+  // return NaN.
+  __ testb(rax, Immediate(5));
+  __ j(zero, &valid_result);
+  __ fstp(0);  // Drop result in st(0).
+  int64_t kNaNValue = V8_INT64_C(0x7ff8000000000000);
+  __ movq(rcx, kNaNValue, RelocInfo::NONE);
+  __ movq(Operand(rsp, kPointerSize), rcx);
+  __ movsd(xmm0, Operand(rsp, kPointerSize));
+  __ jmp(&return_result);
+
+  // If result is valid, return that.
+  __ bind(&valid_result);
+  __ fstp_d(Operand(rsp, kPointerSize));
+  __ movsd(xmm0, Operand(rsp, kPointerSize));
+
+  // Clean up FPU stack and exceptions and return xmm0
+  __ bind(&return_result);
+  __ fstp(0);  // Unload y.
+  {
+    Label no_exceptions;
+    __ testb(rax, Immediate(0x3f /* Any Exception*/));
+    __ j(zero, &no_exceptions);
+    __ fnclex();
+    __ bind(&no_exceptions);
+  }
+  __ ret(0);
+
+  CodeDesc desc;
+  masm.GetCode(&desc);
+  // Call the function from C++.
+  return FUNCTION_CAST<ModuloFunction>(buffer);
+}
+
+#endif

  #undef __

=======================================
--- /branches/bleeding_edge/src/x64/disasm-x64.cc       Thu Aug 27 23:18:36 2009
+++ /branches/bleeding_edge/src/x64/disasm-x64.cc       Fri Oct 23 02:18:19 2009
@@ -860,12 +860,22 @@
        return count + 1;
      }
    } else if (b1 == 0xDD) {
-    if ((b2 & 0xF8) == 0xC0) {
-      AppendToBuffer("ffree st%d", b2 & 0x7);
+    int mod, regop, rm;
+    get_modrm(*(data + 1), &mod, &regop, &rm);
+    if (mod == 3) {
+      switch (regop) {
+        case 0:
+          AppendToBuffer("ffree st%d", rm & 7);
+          break;
+        case 2:
+          AppendToBuffer("fstp st%d", rm & 7);
+          break;
+        default:
+          UnimplementedInstruction();
+          break;
+      }
        return 2;
      } else {
-      int mod, regop, rm;
-      get_modrm(*(data + 1), &mod, &regop, &rm);
        const char* mnem = "?";
        switch (regop) {
          case 0:
=======================================
--- /branches/bleeding_edge/test/mjsunit/div-mod.js     Wed Aug 19 04:34:06 2009
+++ /branches/bleeding_edge/test/mjsunit/div-mod.js     Fri Oct 23 02:18:19 2009
@@ -86,3 +86,72 @@
  for (var i = 0; i < divisors.length; i++) {
    run_tests_for(divisors[i]);
  }
+
+// Test extreme corner cases of modulo.
+
+// Computes the modulo by slow but lossless operations.
+function compute_mod(dividend, divisor) {
+  // Return NaN if either operand is NaN, if divisor is 0 or
+  // dividend is an infinity. Return dividend if divisor is an infinity.
+  if (isNaN(dividend) || isNaN(divisor) || divisor == 0) { return NaN; }
+  var sign = 1;
+  if (dividend < 0) { dividend = -dividend; sign = -1; }
+  if (dividend == Infinity) { return NaN; }
+  if (divisor < 0) { divisor = -divisor; }
+  if (divisor == Infinity) { return sign * dividend; }
+  function rec_mod(a, b) {
+    // Subtracts maximal possible multiplum of b from a.
+    if (a >= b) {
+      a = rec_mod(a, 2 * b);
+      if (a >= b) { a -= b; }
+    }
+    return a;
+  }
+  return sign * rec_mod(dividend, divisor);
+}
+
+(function () {
+  var large_non_smi = 1234567891234.12245;
+  var small_non_smi = 43.2367243;
+  var repeating_decimal = 0.3;
+  var finite_decimal = 0.5;
+  var smi = 43;
+  var power_of_two = 64;
+  var min_normal = Number.MIN_VALUE * Math.pow(2, 52);
+  var max_denormal = Number.MIN_VALUE * (Math.pow(2, 52) - 1);
+
+  // All combinations of NaN, Infinity, normal, denormal and zero.
+  var example_numbers = [
+    NaN,
+    0,
+    Number.MIN_VALUE,
+    3 * Number.MIN_VALUE,
+    max_denormal,
+    min_normal,
+    repeating_decimal,
+    finite_decimal,
+    smi,
+    power_of_two,
+    small_non_smi,
+    large_non_smi,
+    Number.MAX_VALUE,
+    Infinity
+  ];
+
+  function doTest(a, b) {
+    var exp = compute_mod(a, b);
+    var act = a % b;
+    assertEquals(exp, act, a + " % " + b);
+  }
+
+  for (var i = 0; i < example_numbers.length; i++) {
+    for (var j = 0; j < example_numbers.length; j++) {
+      var a = example_numbers[i];
+      var b = example_numbers[j];
+      doTest(a,b);
+      doTest(-a,b);
+      doTest(a,-b);
+      doTest(-a,-b);
+    }
+  }
+})()

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to