[runtime] Implement %_ToLength via ToLengthStub.

Use %_ToLength for TO_LENGTH, implemented via a ToLengthStub
that supports a fast path for small integers. Everything else is still
handled in the runtime.

CQ_INCLUDE_TRYBOTS=tryserver.v8:v8_linux_nosnap_rel
BUG=v8:4494
LOG=n

Review URL: https://codereview.chromium.org/1412963002

Cr-Commit-Position: refs/heads/master@{#31358}
This commit is contained in:
bmeurer 2015-10-19 01:35:00 -07:00 committed by Commit bot
parent 51cb4481a9
commit e678a0f9a9
28 changed files with 335 additions and 1 deletions

View File

@ -3168,6 +3168,21 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
}
void ToLengthStub::Generate(MacroAssembler* masm) {
// The ToLength stub takes one argument in r0.
Label not_smi;
__ JumpIfNotSmi(r0, &not_smi);
STATIC_ASSERT(kSmiTag == 0);
__ tst(r0, r0);
__ mov(r0, Operand(0), LeaveCC, lt);
__ Ret();
__ bind(&not_smi);
__ push(r0); // Push argument.
__ TailCallRuntime(Runtime::kToLength, 1, 1);
}
void ToStringStub::Generate(MacroAssembler* masm) {
// The ToString stub takes one argument in r0.
Label is_number;

View File

@ -101,6 +101,10 @@ void ToNumberDescriptor::InitializePlatformSpecific(
}
// static
const Register ToLengthDescriptor::ReceiverRegister() { return r0; }
// static
const Register ToStringDescriptor::ReceiverRegister() { return r0; }

View File

@ -3914,6 +3914,21 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
}
void ToLengthStub::Generate(MacroAssembler* masm) {
// The ToLength stub takes one argument in x0.
Label not_smi;
__ JumpIfNotSmi(x0, &not_smi);
STATIC_ASSERT(kSmiTag == 0);
__ Tst(x0, x0);
__ Csel(x0, x0, Operand(0), ge);
__ Ret();
__ Bind(&not_smi);
__ Push(x0); // Push argument.
__ TailCallRuntime(Runtime::kToLength, 1, 1);
}
void ToStringStub::Generate(MacroAssembler* masm) {
// The ToString stub takes one argument in x0.
Label is_number;

View File

@ -102,6 +102,10 @@ void ToNumberDescriptor::InitializePlatformSpecific(
}
// static
const Register ToLengthDescriptor::ReceiverRegister() { return x0; }
// static
const Register ToStringDescriptor::ReceiverRegister() { return x0; }

View File

@ -182,6 +182,13 @@ Callable CodeFactory::ToString(Isolate* isolate) {
}
// static
Callable CodeFactory::ToLength(Isolate* isolate) {
ToLengthStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::ToObject(Isolate* isolate) {
ToObjectStub stub(isolate);

View File

@ -74,6 +74,7 @@ class CodeFactory final {
static Callable ToNumber(Isolate* isolate);
static Callable ToString(Isolate* isolate);
static Callable ToLength(Isolate* isolate);
static Callable ToObject(Isolate* isolate);
static Callable StringAdd(Isolate* isolate, StringAddFlags flags,

View File

@ -52,6 +52,7 @@ namespace internal {
V(StubFailureTrampoline) \
V(SubString) \
V(ToNumber) \
V(ToLength) \
V(ToString) \
V(ToObject) \
V(VectorStoreICTrampoline) \
@ -3076,6 +3077,15 @@ class ToNumberStub final : public PlatformCodeStub {
};
class ToLengthStub final : public PlatformCodeStub {
public:
explicit ToLengthStub(Isolate* isolate) : PlatformCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(ToLength);
DEFINE_PLATFORM_CODE_STUB(ToLength, PlatformCodeStub);
};
class ToStringStub final : public PlatformCodeStub {
public:
explicit ToStringStub(Isolate* isolate) : PlatformCodeStub(isolate) {}

View File

@ -3797,6 +3797,19 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
}
void FullCodeGenerator::EmitToLength(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());
// Load the argument into r0 and convert it.
VisitForAccumulatorValue(args->at(0));
ToLengthStub stub(isolate());
__ CallStub(&stub);
context()->Plug(r0);
}
void FullCodeGenerator::EmitToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());

View File

@ -3507,6 +3507,19 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
}
void FullCodeGenerator::EmitToLength(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());
// Load the argument into x0 and convert it.
VisitForAccumulatorValue(args->at(0));
ToLengthStub stub(isolate());
__ CallStub(&stub);
context()->Plug(x0);
}
void FullCodeGenerator::EmitToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());

View File

@ -518,6 +518,7 @@ class FullCodeGenerator: public AstVisitor {
F(ToInteger) \
F(NumberToString) \
F(ToString) \
F(ToLength) \
F(ToNumber) \
F(ToName) \
F(ToObject) \

View File

@ -3695,6 +3695,19 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
}
void FullCodeGenerator::EmitToLength(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());
// Load the argument into eax and convert it.
VisitForAccumulatorValue(args->at(0));
ToLengthStub stub(isolate());
__ CallStub(&stub);
context()->Plug(eax);
}
void FullCodeGenerator::EmitToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());

