| Index: src/x64/builtins-x64.cc
|
| diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
|
| index abf9b56917001bd001adc8a956aba7cb06c4f3eb..4e037ff465f1e5b2ff1e3050520028276d5b23af 100644
|
| --- a/src/x64/builtins-x64.cc
|
| +++ b/src/x64/builtins-x64.cc
|
| @@ -330,10 +330,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
|
| }
|
|
|
| // Store offset of return address for deoptimizer.
|
| - // TODO(849): Once Generate_StringConstructCode doesn't reuse this
|
| - // generator, we can drop the third condition below!
|
| - if (!is_api_function && !count_constructions &&
|
| - masm->isolate()->heap()->construct_stub_deopt_pc_offset() == 0) {
|
| + if (!is_api_function && !count_constructions) {
|
| masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
|
| }
|
|
|
| @@ -1435,9 +1432,130 @@ void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
|
|
|
|
|
| void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
|
| - // TODO(849): implement custom construct stub.
|
| - // Generate a copy of the generic stub for now.
|
| - Generate_JSConstructStubGeneric(masm);
|
| + // ----------- S t a t e -------------
|
| + // -- rax : number of arguments
|
| + // -- rdi : constructor function
|
| + // -- rsp[0] : return address
|
| + // -- rsp[(argc - n) * 8] : arg[n] (zero-based)
|
| + // -- rsp[(argc + 1) * 8] : receiver
|
| + // -----------------------------------
|
| + Counters* counters = masm->isolate()->counters();
|
| + __ IncrementCounter(counters->string_ctor_calls(), 1);
|
| +
|
| + if (FLAG_debug_code) {
|
| + __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, rcx);
|
| + __ cmpq(rdi, rcx);
|
| + __ Assert(equal, "Unexpected String function");
|
| + }
|
| +
|
| + // Load the first argument into rax and get rid of the rest
|
| + // (including the receiver).
|
| + Label no_arguments;
|
| + __ testq(rax, rax);
|
| + __ j(zero, &no_arguments);
|
| + __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
|
| + __ pop(rcx);
|
| + __ lea(rsp, Operand(rsp, rax, times_pointer_size, kPointerSize));
|
| + __ push(rcx);
|
| + __ movq(rax, rbx);
|
| +
|
| + // Lookup the argument in the number to string cache.
|
| + Label not_cached, argument_is_string;
|
| + NumberToStringStub::GenerateLookupNumberStringCache(
|
| + masm,
|
| + rax, // Input.
|
| + rbx, // Result.
|
| + rcx, // Scratch 1.
|
| + rdx, // Scratch 2.
|
| + false, // Input is known to be smi?
|
| + ¬_cached);
|
| + __ IncrementCounter(counters->string_ctor_cached_number(), 1);
|
| + __ bind(&argument_is_string);
|
| +
|
| + // ----------- S t a t e -------------
|
| + // -- rbx : argument converted to string
|
| + // -- rdi : constructor function
|
| + // -- rsp[0] : return address
|
| + // -----------------------------------
|
| +
|
| + // Allocate a JSValue and put the tagged pointer into rax.
|
| + Label gc_required;
|
| + __ AllocateInNewSpace(JSValue::kSize,
|
| + rax, // Result.
|
| + rcx, // New allocation top (we ignore it).
|
| + no_reg,
|
| + &gc_required,
|
| + TAG_OBJECT);
|
| +
|
| + // Set the map.
|
| + __ LoadGlobalFunctionInitialMap(rdi, rcx);
|
| + if (FLAG_debug_code) {
|
| + __ cmpb(FieldOperand(rcx, Map::kInstanceSizeOffset),
|
| + Immediate(JSValue::kSize >> kPointerSizeLog2));
|
| + __ Assert(equal, "Unexpected string wrapper instance size");
|
| + __ cmpb(FieldOperand(rcx, Map::kUnusedPropertyFieldsOffset), Immediate(0));
|
| + __ Assert(equal, "Unexpected unused properties of string wrapper");
|
| + }
|
| + __ movq(FieldOperand(rax, HeapObject::kMapOffset), rcx);
|
| +
|
| + // Set properties and elements.
|
| + __ LoadRoot(rcx, Heap::kEmptyFixedArrayRootIndex);
|
| + __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), rcx);
|
| + __ movq(FieldOperand(rax, JSObject::kElementsOffset), rcx);
|
| +
|
| + // Set the value.
|
| + __ movq(FieldOperand(rax, JSValue::kValueOffset), rbx);
|
| +
|
| + // Ensure the object is fully initialized.
|
| + STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
|
| +
|
| + // We're done. Return.
|
| + __ ret(0);
|
| +
|
| + // The argument was not found in the number to string cache. Check
|
| + // if it's a string already before calling the conversion builtin.
|
| + Label convert_argument;
|
| + __ bind(¬_cached);
|
| + STATIC_ASSERT(kSmiTag == 0);
|
| + __ JumpIfSmi(rax, &convert_argument);
|
| + Condition is_string = masm->IsObjectStringType(rax, rbx, rcx);
|
| + __ j(NegateCondition(is_string), &convert_argument);
|
| + __ movq(rbx, rax);
|
| + __ IncrementCounter(counters->string_ctor_string_value(), 1);
|
| + __ jmp(&argument_is_string);
|
| +
|
| + // Invoke the conversion builtin and put the result into rbx.
|
| + __ bind(&convert_argument);
|
| + __ IncrementCounter(counters->string_ctor_conversions(), 1);
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
| + __ push(rdi); // Preserve the function.
|
| + __ push(rax);
|
| + __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
|
| + __ pop(rdi);
|
| + }
|
| + __ movq(rbx, rax);
|
| + __ jmp(&argument_is_string);
|
| +
|
| + // Load the empty string into rbx, remove the receiver from the
|
| + // stack, and jump back to the case where the argument is a string.
|
| + __ bind(&no_arguments);
|
| + __ LoadRoot(rbx, Heap::kEmptyStringRootIndex);
|
| + __ pop(rcx);
|
| + __ lea(rsp, Operand(rsp, kPointerSize));
|
| + __ push(rcx);
|
| + __ jmp(&argument_is_string);
|
| +
|
| + // At this point the argument is already a string. Call runtime to
|
| + // create a string wrapper.
|
| + __ bind(&gc_required);
|
| + __ IncrementCounter(counters->string_ctor_gc_required(), 1);
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
| + __ push(rbx);
|
| + __ CallRuntime(Runtime::kNewStringWrapper, 1);
|
| + }
|
| + __ ret(0);
|
| }
|
|
|
|
|
|
|