Commit 1982f9d2 authored by vitalyr@chromium.org's avatar vitalyr@chromium.org

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
parent 0012576e
......@@ -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, &not_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(&not_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,
......
......@@ -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, &not_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(&not_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,
......
......@@ -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 {
......
......@@ -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, &not_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(&not_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<int64_t>(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) {
......
......@@ -25,24 +25,74 @@
// (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;
// 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; } }));
}
assertTrue(!isMinusZero(0));
assertTrue(isMinusZero(minusZero));
assertEquals(0, Math.abs(minusZero));
assertTrue(!isMinusZero(Math.abs(minusZero)));
assertTrue(!isMinusZero(Math.abs(0.0)));
// Test in a loop to cover the custom IC and GC-related issues.
for (var i = 0; i < 500; i++) {
test();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment