Custom call IC for Math.floor.
authorvitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 21 Sep 2010 12:54:12 +0000 (12:54 +0000)
committervitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 21 Sep 2010 12:54:12 +0000 (12:54 +0000)
Review URL: http://codereview.chromium.org/3327022

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5499 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

13 files changed:
src/arm/stub-cache-arm.cc
src/bootstrapper.cc
src/ia32/assembler-ia32.cc
src/ia32/assembler-ia32.h
src/ia32/disasm-ia32.cc
src/ia32/macro-assembler-ia32.cc
src/ia32/macro-assembler-ia32.h
src/ia32/stub-cache-ia32.cc
src/stub-cache.cc
src/stub-cache.h
src/x64/stub-cache-x64.cc
test/cctest/test-disasm-ia32.cc
test/mjsunit/math-floor.js [new file with mode: 0644]

index 87a7fb9..070e352 100644 (file)
@@ -1633,6 +1633,16 @@ Object* CallStubCompiler::CompileStringFromCharCodeCall(
 }
 
 
+Object* CallStubCompiler::CompileMathFloorCall(Object* object,
+                                               JSObject* holder,
+                                               JSGlobalPropertyCell* cell,
+                                               JSFunction* function,
+                                               String* name) {
+  // TODO(872): implement this.
+  return Heap::undefined_value();
+}
+
+
 Object* CallStubCompiler::CompileCallConstant(Object* object,
                                               JSObject* holder,
                                               JSFunction* function,
index 6e6c2c6..ff68572 100644 (file)
@@ -1344,33 +1344,40 @@ bool Genesis::InstallNatives() {
 }
 
 
-static void InstallCustomCallGenerator(
-    Handle<JSFunction> holder_function,
-    CallStubCompiler::CustomGeneratorOwner owner_flag,
-    const char* function_name,
-    int id) {
-  Handle<JSObject> owner;
-  if (owner_flag == CallStubCompiler::FUNCTION) {
-    owner = Handle<JSObject>::cast(holder_function);
-  } else {
-    ASSERT(owner_flag == CallStubCompiler::INSTANCE_PROTOTYPE);
-    owner = Handle<JSObject>(
-        JSObject::cast(holder_function->instance_prototype()));
+static Handle<JSObject> ResolveCustomCallGeneratorHolder(
+    Handle<Context> global_context,
+    const char* holder_expr) {
+  Handle<GlobalObject> global(global_context->global());
+  const char* period_pos = strchr(holder_expr, '.');
+  if (period_pos == NULL) {
+    return Handle<JSObject>::cast(
+        GetProperty(global, Factory::LookupAsciiSymbol(holder_expr)));
   }
+  ASSERT_EQ(".prototype", period_pos);
+  Vector<const char> property(holder_expr, period_pos - holder_expr);
+  Handle<JSFunction> function = Handle<JSFunction>::cast(
+      GetProperty(global, Factory::LookupSymbol(property)));
+  return Handle<JSObject>(JSObject::cast(function->prototype()));
+}
+
+
+static void InstallCustomCallGenerator(Handle<JSObject> holder,
+                                       const char* function_name,
+                                       int id) {
   Handle<String> name = Factory::LookupAsciiSymbol(function_name);
-  Handle<JSFunction> function(JSFunction::cast(owner->GetProperty(*name)));
+  Handle<JSFunction> function(JSFunction::cast(holder->GetProperty(*name)));
   function->shared()->set_function_data(Smi::FromInt(id));
 }
 
 
 void Genesis::InstallCustomCallGenerators() {
   HandleScope scope;
-#define INSTALL_CALL_GENERATOR(holder_fun, owner_flag, fun_name, name)    \
-  {                                                                       \
-    Handle<JSFunction> holder(global_context()->holder_fun##_function()); \
-    const int id = CallStubCompiler::k##name##CallGenerator;              \
-    InstallCustomCallGenerator(holder, CallStubCompiler::owner_flag,      \
-                               #fun_name, id);                            \
+#define INSTALL_CALL_GENERATOR(holder_expr, fun_name, name)     \
+  {                                                             \
+    Handle<JSObject> holder = ResolveCustomCallGeneratorHolder( \
+        global_context(), #holder_expr);                        \
+    const int id = CallStubCompiler::k##name##CallGenerator;    \
+    InstallCustomCallGenerator(holder, #fun_name, id);          \
   }
   CUSTOM_CALL_IC_GENERATORS(INSTALL_CALL_GENERATOR)
 #undef INSTALL_CALL_GENERATOR
index f8a1593..fcb973d 100644 (file)
@@ -2205,6 +2205,16 @@ void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
 }
 
 
+void Assembler::andpd(XMMRegister dst, XMMRegister src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  EMIT(0x66);
+  EMIT(0x0F);
+  EMIT(0x54);
+  emit_sse_operand(dst, src);
+}
+
+
 void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
   ASSERT(CpuFeatures::IsEnabled(SSE2));
   EnsureSpace ensure_space(this);
@@ -2227,7 +2237,29 @@ void Assembler::movmskpd(Register dst, XMMRegister src) {
 }
 
 
-void Assembler::movdqa(const Operand& dst, XMMRegister src ) {
+void Assembler::cmpltsd(XMMRegister dst, XMMRegister src) {
+  ASSERT(CpuFeatures::IsEnabled(SSE2));
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  EMIT(0xF2);
+  EMIT(0x0F);
+  EMIT(0xC2);
+  emit_sse_operand(dst, src);
+  EMIT(1);  // LT == 1
+}
+
+
+void Assembler::movaps(XMMRegister dst, XMMRegister src) {
+  ASSERT(CpuFeatures::IsEnabled(SSE2));
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  EMIT(0x0F);
+  EMIT(0x28);
+  emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movdqa(const Operand& dst, XMMRegister src) {
   ASSERT(CpuFeatures::IsEnabled(SSE2));
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -2384,6 +2416,19 @@ void Assembler::ptest(XMMRegister dst, XMMRegister src) {
   emit_sse_operand(dst, src);
 }
 
+
+void Assembler::psllq(XMMRegister reg, int8_t imm8) {
+  ASSERT(CpuFeatures::IsEnabled(SSE2));
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  EMIT(0x66);
+  EMIT(0x0F);
+  EMIT(0x73);
+  emit_sse_operand(esi, reg);  // esi == 6
+  EMIT(imm8);
+}
+
+
 void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) {
   Register ireg = { reg.code() };
   emit_operand(ireg, adr);
index e0d7424..265c82d 100644 (file)
@@ -795,9 +795,15 @@ class Assembler : public Malloced {
   void xorpd(XMMRegister dst, XMMRegister src);
   void sqrtsd(XMMRegister dst, XMMRegister src);
 
+  void andpd(XMMRegister dst, XMMRegister src);
+
   void ucomisd(XMMRegister dst, XMMRegister src);
   void movmskpd(Register dst, XMMRegister src);
 
+  void cmpltsd(XMMRegister dst, XMMRegister src);
+
+  void movaps(XMMRegister dst, XMMRegister src);
+
   void movdqa(XMMRegister dst, const Operand& src);
   void movdqa(const Operand& dst, XMMRegister src);
   void movdqu(XMMRegister dst, const Operand& src);
@@ -813,6 +819,8 @@ class Assembler : public Malloced {
   void pxor(XMMRegister dst, XMMRegister src);
   void ptest(XMMRegister dst, XMMRegister src);
 
+  void psllq(XMMRegister reg, int8_t imm8);
+
   // Parallel XMM operations.
   void movntdqa(XMMRegister src, const Operand& dst);
   void movntdq(const Operand& dst, XMMRegister src);
index 9f3e8d8..207648b 100644 (file)
@@ -958,6 +958,14 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
           } else if (f0byte == 0xA2 || f0byte == 0x31) {
             AppendToBuffer("%s", f0mnem);
             data += 2;
+          } else if (f0byte == 0x28) {
+            data += 2;
+            int mod, regop, rm;
+            get_modrm(*data, &mod, &regop, &rm);
+            AppendToBuffer("movaps %s,%s",
+                           NameOfXMMRegister(regop),
+                           NameOfXMMRegister(rm));
+            data++;
           } else if ((f0byte & 0xF0) == 0x80) {
             data += JumpConditional(data, branch_hint);
           } else if (f0byte == 0xBE || f0byte == 0xBF || f0byte == 0xB6 ||
@@ -1157,6 +1165,23 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
                             NameOfXMMRegister(regop),
                             NameOfXMMRegister(rm));
              data++;
+          } else if (*data == 0x73) {
+             data++;
+             int mod, regop, rm;
+             get_modrm(*data, &mod, &regop, &rm);
+             int8_t imm8 = static_cast<int8_t>(data[1]);
+             AppendToBuffer("psllq %s,%d",
+                            NameOfXMMRegister(rm),
+                            static_cast<int>(imm8));
+             data += 2;
+          } else if (*data == 0x54) {
+             data++;
+             int mod, regop, rm;
+             get_modrm(*data, &mod, &regop, &rm);
+             AppendToBuffer("andpd %s,%s",
+                            NameOfXMMRegister(regop),
+                            NameOfXMMRegister(rm));
+             data++;
           } else {
             UnimplementedInstruction();
           }
@@ -1275,6 +1300,23 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
                                NameOfXMMRegister(rm));
                 data++;
               }
+            } else if (b2 == 0xC2) {
+              // Intel manual 2A, Table 3-18.
+              const char* const pseudo_op[] = {
+                "cmpeqsd",
+                "cmpltsd",
+                "cmplesd",
+                "cmpunordsd",
+                "cmpneqsd",
+                "cmpnltsd",
+                "cmpnlesd",
+                "cmpordsd"
+              };
+              AppendToBuffer("%s %s,%s",
+                             pseudo_op[data[1]],
+                             NameOfXMMRegister(regop),
+                             NameOfXMMRegister(rm));
+              data += 2;
             } else {
               if (mod != 0x3) {
                 AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
@@ -1368,7 +1410,7 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
                                      " %s",
                                      tmp_buffer_.start());
   return instr_len;
-}
+}  // NOLINT (function is too long)
 
 
 //------------------------------------------------------------------------------
index 87e25d7..f8dabd5 100644 (file)
@@ -1553,6 +1553,17 @@ void MacroAssembler::ConvertToInt32(Register dst,
 }
 
 
+void MacroAssembler::LoadPowerOf2(XMMRegister dst,
+                                  Register scratch,
+                                  int power) {
+  ASSERT(is_uintn(power + HeapNumber::kExponentBias,
+                  HeapNumber::kExponentBits));
+  mov(scratch, Immediate(power + HeapNumber::kExponentBias));
+  movd(dst, Operand(scratch));
+  psllq(dst, HeapNumber::kMantissaBits);
+}
+
+
 void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
     Register instance_type,
     Register scratch,
index a7534cb..aa7caf5 100644 (file)
@@ -258,6 +258,8 @@ class MacroAssembler: public Assembler {
                       TypeInfo info,
                       Label* on_not_int32);
 
+  void LoadPowerOf2(XMMRegister dst, Register scratch, int power);
+
   // Abort execution if argument is not a number. Used in debug code.
   void AbortIfNotNumber(Register object);
 
index e2af9d4..672d8c7 100644 (file)
@@ -1819,6 +1819,131 @@ Object* CallStubCompiler::CompileStringFromCharCodeCall(
 }
 
 
+Object* CallStubCompiler::CompileMathFloorCall(Object* object,
+                                               JSObject* holder,
+                                               JSGlobalPropertyCell* cell,
+                                               JSFunction* function,
+                                               String* name) {
+  // ----------- S t a t e -------------
+  //  -- ecx                 : name
+  //  -- esp[0]              : return address
+  //  -- esp[(argc - n) * 4] : arg[n] (zero-based)
+  //  -- ...
+  //  -- esp[(argc + 1) * 4] : receiver
+  // -----------------------------------
+
+  if (!CpuFeatures::IsSupported(SSE2)) return Heap::undefined_value();
+  CpuFeatures::Scope use_sse2(SSE2);
+
+  const int argc = arguments().immediate();
+
+  // If the object is not a JSObject or we got an unexpected number of
+  // arguments, bail out to the regular call.
+  if (!object->IsJSObject() || argc != 1) return Heap::undefined_value();
+
+  Label miss;
+  GenerateNameCheck(name, &miss);
+
+  if (cell == NULL) {
+    __ mov(edx, Operand(esp, 2 * kPointerSize));
+
+    STATIC_ASSERT(kSmiTag == 0);
+    __ test(edx, Immediate(kSmiTagMask));
+    __ j(zero, &miss);
+
+    CheckPrototypes(JSObject::cast(object), edx, holder, ebx, eax, edi, name,
+                    &miss);
+  } else {
+    ASSERT(cell->value() == function);
+    GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss);
+    GenerateLoadFunctionFromCell(cell, function, &miss);
+  }
+
+  // Load the (only) argument into eax.
+  __ mov(eax, Operand(esp, 1 * kPointerSize));
+
+  // Check if the argument is a smi.
+  Label smi;
+  STATIC_ASSERT(kSmiTag == 0);
+  __ test(eax, Immediate(kSmiTagMask));
+  __ j(zero, &smi);
+
+  // Check if the argument is a heap number and load its value into xmm0.
+  Label slow;
+  __ CheckMap(eax, Factory::heap_number_map(), &slow, true);
+  __ movdbl(xmm0, FieldOperand(eax, HeapNumber::kValueOffset));
+
+  // Check if the argument is strictly positive. Note this also
+  // discards NaN.
+  __ xorpd(xmm1, xmm1);
+  __ ucomisd(xmm0, xmm1);
+  __ j(below_equal, &slow);
+
+  // Do a truncating conversion.
+  __ cvttsd2si(eax, Operand(xmm0));
+
+  // Check if the result fits into a smi. Note this also checks for
+  // 0x80000000 which signals a failed conversion.
+  Label wont_fit_into_smi;
+  __ test(eax, Immediate(0xc0000000));
+  __ j(not_zero, &wont_fit_into_smi);
+
+  // Smi tag and return.
+  __ SmiTag(eax);
+  __ bind(&smi);
+  __ ret(2 * kPointerSize);
+
+  // Check if the argument is < 2^kMantissaBits.
+  Label already_round;
+  __ bind(&wont_fit_into_smi);
+  __ LoadPowerOf2(xmm1, ebx, HeapNumber::kMantissaBits);
+  __ ucomisd(xmm0, xmm1);
+  __ j(above_equal, &already_round);
+
+  // Save a copy of the argument.
+  __ movaps(xmm2, xmm0);
+
+  // Compute (argument + 2^kMantissaBits) - 2^kMantissaBits.
+  __ addsd(xmm0, xmm1);
+  __ subsd(xmm0, xmm1);
+
+  // Compare the argument and the tentative result to get the right mask:
+  //   if xmm2 < xmm0:
+  //     xmm2 = 1...1
+  //   else:
+  //     xmm2 = 0...0
+  __ cmpltsd(xmm2, xmm0);
+
+  // Subtract 1 if the argument was less than the tentative result.
+  __ LoadPowerOf2(xmm1, ebx, 0);
+  __ andpd(xmm1, xmm2);
+  __ subsd(xmm0, xmm1);
+
+  // Return a new heap number.
+  __ AllocateHeapNumber(eax, ebx, edx, &slow);
+  __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+  __ ret(2 * kPointerSize);
+
+  // Return the argument (when it's an already round heap number).
+  __ bind(&already_round);
+  __ mov(eax, Operand(esp, 1 * kPointerSize));
+  __ ret(2 * kPointerSize);
+
+  // Tail call the full function. We do not have to patch the receiver
+  // because the function makes no use of it.
+  __ bind(&slow);
+  __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+
+  __ bind(&miss);
+  // ecx: function name.
+  Object* obj = GenerateMissBranch();
+  if (obj->IsFailure()) return obj;
+
+  // Return the generated code.
+  return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name);
+}
+
+
 Object* CallStubCompiler::CompileCallConstant(Object* object,
                                               JSObject* holder,
                                               JSFunction* function,
index 34989d3..af7c0bd 100644 (file)
@@ -1227,7 +1227,7 @@ Object* CallStubCompiler::CompileCustomCall(int generator_id,
                                             String* fname) {
   ASSERT(generator_id >= 0 && generator_id < kNumCallGenerators);
   switch (generator_id) {
-#define CALL_GENERATOR_CASE(ignored1, ignored2, ignored3, name) \
+#define CALL_GENERATOR_CASE(ignored1, ignored2, name)           \
     case k##name##CallGenerator:                                \
       return CallStubCompiler::Compile##name##Call(object,      \
                                                    holder,      \
index 7e44fd0..c47cab7 100644 (file)
@@ -614,29 +614,25 @@ class KeyedStoreStubCompiler: public StubCompiler {
 // Installation of custom call generators for the selected builtins is
 // handled by the bootstrapper.
 //
-// Each entry has a name of a global function (lowercased), a flag
-// controlling whether the generator is set on the function itself or
-// on its instance prototype, a name of a builtin function on the
-// function or its instance prototype (the one the generator is set
-// for), and a name of a generator itself (used to build ids and
-// generator function names).
-#define CUSTOM_CALL_IC_GENERATORS(V)                          \
-  V(array, INSTANCE_PROTOTYPE, push, ArrayPush)               \
-  V(array, INSTANCE_PROTOTYPE, pop, ArrayPop)                 \
-  V(string, INSTANCE_PROTOTYPE, charCodeAt, StringCharCodeAt) \
-  V(string, INSTANCE_PROTOTYPE, charAt, StringCharAt)         \
-  V(string, FUNCTION, fromCharCode, StringFromCharCode)
+// Each entry has a name of a global object property holding an object
+// optionally followed by ".prototype" (this controls whether the
+// generator is set on the object itself or, in case it's a function,
+// on the its instance prototype), a name of a builtin function on the
+// object (the one the generator is set for), and a name of the
+// generator (used to build ids and generator function names).
+#define CUSTOM_CALL_IC_GENERATORS(V)                \
+  V(Array.prototype, push, ArrayPush)               \
+  V(Array.prototype, pop, ArrayPop)                 \
+  V(String.prototype, charCodeAt, StringCharCodeAt) \
+  V(String.prototype, charAt, StringCharAt)         \
+  V(String, fromCharCode, StringFromCharCode)       \
+  V(Math, floor, MathFloor)
 
 
 class CallStubCompiler: public StubCompiler {
  public:
-  enum CustomGeneratorOwner {
-    FUNCTION,
-    INSTANCE_PROTOTYPE
-  };
-
   enum {
-#define DECLARE_CALL_GENERATOR_ID(ignored1, ignore2, ignored3, name) \
+#define DECLARE_CALL_GENERATOR_ID(ignored1, ignore2, name) \
     k##name##CallGenerator,
     CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR_ID)
 #undef DECLARE_CALL_GENERATOR_ID
@@ -675,11 +671,11 @@ class CallStubCompiler: public StubCompiler {
                             JSFunction* function,
                             String* name);
 
-#define DECLARE_CALL_GENERATOR(ignored1, ignored2, ignored3, name) \
-  Object* Compile##name##Call(Object* object,                      \
-                              JSObject* holder,                    \
-                              JSGlobalPropertyCell* cell,          \
-                              JSFunction* function,                \
+#define DECLARE_CALL_GENERATOR(ignored1, ignored2,  name) \
+  Object* Compile##name##Call(Object* object,             \
+                              JSObject* holder,           \
+                              JSGlobalPropertyCell* cell, \
+                              JSFunction* function,       \
                               String* fname);
   CUSTOM_CALL_IC_GENERATORS(DECLARE_CALL_GENERATOR)
 #undef DECLARE_CALL_GENERATOR
index df745cc..75956eb 100644 (file)
@@ -1548,6 +1548,16 @@ Object* CallStubCompiler::CompileStringFromCharCodeCall(
 }
 
 
+Object* CallStubCompiler::CompileMathFloorCall(Object* object,
+                                               JSObject* holder,
+                                               JSGlobalPropertyCell* cell,
+                                               JSFunction* function,
+                                               String* name) {
+  // TODO(872): implement this.
+  return Heap::undefined_value();
+}
+
+
 Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
                                                  JSObject* holder,
                                                  String* name) {
index 25d2ec0..71735bc 100644 (file)
@@ -412,6 +412,24 @@ TEST(DisasmIa320) {
     }
   }
 
+  // andpd, cmpltsd, movaps, psllq.
+  {
+    if (CpuFeatures::IsSupported(SSE2)) {
+      CpuFeatures::Scope fscope(SSE2);
+      __ andpd(xmm0, xmm1);
+      __ andpd(xmm1, xmm2);
+
+      __ cmpltsd(xmm0, xmm1);
+      __ cmpltsd(xmm1, xmm2);
+
+      __ movaps(xmm0, xmm1);
+      __ movaps(xmm1, xmm2);
+
+      __ psllq(xmm0, 17);
+      __ psllq(xmm1, 42);
+    }
+  }
+
   __ ret(0);
 
   CodeDesc desc;
diff --git a/test/mjsunit/math-floor.js b/test/mjsunit/math-floor.js
new file mode 100644 (file)
index 0000000..e56341c
--- /dev/null
@@ -0,0 +1,118 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --max-new-space-size=262144
+
+function zero() {
+  var x = 0.5;
+  return (function() { return x - 0.5; })();
+}
+
+function test() {
+  assertEquals(0, Math.floor(0));
+  assertEquals(0, Math.floor(zero()));
+  assertEquals(1/-0, 1/Math.floor(-0));  // 0 == -0, so we use reciprocals.
+  assertEquals(Infinity, Math.floor(Infinity));
+  assertEquals(-Infinity, Math.floor(-Infinity));
+  assertNaN(Math.floor(NaN));
+
+  assertEquals(0, Math.floor(0.1));
+  assertEquals(0, Math.floor(0.5));
+  assertEquals(0, Math.floor(0.7));
+  assertEquals(-1, Math.floor(-0.1));
+  assertEquals(-1, Math.floor(-0.5));
+  assertEquals(-1, Math.floor(-0.7));
+  assertEquals(1, Math.floor(1));
+  assertEquals(1, Math.floor(1.1));
+  assertEquals(1, Math.floor(1.5));
+  assertEquals(1, Math.floor(1.7));
+  assertEquals(-1, Math.floor(-1));
+  assertEquals(-2, Math.floor(-1.1));
+  assertEquals(-2, Math.floor(-1.5));
+  assertEquals(-2, Math.floor(-1.7));
+
+  assertEquals(0, Math.floor(Number.MIN_VALUE));
+  assertEquals(-1, Math.floor(-Number.MIN_VALUE));
+  assertEquals(Number.MAX_VALUE, Math.floor(Number.MAX_VALUE));
+  assertEquals(-Number.MAX_VALUE, Math.floor(-Number.MAX_VALUE));
+  assertEquals(Infinity, Math.floor(Infinity));
+  assertEquals(-Infinity, Math.floor(-Infinity));
+
+  // 2^30 is a smi boundary.
+  var two_30 = 1 << 30;
+
+  assertEquals(two_30, Math.floor(two_30));
+  assertEquals(two_30, Math.floor(two_30 + 0.1));
+  assertEquals(two_30, Math.floor(two_30 + 0.5));
+  assertEquals(two_30, Math.floor(two_30 + 0.7));
+
+  assertEquals(two_30 - 1, Math.floor(two_30 - 1));
+  assertEquals(two_30 - 1, Math.floor(two_30 - 1 + 0.1));
+  assertEquals(two_30 - 1, Math.floor(two_30 - 1 + 0.5));
+  assertEquals(two_30 - 1, Math.floor(two_30 - 1 + 0.7));
+
+  assertEquals(-two_30, Math.floor(-two_30));
+  assertEquals(-two_30, Math.floor(-two_30 + 0.1));
+  assertEquals(-two_30, Math.floor(-two_30 + 0.5));
+  assertEquals(-two_30, Math.floor(-two_30 + 0.7));
+
+  assertEquals(-two_30 + 1, Math.floor(-two_30 + 1));
+  assertEquals(-two_30 + 1, Math.floor(-two_30 + 1 + 0.1));
+  assertEquals(-two_30 + 1, Math.floor(-two_30 + 1 + 0.5));
+  assertEquals(-two_30 + 1, Math.floor(-two_30 + 1 + 0.7));
+
+  // 2^52 is a precision boundary.
+  var two_52 = (1 << 30) * (1 << 22);
+
+  assertEquals(two_52, Math.floor(two_52));
+  assertEquals(two_52, Math.floor(two_52 + 0.1));
+  assertEquals(two_52, two_52 + 0.5);
+  assertEquals(two_52, Math.floor(two_52 + 0.5));
+  assertEquals(two_52 + 1, two_52 + 0.7);
+  assertEquals(two_52 + 1, Math.floor(two_52 + 0.7));
+
+  assertEquals(two_52 - 1, Math.floor(two_52 - 1));
+  assertEquals(two_52 - 1, Math.floor(two_52 - 1 + 0.1));
+  assertEquals(two_52 - 1, Math.floor(two_52 - 1 + 0.5));
+  assertEquals(two_52 - 1, Math.floor(two_52 - 1 + 0.7));
+
+  assertEquals(-two_52, Math.floor(-two_52));
+  assertEquals(-two_52, Math.floor(-two_52 + 0.1));
+  assertEquals(-two_52, Math.floor(-two_52 + 0.5));
+  assertEquals(-two_52, Math.floor(-two_52 + 0.7));
+
+  assertEquals(-two_52 + 1, Math.floor(-two_52 + 1));
+  assertEquals(-two_52 + 1, Math.floor(-two_52 + 1 + 0.1));
+  assertEquals(-two_52 + 1, Math.floor(-two_52 + 1 + 0.5));
+  assertEquals(-two_52 + 1, Math.floor(-two_52 + 1 + 0.7));
+}
+
+
+// Test in a loop to cover the custom IC and GC-related issues.
+for (var i = 0; i < 500; i++) {
+  test();
+}