From 1982f9d257f1ed614516265eeb8c176d4d42df1f Mon Sep 17 00:00:00 2001 From: "vitalyr@chromium.org" Date: Mon, 27 Sep 2010 23:41:41 +0000 Subject: [PATCH] Custom call IC for Math.abs. Review URL: http://codereview.chromium.org/3446024 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5538 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/stub-cache-arm.cc | 102 +++++++++++++++++++++++++++++++++++++++++++ src/ia32/stub-cache-ia32.cc | 103 ++++++++++++++++++++++++++++++++++++++++++++ src/stub-cache.h | 3 +- src/x64/stub-cache-x64.cc | 103 ++++++++++++++++++++++++++++++++++++++++++++ test/mjsunit/abs.js | 48 --------------------- test/mjsunit/math-abs.js | 98 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 408 insertions(+), 49 deletions(-) delete mode 100644 test/mjsunit/abs.js create mode 100644 test/mjsunit/math-abs.js diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 659f29c..97f9495 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -1643,6 +1643,108 @@ Object* CallStubCompiler::CompileMathFloorCall(Object* object, } +Object* CallStubCompiler::CompileMathAbsCall(Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { + // ----------- S t a t e ------------- + // -- r2 : function name + // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based) + // -- ... + // -- sp[argc * 4] : receiver + // ----------------------------------- + + 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) { + __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); + + STATIC_ASSERT(kSmiTag == 0); + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &miss); + + CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, + &miss); + } else { + ASSERT(cell->value() == function); + GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + GenerateLoadFunctionFromCell(cell, function, &miss); + } + + // Load the (only) argument into r0. + __ ldr(r0, MemOperand(sp, 0 * kPointerSize)); + + // Check if the argument is a smi. + Label not_smi; + STATIC_ASSERT(kSmiTag == 0); + __ BranchOnNotSmi(r0, ¬_smi); + + // Do bitwise not or do nothing depending on the sign of the + // argument. + __ eor(r1, r0, Operand(r0, ASR, kBitsPerInt - 1)); + + // Add 1 or do nothing depending on the sign of the argument. + __ sub(r0, r1, Operand(r0, ASR, kBitsPerInt - 1), SetCC); + + // If the result is still negative, go to the slow case. + // This only happens for the most negative smi. + Label slow; + __ b(mi, &slow); + + // Smi case done. + __ Drop(argc + 1); + __ Ret(); + + // Check if the argument is a heap number and load its exponent and + // sign. + __ bind(¬_smi); + __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, true); + __ ldr(r1, FieldMemOperand(r0, HeapNumber::kExponentOffset)); + + // Check the sign of the argument. If the argument is positive, + // just return it. + Label negative_sign; + __ tst(r1, Operand(HeapNumber::kSignMask)); + __ b(ne, &negative_sign); + __ Drop(argc + 1); + __ Ret(); + + // If the argument is negative, clear the sign, and return a new + // number. + __ bind(&negative_sign); + __ eor(r1, r1, Operand(HeapNumber::kSignMask)); + __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); + __ LoadRoot(r6, Heap::kHeapNumberMapRootIndex); + __ AllocateHeapNumber(r0, r4, r5, r6, &slow); + __ str(r1, FieldMemOperand(r0, HeapNumber::kExponentOffset)); + __ str(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset)); + __ Drop(argc + 1); + __ Ret(); + + // 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); + // r2: 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/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index dd0d636..bb0a46c 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -1944,6 +1944,109 @@ Object* CallStubCompiler::CompileMathFloorCall(Object* object, } +Object* CallStubCompiler::CompileMathAbsCall(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 + // ----------------------------------- + + 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 not_smi; + STATIC_ASSERT(kSmiTag == 0); + __ test(eax, Immediate(kSmiTagMask)); + __ j(not_zero, ¬_smi); + + // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0 + // otherwise. + __ mov(ebx, eax); + __ sar(ebx, kBitsPerInt - 1); + + // Do bitwise not or do nothing depending on ebx. + __ xor_(eax, Operand(ebx)); + + // Add 1 or do nothing depending on ebx. + __ sub(eax, Operand(ebx)); + + // If the result is still negative, go to the slow case. + // This only happens for the most negative smi. + Label slow; + __ j(negative, &slow); + + // Smi case done. + __ ret(2 * kPointerSize); + + // Check if the argument is a heap number and load its exponent and + // sign into ebx. + __ bind(¬_smi); + __ CheckMap(eax, Factory::heap_number_map(), &slow, true); + __ mov(ebx, FieldOperand(eax, HeapNumber::kExponentOffset)); + + // Check the sign of the argument. If the argument is positive, + // just return it. + Label negative_sign; + __ test(ebx, Immediate(HeapNumber::kSignMask)); + __ j(not_zero, &negative_sign); + __ ret(2 * kPointerSize); + + // If the argument is negative, clear the sign, and return a new + // number. + __ bind(&negative_sign); + __ and_(ebx, ~HeapNumber::kSignMask); + __ mov(ecx, FieldOperand(eax, HeapNumber::kMantissaOffset)); + __ AllocateHeapNumber(eax, edi, edx, &slow); + __ mov(FieldOperand(eax, HeapNumber::kExponentOffset), ebx); + __ mov(FieldOperand(eax, HeapNumber::kMantissaOffset), ecx); + __ 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.h b/src/stub-cache.h index c47cab7..e4a9e95 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -626,7 +626,8 @@ class KeyedStoreStubCompiler: public StubCompiler { V(String.prototype, charCodeAt, StringCharCodeAt) \ V(String.prototype, charAt, StringCharAt) \ V(String, fromCharCode, StringFromCharCode) \ - V(Math, floor, MathFloor) + V(Math, floor, MathFloor) \ + V(Math, abs, MathAbs) class CallStubCompiler: public StubCompiler { diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 68b18a2..eb48da9 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -1558,6 +1558,109 @@ Object* CallStubCompiler::CompileMathFloorCall(Object* object, } +Object* CallStubCompiler::CompileMathAbsCall(Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { + // ----------- S t a t e ------------- + // -- rcx : function name + // -- rsp[0] : return address + // -- rsp[(argc - n) * 8] : arg[n] (zero-based) + // -- ... + // -- rsp[(argc + 1) * 8] : receiver + // ----------------------------------- + + 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) { + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); + + __ JumpIfSmi(rdx, &miss); + + CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name, + &miss); + } else { + ASSERT(cell->value() == function); + GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + GenerateLoadFunctionFromCell(cell, function, &miss); + } + + // Load the (only) argument into rax. + __ movq(rax, Operand(rsp, 1 * kPointerSize)); + + // Check if the argument is a smi. + Label not_smi; + STATIC_ASSERT(kSmiTag == 0); + __ JumpIfNotSmi(rax, ¬_smi); + __ SmiToInteger32(rax, rax); + + // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0 + // otherwise. + __ movl(rbx, rax); + __ sarl(rbx, Immediate(kBitsPerInt - 1)); + + // Do bitwise not or do nothing depending on ebx. + __ xorl(rax, rbx); + + // Add 1 or do nothing depending on ebx. + __ subl(rax, rbx); + + // If the result is still negative, go to the slow case. + // This only happens for the most negative smi. + Label slow; + __ j(negative, &slow); + + // Smi case done. + __ Integer32ToSmi(rax, rax); + __ ret(2 * kPointerSize); + + // Check if the argument is a heap number and load its value. + __ bind(¬_smi); + __ CheckMap(rax, Factory::heap_number_map(), &slow, true); + __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset)); + + // Check the sign of the argument. If the argument is positive, + // just return it. + Label negative_sign; + const int sign_mask_shift = + (HeapNumber::kExponentOffset - HeapNumber::kValueOffset) * kBitsPerByte; + __ movq(rdi, static_cast(HeapNumber::kSignMask) << sign_mask_shift, + RelocInfo::NONE); + __ testq(rbx, rdi); + __ j(not_zero, &negative_sign); + __ ret(2 * kPointerSize); + + // If the argument is negative, clear the sign, and return a new + // number. We still have the sign mask in rdi. + __ bind(&negative_sign); + __ xor_(rbx, rdi); + __ AllocateHeapNumber(rax, rdx, &slow); + __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rbx); + __ 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); + // rcx: function name. + Object* obj = GenerateMissBranch(); + if (obj->IsFailure()) return obj; + + // Return the generated code. + return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); +} + + Object* CallStubCompiler::CompileCallInterceptor(JSObject* object, JSObject* holder, String* name) { diff --git a/test/mjsunit/abs.js b/test/mjsunit/abs.js deleted file mode 100644 index d1c453c..0000000 --- a/test/mjsunit/abs.js +++ /dev/null @@ -1,48 +0,0 @@ -// 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. - -// Test Math.sin and Math.abs. - -assertEquals(1, Math.abs(1)); // Positive SMI. -assertEquals(1, Math.abs(-1)); // Negative SMI. -assertEquals(0.5, Math.abs(0.5)); // Positive double. -assertEquals(0.5, Math.abs(-0.5)); // Negative double. -assertEquals('Infinity', Math.abs(Number('+Infinity').toString())); -assertEquals('Infinity', Math.abs(Number('-Infinity').toString())); -assertEquals('NaN', Math.abs(NaN).toString()); -assertEquals('NaN', Math.abs(-NaN).toString()); - -var minusZero = 1 / (-1 / 0); -function isMinusZero(x) { - return x === 0 && 1 / x < 0; -} - -assertTrue(!isMinusZero(0)); -assertTrue(isMinusZero(minusZero)); -assertEquals(0, Math.abs(minusZero)); -assertTrue(!isMinusZero(Math.abs(minusZero))); -assertTrue(!isMinusZero(Math.abs(0.0))); diff --git a/test/mjsunit/math-abs.js b/test/mjsunit/math-abs.js new file mode 100644 index 0000000..a40243b --- /dev/null +++ b/test/mjsunit/math-abs.js @@ -0,0 +1,98 @@ +// 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.abs(0)); + assertEquals(0, Math.abs(zero())); + assertEquals(1/0, 1/Math.abs(-0)); // 0 == -0, so we use reciprocals. + assertEquals(Infinity, Math.abs(Infinity)); + assertEquals(Infinity, Math.abs(-Infinity)); + assertNaN(Math.abs(NaN)); + assertNaN(Math.abs(-NaN)); + assertEquals('Infinity', Math.abs(Number('+Infinity').toString())); + assertEquals('Infinity', Math.abs(Number('-Infinity').toString())); + assertEquals('NaN', Math.abs(NaN).toString()); + assertEquals('NaN', Math.abs(-NaN).toString()); + + assertEquals(0.1, Math.abs(0.1)); + assertEquals(0.5, Math.abs(0.5)); + assertEquals(0.1, Math.abs(-0.1)); + assertEquals(0.5, Math.abs(-0.5)); + assertEquals(1, Math.abs(1)); + assertEquals(1.1, Math.abs(1.1)); + assertEquals(1.5, Math.abs(1.5)); + assertEquals(1, Math.abs(-1)); + assertEquals(1.1, Math.abs(-1.1)); + assertEquals(1.5, Math.abs(-1.5)); + + assertEquals(Number.MIN_VALUE, Math.abs(Number.MIN_VALUE)); + assertEquals(Number.MIN_VALUE, Math.abs(-Number.MIN_VALUE)); + assertEquals(Number.MAX_VALUE, Math.abs(Number.MAX_VALUE)); + assertEquals(Number.MAX_VALUE, Math.abs(-Number.MAX_VALUE)); + + // 2^30 is a smi boundary on arm and ia32. + var two_30 = 1 << 30; + + assertEquals(two_30, Math.abs(two_30)); + assertEquals(two_30, Math.abs(-two_30)); + + assertEquals(two_30 + 1, Math.abs(two_30 + 1)); + assertEquals(two_30 + 1, Math.abs(-two_30 - 1)); + + assertEquals(two_30 - 1, Math.abs(two_30 - 1)); + assertEquals(two_30 - 1, Math.abs(-two_30 + 1)); + + // 2^31 is a smi boundary on x64. + var two_31 = 2 * two_30; + + assertEquals(two_31, Math.abs(two_31)); + assertEquals(two_31, Math.abs(-two_31)); + + assertEquals(two_31 + 1, Math.abs(two_31 + 1)); + assertEquals(two_31 + 1, Math.abs(-two_31 - 1)); + + assertEquals(two_31 - 1, Math.abs(two_31 - 1)); + assertEquals(two_31 - 1, Math.abs(-two_31 + 1)); + + assertNaN(Math.abs("not a number")); + assertNaN(Math.abs([1, 2, 3])); + assertEquals(42, Math.abs({valueOf: function() { return 42; } })); + assertEquals(42, Math.abs({valueOf: function() { return -42; } })); +} + + +// Test in a loop to cover the custom IC and GC-related issues. +for (var i = 0; i < 500; i++) { + test(); +} -- 2.7.4