From a6bbe9820e99a879f264e5598c1df95ab6b575ed Mon Sep 17 00:00:00 2001 From: "vitalyr@chromium.org" Date: Tue, 21 Sep 2010 12:54:12 +0000 Subject: [PATCH] Custom call IC for Math.floor. Review URL: http://codereview.chromium.org/3327022 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5499 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/stub-cache-arm.cc | 10 ++++ src/bootstrapper.cc | 45 ++++++++------ src/ia32/assembler-ia32.cc | 47 ++++++++++++++- src/ia32/assembler-ia32.h | 8 +++ src/ia32/disasm-ia32.cc | 44 +++++++++++++- src/ia32/macro-assembler-ia32.cc | 11 ++++ src/ia32/macro-assembler-ia32.h | 2 + src/ia32/stub-cache-ia32.cc | 125 +++++++++++++++++++++++++++++++++++++++ src/stub-cache.cc | 2 +- src/stub-cache.h | 42 ++++++------- src/x64/stub-cache-x64.cc | 10 ++++ test/cctest/test-disasm-ia32.cc | 18 ++++++ test/mjsunit/math-floor.js | 118 ++++++++++++++++++++++++++++++++++++ 13 files changed, 437 insertions(+), 45 deletions(-) create mode 100644 test/mjsunit/math-floor.js diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 87a7fb9..070e352 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -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, diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 6e6c2c6..ff68572 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1344,33 +1344,40 @@ bool Genesis::InstallNatives() { } -static void InstallCustomCallGenerator( - Handle holder_function, - CallStubCompiler::CustomGeneratorOwner owner_flag, - const char* function_name, - int id) { - Handle owner; - if (owner_flag == CallStubCompiler::FUNCTION) { - owner = Handle::cast(holder_function); - } else { - ASSERT(owner_flag == CallStubCompiler::INSTANCE_PROTOTYPE); - owner = Handle( - JSObject::cast(holder_function->instance_prototype())); +static Handle ResolveCustomCallGeneratorHolder( + Handle global_context, + const char* holder_expr) { + Handle global(global_context->global()); + const char* period_pos = strchr(holder_expr, '.'); + if (period_pos == NULL) { + return Handle::cast( + GetProperty(global, Factory::LookupAsciiSymbol(holder_expr))); } + ASSERT_EQ(".prototype", period_pos); + Vector property(holder_expr, period_pos - holder_expr); + Handle function = Handle::cast( + GetProperty(global, Factory::LookupSymbol(property))); + return Handle(JSObject::cast(function->prototype())); +} + + +static void InstallCustomCallGenerator(Handle holder, + const char* function_name, + int id) { Handle name = Factory::LookupAsciiSymbol(function_name); - Handle function(JSFunction::cast(owner->GetProperty(*name))); + Handle 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 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 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 diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index f8a1593..fcb973d 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -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); diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index e0d7424..265c82d 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -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); diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc index 9f3e8d8..207648b 100644 --- a/src/ia32/disasm-ia32.cc +++ b/src/ia32/disasm-ia32.cc @@ -958,6 +958,14 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector 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, ®op, &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 out_buffer, NameOfXMMRegister(regop), NameOfXMMRegister(rm)); data++; + } else if (*data == 0x73) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + int8_t imm8 = static_cast(data[1]); + AppendToBuffer("psllq %s,%d", + NameOfXMMRegister(rm), + static_cast(imm8)); + data += 2; + } else if (*data == 0x54) { + data++; + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + AppendToBuffer("andpd %s,%s", + NameOfXMMRegister(regop), + NameOfXMMRegister(rm)); + data++; } else { UnimplementedInstruction(); } @@ -1275,6 +1300,23 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector 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 out_buffer, " %s", tmp_buffer_.start()); return instr_len; -} +} // NOLINT (function is too long) //------------------------------------------------------------------------------ diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 87e25d7..f8dabd5 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -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, diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index a7534cb..aa7caf5 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -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); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index e2af9d4..672d8c7 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -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, diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 34989d3..af7c0bd 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -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, \ diff --git a/src/stub-cache.h b/src/stub-cache.h index 7e44fd0..c47cab7 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -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 diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index df745cc..75956eb 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -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) { diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc index 25d2ec0..71735bc 100644 --- a/test/cctest/test-disasm-ia32.cc +++ b/test/cctest/test-disasm-ia32.cc @@ -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 index 0000000..e56341c --- /dev/null +++ b/test/mjsunit/math-floor.js @@ -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(); +} -- 2.7.4