View File

@ -3811,6 +3811,20 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
}
void FullCodeGenerator::EmitToLength(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());
// Load the argument into a0 and convert it.
VisitForAccumulatorValue(args->at(0));
__ mov(a0, result_register());
ToLengthStub stub(isolate());
__ CallStub(&stub);
context()->Plug(v0);
}
void FullCodeGenerator::EmitToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());

View File

@ -3814,6 +3814,20 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
}
void FullCodeGenerator::EmitToLength(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());
// Load the argument into a0 and convert it.
VisitForAccumulatorValue(args->at(0));
__ mov(a0, result_register());
ToLengthStub stub(isolate());
__ CallStub(&stub);
context()->Plug(v0);
}
void FullCodeGenerator::EmitToString(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());

View File

@ -3704,6 +3704,19 @@ void FullCodeGenerator::EmitToString(CallRuntime* expr) {
}
void FullCodeGenerator::EmitToLength(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());
// Load the argument into rax and convert it.
VisitForAccumulatorValue(args->at(0));
ToLengthStub stub(isolate());
__ CallStub(&stub);
context()->Plug(rax);
}
void FullCodeGenerator::EmitToNumber(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
DCHECK_EQ(1, args->length());

View File

@ -12285,6 +12285,20 @@ void HOptimizedGraphBuilder::GenerateToString(CallRuntime* call) {
}
void HOptimizedGraphBuilder::GenerateToLength(CallRuntime* call) {
DCHECK_EQ(1, call->arguments()->length());
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
Callable callable = CodeFactory::ToLength(isolate());
HValue* input = Pop();
HValue* stub = Add<HConstant>(callable.code());
HValue* values[] = {context(), input};
HInstruction* result =
New<HCallWithDescriptor>(stub, 0, callable.descriptor(),
Vector<HValue*>(values, arraysize(values)));
return ast_context()->ReturnInstruction(result, call->id());
}
void HOptimizedGraphBuilder::GenerateToNumber(CallRuntime* call) {
DCHECK_EQ(1, call->arguments()->length());
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));

View File

@ -2226,6 +2226,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
F(ToInteger) \
F(ToObject) \
F(ToString) \
F(ToLength) \
F(ToNumber) \
F(IsFunction) \
F(IsSpecObject) \

View File

@ -3192,6 +3192,25 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
}
void ToLengthStub::Generate(MacroAssembler* masm) {
// The ToLength stub takes on argument in eax.
Label not_smi, positive_smi;
__ JumpIfNotSmi(eax, &not_smi, Label::kNear);
STATIC_ASSERT(kSmiTag == 0);
__ test(eax, eax);
__ j(greater_equal, &positive_smi, Label::kNear);
__ xor_(eax, eax);
__ bind(&positive_smi);
__ Ret();
__ bind(&not_smi);
__ pop(ecx); // Pop return address.
__ push(eax); // Push argument.
__ push(ecx); // Push return address.
__ TailCallRuntime(Runtime::kToLength, 1, 1);
}
void ToStringStub::Generate(MacroAssembler* masm) {
// The ToString stub takes one argument in eax.
Label is_number;

View File

@ -105,6 +105,10 @@ void ToNumberDescriptor::InitializePlatformSpecific(
}
// static
const Register ToLengthDescriptor::ReceiverRegister() { return eax; }
// static
const Register ToStringDescriptor::ReceiverRegister() { return eax; }

View File

@ -189,6 +189,13 @@ void StringCompareDescriptor::InitializePlatformSpecific(
}
void ToLengthDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {ReceiverRegister()};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void ToStringDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {ReceiverRegister()};

View File

@ -25,6 +25,7 @@ class PlatformInterfaceDescriptor;
V(FastNewClosure) \
V(FastNewContext) \
V(ToNumber) \
V(ToLength) \
V(ToString) \
V(ToObject) \
V(NumberToString) \
@ -373,6 +374,16 @@ class ToNumberDescriptor : public CallInterfaceDescriptor {
};
class ToLengthDescriptor : public CallInterfaceDescriptor {
public:
enum ParameterIndices { kReceiverIndex };
DECLARE_DESCRIPTOR(ToLengthDescriptor, CallInterfaceDescriptor)
static const Register ReceiverRegister();
};
class ToStringDescriptor : public CallInterfaceDescriptor {
public:
enum ParameterIndices { kReceiverIndex };

View File

@ -146,7 +146,7 @@ macro TO_INTEGER(arg) = (%_ToInteger(arg));
macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(arg));
macro TO_INT32(arg) = ((arg) | 0);
macro TO_UINT32(arg) = ((arg) >>> 0);
macro TO_LENGTH(arg) = (%ToLength(arg));
macro TO_LENGTH(arg) = (%_ToLength(arg));
macro TO_LENGTH_OR_UINT32(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_UINT32(arg));
macro TO_LENGTH_OR_INTEGER(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_INTEGER(arg));
macro TO_STRING(arg) = (%_ToString(arg));

View File

@ -3314,6 +3314,23 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
}
void ToLengthStub::Generate(MacroAssembler* masm) {
// The ToLength stub takes on argument in a0.
Label not_smi, positive_smi;
__ JumpIfNotSmi(a0, &not_smi);
STATIC_ASSERT(kSmiTag == 0);
__ Branch(&positive_smi, ge, a0, Operand(zero_reg));
__ mov(a0, zero_reg);
__ bind(&positive_smi);
__ Ret(USE_DELAY_SLOT);
__ mov(v0, a0);
__ bind(&not_smi);
__ push(a0); // Push argument.
__ TailCallRuntime(Runtime::kToLength, 1, 1);
}
void ToStringStub::Generate(MacroAssembler* masm) {
// The ToString stub takes on argument in a0.
Label is_number;

View File

@ -99,6 +99,10 @@ void ToNumberDescriptor::InitializePlatformSpecific(
}
// static
const Register ToLengthDescriptor::ReceiverRegister() { return a0; }
// static
const Register ToStringDescriptor::ReceiverRegister() { return a0; }

View File

@ -3348,6 +3348,23 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
}
void ToLengthStub::Generate(MacroAssembler* masm) {
// The ToLength stub takes on argument in a0.
Label not_smi, positive_smi;
__ JumpIfNotSmi(a0, &not_smi);
STATIC_ASSERT(kSmiTag == 0);
__ Branch(&positive_smi, ge, a0, Operand(zero_reg));
__ mov(a0, zero_reg);
__ bind(&positive_smi);
__ Ret(USE_DELAY_SLOT);
__ mov(v0, a0);
__ bind(&not_smi);
__ push(a0); // Push argument.
__ TailCallRuntime(Runtime::kToLength, 1, 1);
}
void ToStringStub::Generate(MacroAssembler* masm) {
// The ToString stub takes on argument in a0.
Label is_number;

View File

@ -99,6 +99,10 @@ void ToNumberDescriptor::InitializePlatformSpecific(
}
// static
const Register ToLengthDescriptor::ReceiverRegister() { return a0; }
// static
const Register ToStringDescriptor::ReceiverRegister() { return a0; }

View File

@ -3122,6 +3122,25 @@ void ToNumberStub::Generate(MacroAssembler* masm) {
}
void ToLengthStub::Generate(MacroAssembler* masm) {
// The ToLength stub takes on argument in rax.
Label not_smi, positive_smi;
__ JumpIfNotSmi(rax, &not_smi, Label::kNear);
STATIC_ASSERT(kSmiTag == 0);
__ testp(rax, rax);
__ j(greater_equal, &positive_smi, Label::kNear);
__ xorl(rax, rax);
__ bind(&positive_smi);
__ Ret();
__ bind(&not_smi);
__ PopReturnAddressTo(rcx); // Pop return address.
__ Push(rax); // Push argument.
__ PushReturnAddressFrom(rcx); // Push return address.
__ TailCallRuntime(Runtime::kToLength, 1, 1);
}
void ToStringStub::Generate(MacroAssembler* masm) {
// The ToString stub takes one argument in rax.
Label is_number;

View File

@ -107,6 +107,10 @@ void ToNumberDescriptor::InitializePlatformSpecific(
}
// static
const Register ToLengthDescriptor::ReceiverRegister() { return rax; }
// static
const Register ToStringDescriptor::ReceiverRegister() { return rax; }

View File

@ -0,0 +1,76 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
assertEquals(0, %ToLength(NaN));
assertEquals(0, %_ToLength(NaN));
assertEquals(0, %ToLength(-Infinity));
assertEquals(0, %_ToLength(-Infinity));
assertEquals(0, %ToLength(0));
assertEquals(0, %_ToLength(0));
assertEquals(0, %ToLength(.5));
assertEquals(0, %_ToLength(.5));
assertEquals(42, %ToLength(42.99999));
assertEquals(42, %_ToLength(42.99999));
assertEquals(9007199254740991, %ToLength(9007199254740991));
assertEquals(9007199254740991, %_ToLength(9007199254740991));
assertEquals(9007199254740991, %ToLength(Infinity));
assertEquals(9007199254740991, %_ToLength(Infinity));
assertEquals(0, %ToLength(null));
assertEquals(0, %_ToLength(null));
assertEquals(1, %ToLength(true));
assertEquals(1, %_ToLength(true));
assertEquals(0, %ToLength(false));
assertEquals(0, %_ToLength(false));
assertEquals(0, %ToLength(undefined));
assertEquals(0, %_ToLength(undefined));
assertEquals(0, %ToLength("-1"));
assertEquals(0, %_ToLength("-1"));
assertEquals(123, %ToLength("123"));
assertEquals(123, %_ToLength("123"));
assertEquals(0, %ToLength("random text"));
assertEquals(0, %_ToLength("random text"));
assertThrows(function() { %ToLength(Symbol.toPrimitive) }, TypeError);
assertThrows(function() { %_ToLength(Symbol.toPrimitive) }, TypeError);
var a = { toString: function() { return 54321 }};
assertEquals(54321, %ToLength(a));
assertEquals(54321, %_ToLength(a));
var b = { valueOf: function() { return 42 }};
assertEquals(42, %ToLength(b));
assertEquals(42, %_ToLength(b));
var c = {
toString: function() { return "x"},
valueOf: function() { return 123 }
};
assertEquals(123, %ToLength(c));
assertEquals(123, %_ToLength(c));
var d = {
[Symbol.toPrimitive]: function(hint) {
assertEquals("number", hint);
return 987654321;
}
};
assertEquals(987654321, %ToLength(d));
assertEquals(987654321, %_ToLength(d));
var e = new Date(0);
assertEquals(0, %ToLength(e));
assertEquals(0, %_ToLength(e));