From 09de997b3504368d40644afa7310b90faff5d09c Mon Sep 17 00:00:00 2001 From: bmeurer Date: Fri, 28 Aug 2015 05:59:51 -0700 Subject: [PATCH] [runtime] Add %ToString and %_ToString and remove the TO_STRING builtin. This adds a new ToString runtime function and a fast-path ToStringStub (which is just a simple dispatcher for existing functionality), and also implements %_ToName using the ToStringStub. R=mstarzinger@chromium.org, yangguo@chromium.org BUG=v8:4307 LOG=n Review URL: https://codereview.chromium.org/1319973007 Cr-Commit-Position: refs/heads/master@{#30442} --- src/arm/builtins-arm.cc | 4 +- src/arm/code-stubs-arm.cc | 43 ++++++++++++---- src/arm/interface-descriptors-arm.cc | 4 ++ src/arm64/builtins-arm64.cc | 4 +- src/arm64/code-stubs-arm64.cc | 32 ++++++++++++ src/arm64/interface-descriptors-arm64.cc | 4 ++ src/code-factory.cc | 7 +++ src/code-factory.h | 1 + src/code-stubs.h | 10 ++++ src/compiler/js-generic-lowering.cc | 4 +- src/compiler/linkage.cc | 1 + src/contexts.h | 3 +- src/full-codegen/arm/full-codegen-arm.cc | 33 ++++++++++++ src/full-codegen/arm64/full-codegen-arm64.cc | 32 ++++++++++++ src/full-codegen/full-codegen.h | 2 + src/full-codegen/ia32/full-codegen-ia32.cc | 34 +++++++++++++ src/full-codegen/mips/full-codegen-mips.cc | 35 +++++++++++++ .../mips64/full-codegen-mips64.cc | 35 +++++++++++++ src/full-codegen/x64/full-codegen-x64.cc | 34 +++++++++++++ src/ia32/builtins-ia32.cc | 4 +- src/ia32/code-stubs-ia32.cc | 35 +++++++++++++ src/ia32/interface-descriptors-ia32.cc | 4 ++ src/interface-descriptors.cc | 7 +++ src/interface-descriptors.h | 11 ++++ src/mips/builtins-mips.cc | 4 +- src/mips/code-stubs-mips.cc | 32 ++++++++++++ src/mips/interface-descriptors-mips.cc | 4 ++ src/mips64/builtins-mips64.cc | 4 +- src/mips64/code-stubs-mips64.cc | 32 ++++++++++++ src/mips64/interface-descriptors-mips64.cc | 4 ++ src/runtime.js | 1 - src/runtime/runtime-object.cc | 11 ++++ src/runtime/runtime.h | 1 + src/x64/builtins-x64.cc | 4 +- src/x64/code-stubs-x64.cc | 35 +++++++++++++ src/x64/interface-descriptors-x64.cc | 4 ++ test/mjsunit/harmony/to-string.js | 50 +++++++++++++++++++ 37 files changed, 544 insertions(+), 25 deletions(-) create mode 100644 test/mjsunit/harmony/to-string.js diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 9186c358b..79f1cda6e 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -235,8 +235,8 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { __ IncrementCounter(counters->string_ctor_conversions(), 1, r3, r4); { FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ push(r0); - __ InvokeBuiltin(Context::TO_STRING_BUILTIN_INDEX, CALL_FUNCTION); + ToStringStub stub(masm->isolate()); + __ CallStub(&stub); } __ pop(function); __ mov(argument, r0); diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 695bf33dc..f28288709 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3232,15 +3232,10 @@ void ToNumberStub::Generate(MacroAssembler* masm) { __ Ret(); __ bind(¬_smi); - Label not_heap_number; - __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset)); - // r0: object - // r1: instance type. - __ cmp(r1, Operand(HEAP_NUMBER_TYPE)); - __ b(ne, ¬_heap_number); - __ Ret(); - __ bind(¬_heap_number); + __ CompareObjectType(r0, r1, r1, HEAP_NUMBER_TYPE); + // r0: receiver + // r1: receiver instance type + __ Ret(eq); Label not_string, slow_string; __ cmp(r1, Operand(FIRST_NONSTRING_TYPE)); @@ -3268,6 +3263,36 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToStringStub::Generate(MacroAssembler* masm) { + // The ToString stub takes one argument in r0. + Label is_number; + __ JumpIfSmi(r0, &is_number); + + __ CompareObjectType(r0, r1, r1, FIRST_NONSTRING_TYPE); + // r0: receiver + // r1: receiver instance type + __ Ret(lo); + + Label not_heap_number; + __ cmp(r1, Operand(HEAP_NUMBER_TYPE)); + __ b(ne, ¬_heap_number); + __ bind(&is_number); + NumberToStringStub stub(isolate()); + __ TailCallStub(&stub); + __ bind(¬_heap_number); + + Label not_oddball; + __ cmp(r1, Operand(ODDBALL_TYPE)); + __ b(ne, ¬_oddball); + __ ldr(r0, FieldMemOperand(r0, Oddball::kToStringOffset)); + __ Ret(); + __ bind(¬_oddball); + + __ push(r0); // Push argument. + __ TailCallRuntime(Runtime::kToString, 1, 1); +} + + void StringHelper::GenerateFlatOneByteStringEquals( MacroAssembler* masm, Register left, Register right, Register scratch1, Register scratch2, Register scratch3) { diff --git a/src/arm/interface-descriptors-arm.cc b/src/arm/interface-descriptors-arm.cc index a59d2c38c..2b4e1d34a 100644 --- a/src/arm/interface-descriptors-arm.cc +++ b/src/arm/interface-descriptors-arm.cc @@ -98,6 +98,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToStringDescriptor::ReceiverRegister() { return r0; } + + // static const Register ToObjectDescriptor::ReceiverRegister() { return r0; } diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 3c51f501c..dc867815b 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -228,8 +228,8 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { __ IncrementCounter(counters->string_ctor_conversions(), 1, x10, x11); { FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(arg); - __ InvokeBuiltin(Context::TO_STRING_BUILTIN_INDEX, CALL_FUNCTION); + ToStringStub stub(masm->isolate()); + __ CallStub(&stub); } __ Pop(function); __ Mov(argument, x0); diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc index 4555a81c5..62d68fdb8 100644 --- a/src/arm64/code-stubs-arm64.cc +++ b/src/arm64/code-stubs-arm64.cc @@ -3999,6 +3999,38 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToStringStub::Generate(MacroAssembler* masm) { + // The ToString stub takes one argument in x0. + Label is_number; + __ JumpIfSmi(x0, &is_number); + + Label not_string; + __ JumpIfObjectType(x0, x1, x1, FIRST_NONSTRING_TYPE, ¬_string, hs); + // x0: receiver + // x1: receiver instance type + __ Ret(); + __ Bind(¬_string); + + Label not_heap_number; + __ Cmp(x1, HEAP_NUMBER_TYPE); + __ B(ne, ¬_heap_number); + __ Bind(&is_number); + NumberToStringStub stub(isolate()); + __ TailCallStub(&stub); + __ Bind(¬_heap_number); + + Label not_oddball; + __ Cmp(x1, ODDBALL_TYPE); + __ B(ne, ¬_oddball); + __ Ldr(x0, FieldMemOperand(x0, Oddball::kToStringOffset)); + __ Ret(); + __ Bind(¬_oddball); + + __ Push(x0); // Push argument. + __ TailCallRuntime(Runtime::kToString, 1, 1); +} + + void StringHelper::GenerateFlatOneByteStringEquals( MacroAssembler* masm, Register left, Register right, Register scratch1, Register scratch2, Register scratch3) { diff --git a/src/arm64/interface-descriptors-arm64.cc b/src/arm64/interface-descriptors-arm64.cc index 8adb48322..632b96b8a 100644 --- a/src/arm64/interface-descriptors-arm64.cc +++ b/src/arm64/interface-descriptors-arm64.cc @@ -99,6 +99,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToStringDescriptor::ReceiverRegister() { return x0; } + + // static const Register ToObjectDescriptor::ReceiverRegister() { return x0; } diff --git a/src/code-factory.cc b/src/code-factory.cc index 6835ba0ed..c3da10532 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -175,6 +175,13 @@ Callable CodeFactory::ToNumber(Isolate* isolate) { } +// static +Callable CodeFactory::ToString(Isolate* isolate) { + ToStringStub stub(isolate); + return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor()); +} + + // static Callable CodeFactory::ToObject(Isolate* isolate) { ToObjectStub stub(isolate); diff --git a/src/code-factory.h b/src/code-factory.h index f813dab1c..22f31a8c7 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -73,6 +73,7 @@ class CodeFactory final { ToBooleanStub::Types types = ToBooleanStub::Types()); static Callable ToNumber(Isolate* isolate); + static Callable ToString(Isolate* isolate); static Callable ToObject(Isolate* isolate); static Callable StringAdd(Isolate* isolate, StringAddFlags flags, diff --git a/src/code-stubs.h b/src/code-stubs.h index a1e015812..f17fa0720 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -54,6 +54,7 @@ namespace internal { V(StubFailureTrampoline) \ V(SubString) \ V(ToNumber) \ + V(ToString) \ V(ToObject) \ V(VectorStoreICTrampoline) \ V(VectorKeyedStoreICTrampoline) \ @@ -3082,6 +3083,15 @@ class ToNumberStub final : public PlatformCodeStub { }; +class ToStringStub final : public PlatformCodeStub { + public: + explicit ToStringStub(Isolate* isolate) : PlatformCodeStub(isolate) {} + + DEFINE_CALL_INTERFACE_DESCRIPTOR(ToString); + DEFINE_PLATFORM_CODE_STUB(ToString, PlatformCodeStub); +}; + + class ToObjectStub final : public HydrogenCodeStub { public: explicit ToObjectStub(Isolate* isolate) : HydrogenCodeStub(isolate) {} diff --git a/src/compiler/js-generic-lowering.cc b/src/compiler/js-generic-lowering.cc index a5f9eeec7..c845130bc 100644 --- a/src/compiler/js-generic-lowering.cc +++ b/src/compiler/js-generic-lowering.cc @@ -296,7 +296,9 @@ void JSGenericLowering::LowerJSToNumber(Node* node) { void JSGenericLowering::LowerJSToString(Node* node) { - ReplaceWithBuiltinCall(node, Context::TO_STRING_BUILTIN_INDEX, 1); + CallDescriptor::Flags flags = AdjustFrameStatesForCall(node); + Callable callable = CodeFactory::ToString(isolate()); + ReplaceWithStubCall(node, callable, flags); } diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index f00c236fd..4b4bcee58 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc @@ -250,6 +250,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) { case Runtime::kInlineToPrimitive_String: case Runtime::kInlineOrdinaryToPrimitive: case Runtime::kInlineToNumber: + case Runtime::kInlineToString: case Runtime::kInlineToName: return 1; case Runtime::kInlineDeoptimizeNow: diff --git a/src/contexts.h b/src/contexts.h index 768702a96..05bc3b55e 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -133,8 +133,7 @@ enum BindingFlags { V(STRING_ADD_LEFT_BUILTIN_INDEX, JSFunction, string_add_left_builtin) \ V(STRING_ADD_RIGHT_BUILTIN_INDEX, JSFunction, string_add_right_builtin) \ V(SUB_BUILTIN_INDEX, JSFunction, sub_builtin) \ - V(SUB_STRONG_BUILTIN_INDEX, JSFunction, sub_strong_builtin) \ - V(TO_STRING_BUILTIN_INDEX, JSFunction, to_string_builtin) + V(SUB_STRONG_BUILTIN_INDEX, JSFunction, sub_strong_builtin) #define NATIVE_CONTEXT_IMPORTED_FIELDS(V) \ diff --git a/src/full-codegen/arm/full-codegen-arm.cc b/src/full-codegen/arm/full-codegen-arm.cc index ca25d6ea5..d63e5b170 100644 --- a/src/full-codegen/arm/full-codegen-arm.cc +++ b/src/full-codegen/arm/full-codegen-arm.cc @@ -3978,6 +3978,39 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToString(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into r0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToStringStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(r0); +} + + +void FullCodeGenerator::EmitToName(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into r0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + Label convert, done_convert; + __ JumpIfSmi(r0, &convert); + STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); + __ CompareObjectType(r0, r1, r1, LAST_NAME_TYPE); + __ b(ls, &done_convert); + __ bind(&convert); + ToStringStub stub(isolate()); + __ CallStub(&stub); + __ bind(&done_convert); + context()->Plug(r0); +} + + void FullCodeGenerator::EmitToObject(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/arm64/full-codegen-arm64.cc b/src/full-codegen/arm64/full-codegen-arm64.cc index 55ac3f5f9..0dc3ffd30 100644 --- a/src/full-codegen/arm64/full-codegen-arm64.cc +++ b/src/full-codegen/arm64/full-codegen-arm64.cc @@ -3689,6 +3689,38 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToString(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into x0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToStringStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(x0); +} + + +void FullCodeGenerator::EmitToName(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into x0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + Label convert, done_convert; + __ JumpIfSmi(x0, &convert); + STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); + __ JumpIfObjectType(x0, x1, x1, LAST_NAME_TYPE, &done_convert, ls); + __ Bind(&convert); + ToStringStub stub(isolate()); + __ CallStub(&stub); + __ Bind(&done_convert); + context()->Plug(x0); +} + + void FullCodeGenerator::EmitToObject(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/full-codegen.h b/src/full-codegen/full-codegen.h index 5ec1d6831..f7651f1c0 100644 --- a/src/full-codegen/full-codegen.h +++ b/src/full-codegen/full-codegen.h @@ -527,6 +527,8 @@ class FullCodeGenerator: public AstVisitor { F(RegExpExec) \ F(RegExpConstructResult) \ F(NumberToString) \ + F(ToString) \ + F(ToName) \ F(ToObject) \ F(DebugIsActive) diff --git a/src/full-codegen/ia32/full-codegen-ia32.cc b/src/full-codegen/ia32/full-codegen-ia32.cc index 1cf1416bd..ef23c545f 100644 --- a/src/full-codegen/ia32/full-codegen-ia32.cc +++ b/src/full-codegen/ia32/full-codegen-ia32.cc @@ -3883,6 +3883,40 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToString(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into eax and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToStringStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(eax); +} + + +void FullCodeGenerator::EmitToName(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into eax and convert it. + VisitForAccumulatorValue(args->at(0)); + + // Convert the object to a name. + Label convert, done_convert; + __ JumpIfSmi(eax, &convert, Label::kNear); + STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); + __ CmpObjectType(eax, LAST_NAME_TYPE, ecx); + __ j(below_equal, &done_convert, Label::kNear); + __ bind(&convert); + ToStringStub stub(isolate()); + __ CallStub(&stub); + __ bind(&done_convert); + context()->Plug(eax); +} + + void FullCodeGenerator::EmitToObject(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/mips/full-codegen-mips.cc b/src/full-codegen/mips/full-codegen-mips.cc index 9baeff034..dde04b63d 100644 --- a/src/full-codegen/mips/full-codegen-mips.cc +++ b/src/full-codegen/mips/full-codegen-mips.cc @@ -3990,6 +3990,41 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToString(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into a0 and convert it. + VisitForAccumulatorValue(args->at(0)); + __ mov(a0, result_register()); + + ToStringStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(v0); +} + + +void FullCodeGenerator::EmitToName(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into v0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + Label convert, done_convert; + __ JumpIfSmi(v0, &convert); + STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); + __ GetObjectType(v0, a1, a1); + __ Branch(&done_convert, le, a1, Operand(LAST_NAME_TYPE)); + __ bind(&convert); + ToStringStub stub(isolate()); + __ mov(a0, v0); + __ CallStub(&stub); + __ bind(&done_convert); + context()->Plug(v0); +} + + void FullCodeGenerator::EmitToObject(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/mips64/full-codegen-mips64.cc b/src/full-codegen/mips64/full-codegen-mips64.cc index 10e458041..048e01c15 100644 --- a/src/full-codegen/mips64/full-codegen-mips64.cc +++ b/src/full-codegen/mips64/full-codegen-mips64.cc @@ -3993,6 +3993,41 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToString(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into a0 and convert it. + VisitForAccumulatorValue(args->at(0)); + __ mov(a0, result_register()); + + ToStringStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(v0); +} + + +void FullCodeGenerator::EmitToName(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into v0 and convert it. + VisitForAccumulatorValue(args->at(0)); + + Label convert, done_convert; + __ JumpIfSmi(v0, &convert); + STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); + __ GetObjectType(v0, a1, a1); + __ Branch(&done_convert, le, a1, Operand(LAST_NAME_TYPE)); + __ bind(&convert); + ToStringStub stub(isolate()); + __ mov(a0, v0); + __ CallStub(&stub); + __ bind(&done_convert); + context()->Plug(v0); +} + + void FullCodeGenerator::EmitToObject(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/full-codegen/x64/full-codegen-x64.cc b/src/full-codegen/x64/full-codegen-x64.cc index d521fcd97..1b1fab8d5 100644 --- a/src/full-codegen/x64/full-codegen-x64.cc +++ b/src/full-codegen/x64/full-codegen-x64.cc @@ -3873,6 +3873,40 @@ void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { } +void FullCodeGenerator::EmitToString(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into rax and convert it. + VisitForAccumulatorValue(args->at(0)); + + ToStringStub stub(isolate()); + __ CallStub(&stub); + context()->Plug(rax); +} + + +void FullCodeGenerator::EmitToName(CallRuntime* expr) { + ZoneList* args = expr->arguments(); + DCHECK_EQ(1, args->length()); + + // Load the argument into rax and convert it. + VisitForAccumulatorValue(args->at(0)); + + // Convert the object to a name. + Label convert, done_convert; + __ JumpIfSmi(rax, &convert, Label::kNear); + STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE); + __ CmpObjectType(rax, LAST_NAME_TYPE, rcx); + __ j(below_equal, &done_convert, Label::kNear); + __ bind(&convert); + ToStringStub stub(isolate()); + __ CallStub(&stub); + __ bind(&done_convert); + context()->Plug(rax); +} + + void FullCodeGenerator::EmitToObject(CallRuntime* expr) { ZoneList* args = expr->arguments(); DCHECK_EQ(1, args->length()); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index db3c6ef71..672d69829 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -1527,8 +1527,8 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); __ push(edi); // Preserve the function. - __ push(eax); - __ InvokeBuiltin(Context::TO_STRING_BUILTIN_INDEX, CALL_FUNCTION); + ToStringStub stub(masm->isolate()); + __ CallStub(&stub); __ pop(edi); } __ mov(ebx, eax); diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index bfabb1f78..7ec98ba93 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -3275,6 +3275,41 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToStringStub::Generate(MacroAssembler* masm) { + // The ToString stub takes one argument in eax. + Label is_number; + __ JumpIfSmi(eax, &is_number, Label::kNear); + + Label not_string; + __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edi); + // eax: receiver + // edi: receiver map + __ j(above_equal, ¬_string, Label::kNear); + __ Ret(); + __ bind(¬_string); + + Label not_heap_number; + __ CompareMap(eax, masm->isolate()->factory()->heap_number_map()); + __ j(not_equal, ¬_heap_number, Label::kNear); + __ bind(&is_number); + NumberToStringStub stub(isolate()); + __ TailCallStub(&stub); + __ bind(¬_heap_number); + + Label not_oddball; + __ CmpInstanceType(edi, ODDBALL_TYPE); + __ j(not_equal, ¬_oddball, Label::kNear); + __ mov(eax, FieldOperand(eax, Oddball::kToStringOffset)); + __ Ret(); + __ bind(¬_oddball); + + __ pop(ecx); // Pop return address. + __ push(eax); // Push argument. + __ push(ecx); // Push return address. + __ TailCallRuntime(Runtime::kToString, 1, 1); +} + + void StringHelper::GenerateFlatOneByteStringEquals(MacroAssembler* masm, Register left, Register right, diff --git a/src/ia32/interface-descriptors-ia32.cc b/src/ia32/interface-descriptors-ia32.cc index 107e6290d..1c0402f11 100644 --- a/src/ia32/interface-descriptors-ia32.cc +++ b/src/ia32/interface-descriptors-ia32.cc @@ -106,6 +106,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToStringDescriptor::ReceiverRegister() { return eax; } + + // static const Register ToObjectDescriptor::ReceiverRegister() { return eax; } diff --git a/src/interface-descriptors.cc b/src/interface-descriptors.cc index 913847bc1..e9bafafba 100644 --- a/src/interface-descriptors.cc +++ b/src/interface-descriptors.cc @@ -162,6 +162,13 @@ void InstanceOfDescriptor::InitializePlatformSpecific( } +void ToStringDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = {ReceiverRegister()}; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + + void ToObjectDescriptor::InitializePlatformSpecific( CallInterfaceDescriptorData* data) { Register registers[] = {ReceiverRegister()}; diff --git a/src/interface-descriptors.h b/src/interface-descriptors.h index 2e77930be..1168fe9d9 100644 --- a/src/interface-descriptors.h +++ b/src/interface-descriptors.h @@ -25,6 +25,7 @@ class PlatformInterfaceDescriptor; V(FastNewClosure) \ V(FastNewContext) \ V(ToNumber) \ + V(ToString) \ V(ToObject) \ V(NumberToString) \ V(Typeof) \ @@ -363,6 +364,16 @@ class ToNumberDescriptor : public CallInterfaceDescriptor { }; +class ToStringDescriptor : public CallInterfaceDescriptor { + public: + enum ParameterIndices { kReceiverIndex }; + + DECLARE_DESCRIPTOR(ToStringDescriptor, CallInterfaceDescriptor) + + static const Register ReceiverRegister(); +}; + + class ToObjectDescriptor : public CallInterfaceDescriptor { public: enum ParameterIndices { kReceiverIndex }; diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index f64266093..ca15f265c 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -240,8 +240,8 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { __ IncrementCounter(counters->string_ctor_conversions(), 1, a3, t0); { FrameScope scope(masm, StackFrame::INTERNAL); - __ push(a0); - __ InvokeBuiltin(Context::TO_STRING_BUILTIN_INDEX, CALL_FUNCTION); + ToStringStub stub(masm->isolate()); + __ CallStub(&stub); } __ pop(function); __ mov(argument, v0); diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index 039268dcb..e20556c98 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -3417,6 +3417,38 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToStringStub::Generate(MacroAssembler* masm) { + // The ToString stub takes on argument in a0. + Label is_number; + __ JumpIfSmi(a0, &is_number); + + Label not_string; + __ GetObjectType(a0, a1, a1); + // a0: receiver + // a1: receiver instance type + __ Branch(¬_string, ge, a1, Operand(FIRST_NONSTRING_TYPE)); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); + __ bind(¬_string); + + Label not_heap_number; + __ Branch(¬_heap_number, ne, a1, Operand(HEAP_NUMBER_TYPE)); + __ bind(&is_number); + NumberToStringStub stub(isolate()); + __ TailCallStub(&stub); + __ bind(¬_heap_number); + + Label not_oddball; + __ Branch(¬_oddball, ne, a1, Operand(ODDBALL_TYPE)); + __ Ret(USE_DELAY_SLOT); + __ lw(v0, FieldMemOperand(a0, Oddball::kToStringOffset)); + __ bind(¬_oddball); + + __ push(a0); // Push argument. + __ TailCallRuntime(Runtime::kToString, 1, 1); +} + + void StringHelper::GenerateFlatOneByteStringEquals( MacroAssembler* masm, Register left, Register right, Register scratch1, Register scratch2, Register scratch3) { diff --git a/src/mips/interface-descriptors-mips.cc b/src/mips/interface-descriptors-mips.cc index 7edc606dc..2f6bf5ce6 100644 --- a/src/mips/interface-descriptors-mips.cc +++ b/src/mips/interface-descriptors-mips.cc @@ -98,6 +98,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToStringDescriptor::ReceiverRegister() { return a0; } + + // static const Register ToObjectDescriptor::ReceiverRegister() { return a0; } diff --git a/src/mips64/builtins-mips64.cc b/src/mips64/builtins-mips64.cc index 1fc04a4f6..11bc8516a 100644 --- a/src/mips64/builtins-mips64.cc +++ b/src/mips64/builtins-mips64.cc @@ -239,8 +239,8 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { __ IncrementCounter(counters->string_ctor_conversions(), 1, a3, a4); { FrameScope scope(masm, StackFrame::INTERNAL); - __ push(a0); - __ InvokeBuiltin(Context::TO_STRING_BUILTIN_INDEX, CALL_FUNCTION); + ToStringStub stub(masm->isolate()); + __ CallStub(&stub); } __ pop(function); __ mov(argument, v0); diff --git a/src/mips64/code-stubs-mips64.cc b/src/mips64/code-stubs-mips64.cc index bff8717d4..49647a420 100644 --- a/src/mips64/code-stubs-mips64.cc +++ b/src/mips64/code-stubs-mips64.cc @@ -3449,6 +3449,38 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToStringStub::Generate(MacroAssembler* masm) { + // The ToString stub takes on argument in a0. + Label is_number; + __ JumpIfSmi(a0, &is_number); + + Label not_string; + __ GetObjectType(a0, a1, a1); + // a0: receiver + // a1: receiver instance type + __ Branch(¬_string, ge, a1, Operand(FIRST_NONSTRING_TYPE)); + __ Ret(USE_DELAY_SLOT); + __ mov(v0, a0); + __ bind(¬_string); + + Label not_heap_number; + __ Branch(¬_heap_number, ne, a1, Operand(HEAP_NUMBER_TYPE)); + __ bind(&is_number); + NumberToStringStub stub(isolate()); + __ TailCallStub(&stub); + __ bind(¬_heap_number); + + Label not_oddball; + __ Branch(¬_oddball, ne, a1, Operand(ODDBALL_TYPE)); + __ Ret(USE_DELAY_SLOT); + __ ld(v0, FieldMemOperand(a0, Oddball::kToStringOffset)); + __ bind(¬_oddball); + + __ push(a0); // Push argument. + __ TailCallRuntime(Runtime::kToString, 1, 1); +} + + void StringHelper::GenerateFlatOneByteStringEquals( MacroAssembler* masm, Register left, Register right, Register scratch1, Register scratch2, Register scratch3) { diff --git a/src/mips64/interface-descriptors-mips64.cc b/src/mips64/interface-descriptors-mips64.cc index 610275f41..4f896b72e 100644 --- a/src/mips64/interface-descriptors-mips64.cc +++ b/src/mips64/interface-descriptors-mips64.cc @@ -98,6 +98,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToStringDescriptor::ReceiverRegister() { return a0; } + + // static const Register ToObjectDescriptor::ReceiverRegister() { return a0; } diff --git a/src/runtime.js b/src/runtime.js index 1c4faf222..231e83283 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -865,7 +865,6 @@ $toString = ToString; "string_add_right_builtin", STRING_ADD_RIGHT, "sub_builtin", SUB, "sub_strong_builtin", SUB_STRONG, - "to_string_builtin", TO_STRING, ]); %InstallToContext([ diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index cb5b3b685..aa2e9c074 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc @@ -1484,6 +1484,17 @@ RUNTIME_FUNCTION(Runtime_ToNumber) { } +RUNTIME_FUNCTION(Runtime_ToString) { + HandleScope scope(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_HANDLE_CHECKED(Object, input, 0); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, + Object::ToString(isolate, input)); + return *result; +} + + RUNTIME_FUNCTION(Runtime_ToName) { HandleScope scope(isolate); DCHECK_EQ(1, args.length()); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 26f030847..f359e8f0b 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -497,6 +497,7 @@ namespace internal { F(ToPrimitive_String, 1, 1) \ F(OrdinaryToPrimitive, 2, 1) \ F(ToNumber, 1, 1) \ + F(ToString, 1, 1) \ F(ToName, 1, 1) \ F(StrictEquals, 2, 1) \ F(InstanceOf, 2, 1) \ diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 29f4732d7..e550835d4 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -1584,8 +1584,8 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); __ Push(rdi); // Preserve the function. - __ Push(rax); - __ InvokeBuiltin(Context::TO_STRING_BUILTIN_INDEX, CALL_FUNCTION); + ToStringStub stub(masm->isolate()); + __ CallStub(&stub); __ Pop(rdi); } __ movp(rbx, rax); diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index aca21a127..d3b373e95 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -3228,6 +3228,41 @@ void ToNumberStub::Generate(MacroAssembler* masm) { } +void ToStringStub::Generate(MacroAssembler* masm) { + // The ToString stub takes one argument in rax. + Label is_number; + __ JumpIfSmi(rax, &is_number, Label::kNear); + + Label not_string; + __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rdi); + // rax: receiver + // rdi: receiver map + __ j(above_equal, ¬_string, Label::kNear); + __ Ret(); + __ bind(¬_string); + + Label not_heap_number; + __ CompareRoot(rax, Heap::kHeapNumberMapRootIndex); + __ j(not_equal, ¬_heap_number, Label::kNear); + __ bind(&is_number); + NumberToStringStub stub(isolate()); + __ TailCallStub(&stub); + __ bind(¬_heap_number); + + Label not_oddball; + __ CmpInstanceType(rdi, ODDBALL_TYPE); + __ j(not_equal, ¬_oddball, Label::kNear); + __ movp(rax, FieldOperand(rax, Oddball::kToStringOffset)); + __ Ret(); + __ bind(¬_oddball); + + __ PopReturnAddressTo(rcx); // Pop return address. + __ Push(rax); // Push argument. + __ PushReturnAddressFrom(rcx); // Push return address. + __ TailCallRuntime(Runtime::kToString, 1, 1); +} + + void StringHelper::GenerateFlatOneByteStringEquals(MacroAssembler* masm, Register left, Register right, diff --git a/src/x64/interface-descriptors-x64.cc b/src/x64/interface-descriptors-x64.cc index 4532da271..67e4970dc 100644 --- a/src/x64/interface-descriptors-x64.cc +++ b/src/x64/interface-descriptors-x64.cc @@ -106,6 +106,10 @@ void ToNumberDescriptor::InitializePlatformSpecific( } +// static +const Register ToStringDescriptor::ReceiverRegister() { return rax; } + + // static const Register ToObjectDescriptor::ReceiverRegister() { return rax; } diff --git a/test/mjsunit/harmony/to-string.js b/test/mjsunit/harmony/to-string.js new file mode 100644 index 000000000..4f03c466e --- /dev/null +++ b/test/mjsunit/harmony/to-string.js @@ -0,0 +1,50 @@ +// 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("1", %ToString(1)); +assertEquals("1", %_ToString(1)); + +assertEquals("0.5", %ToString(.5)); +assertEquals("0.5", %_ToString(.5)); + +assertEquals("null", %ToString(null)); +assertEquals("null", %_ToString(null)); + +assertEquals("true", %ToString(true)); +assertEquals("true", %_ToString(true)); + +assertEquals("false", %ToString(false)); +assertEquals("false", %_ToString(false)); + +assertEquals("undefined", %ToString(undefined)); +assertEquals("undefined", %_ToString(undefined)); + +assertEquals("random text", %ToString("random text")); +assertEquals("random text", %_ToString("random text")); + +assertThrows(function() { %ToString(Symbol.toPrimitive) }, TypeError); +assertThrows(function() { %_ToString(Symbol.toPrimitive) }, TypeError); + +var a = { toString: function() { return "xyz" }}; +assertEquals("xyz", %ToString(a)); +assertEquals("xyz", %_ToString(a)); + +var b = { valueOf: function() { return 42 }}; +assertEquals("[object Object]", %ToString(b)); +assertEquals("[object Object]", %_ToString(b)); + +var c = { + toString: function() { return "x"}, + valueOf: function() { return 123 } +}; +assertEquals("x", %ToString(c)); +assertEquals("x", %_ToString(c)); + +var d = { + [Symbol.toPrimitive]: function(hint) { return hint } +}; +assertEquals("string", %ToString(d)); +assertEquals("string", %_ToString(d)); -- 2.34.1