Index: src/x64/code-stubs-x64.cc |
=================================================================== |
--- src/x64/code-stubs-x64.cc (revision 6881) |
+++ src/x64/code-stubs-x64.cc (working copy) |
@@ -1336,54 +1336,33 @@ |
void TypeRecordingBinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) { |
- GenerateRegisterArgsPush(masm); |
+ ASSERT(op_ == Token::ADD); |
+ NearLabel left_not_string, call_runtime; |
+ |
// Registers containing left and right operands respectively. |
- Register lhs = rdx; |
- Register rhs = rax; |
+ Register left = rdx; |
+ Register right = rax; |
- // Test for string arguments before calling runtime. |
- Label not_strings, both_strings, not_string1, string1, string1_smi2; |
+ // Test if left operand is a string. |
+ __ JumpIfSmi(left, &left_not_string); |
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx); |
+ __ j(above_equal, &left_not_string); |
+ StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB); |
+ GenerateRegisterArgsPush(masm); |
+ __ TailCallStub(&string_add_left_stub); |
- __ JumpIfNotString(lhs, r8, ¬_string1); |
+ // Left operand is not a string, test right. |
+ __ bind(&left_not_string); |
+ __ JumpIfSmi(right, &call_runtime); |
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx); |
+ __ j(above_equal, &call_runtime); |
- // First argument is a a string, test second. |
- __ JumpIfSmi(rhs, &string1_smi2); |
- __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9); |
- __ j(above_equal, &string1); |
+ StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB); |
+ GenerateRegisterArgsPush(masm); |
+ __ TailCallStub(&string_add_right_stub); |
- // First and second argument are strings. |
- StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB); |
- __ TailCallStub(&string_add_stub); |
- |
- __ bind(&string1_smi2); |
- // First argument is a string, second is a smi. Try to lookup the number |
- // string for the smi in the number string cache. |
- NumberToStringStub::GenerateLookupNumberStringCache( |
- masm, rhs, rbx, rcx, r8, true, &string1); |
- |
- // Replace second argument on stack and tailcall string add stub to make |
- // the result. |
- __ movq(Operand(rsp, 1 * kPointerSize), rbx); |
- __ TailCallStub(&string_add_stub); |
- |
- // Only first argument is a string. |
- __ bind(&string1); |
- __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION); |
- |
- // First argument was not a string, test second. |
- __ bind(¬_string1); |
- __ JumpIfNotString(rhs, rhs, ¬_strings); |
- |
- // Only second argument is a string. |
- __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION); |
- |
- __ bind(¬_strings); |
// Neither argument is a string. |
- // Pop arguments, because CallRuntimeCode wants to push them again. |
- __ pop(rcx); |
- __ pop(rax); |
- __ pop(rdx); |
- __ push(rcx); |
+ __ bind(&call_runtime); |
} |
@@ -1440,9 +1419,11 @@ |
void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) { |
+ ASSERT(operands_type_ == TRBinaryOpIC::STRING); |
ASSERT(op_ == Token::ADD); |
GenerateStringAddCode(masm); |
- |
+ // Try to add arguments as strings, otherwise, transition to the generic |
+ // TRBinaryOpIC type. |
GenerateTypeTransition(masm); |
} |
@@ -3802,14 +3783,15 @@ |
void StringAddStub::Generate(MacroAssembler* masm) { |
- Label string_add_runtime; |
+ Label string_add_runtime, call_builtin; |
+ Builtins::JavaScript builtin_id = Builtins::ADD; |
// Load the two arguments. |
- __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument. |
- __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument. |
+ __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument (left). |
+ __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument (right). |
// Make sure that both arguments are strings if not known in advance. |
- if (string_check_) { |
+ if (flags_ == NO_STRING_ADD_FLAGS) { |
Condition is_smi; |
is_smi = masm->CheckSmi(rax); |
__ j(is_smi, &string_add_runtime); |
@@ -3821,6 +3803,20 @@ |
__ j(is_smi, &string_add_runtime); |
__ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9); |
__ j(above_equal, &string_add_runtime); |
+ } else { |
+ // Here at least one of the arguments is definitely a string. |
+ // We convert the one that is not known to be a string. |
+ if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) { |
+ ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0); |
+ GenerateConvertArgument(masm, 2 * kPointerSize, rax, rbx, rcx, rdi, |
+ &call_builtin); |
+ builtin_id = Builtins::STRING_ADD_RIGHT; |
+ } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) { |
+ ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0); |
+ GenerateConvertArgument(masm, 1 * kPointerSize, rdx, rbx, rcx, rdi, |
+ &call_builtin); |
+ builtin_id = Builtins::STRING_ADD_LEFT; |
+ } |
} |
// Both arguments are strings. |
@@ -3848,14 +3844,14 @@ |
// rbx: length of first string |
// rcx: length of second string |
// rdx: second string |
- // r8: map of first string if string check was performed above |
- // r9: map of second string if string check was performed above |
+ // r8: map of first string (if flags_ == NO_STRING_ADD_FLAGS) |
+ // r9: map of second string (if flags_ == NO_STRING_ADD_FLAGS) |
Label string_add_flat_result, longer_than_two; |
__ bind(&both_not_zero_length); |
// If arguments where known to be strings, maps are not loaded to r8 and r9 |
// by the code above. |
- if (!string_check_) { |
+ if (flags_ != NO_STRING_ADD_FLAGS) { |
__ movq(r8, FieldOperand(rax, HeapObject::kMapOffset)); |
__ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset)); |
} |
@@ -4041,9 +4037,57 @@ |
// Just jump to runtime to add the two strings. |
__ bind(&string_add_runtime); |
__ TailCallRuntime(Runtime::kStringAdd, 2, 1); |
+ |
+ if (call_builtin.is_linked()) { |
+ __ bind(&call_builtin); |
+ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION); |
+ } |
} |
+void StringAddStub::GenerateConvertArgument(MacroAssembler* masm, |
+ int stack_offset, |
+ Register arg, |
+ Register scratch1, |
+ Register scratch2, |
+ Register scratch3, |
+ Label* slow) { |
+ // First check if the argument is already a string. |
+ Label not_string, done; |
+ __ JumpIfSmi(arg, ¬_string); |
+ __ CmpObjectType(arg, FIRST_NONSTRING_TYPE, scratch1); |
+ __ j(below, &done); |
+ |
+ // Check the number to string cache. |
+ Label not_cached; |
+ __ bind(¬_string); |
+ // Puts the cached result into scratch1. |
+ NumberToStringStub::GenerateLookupNumberStringCache(masm, |
+ arg, |
+ scratch1, |
+ scratch2, |
+ scratch3, |
+ false, |
+ ¬_cached); |
+ __ movq(arg, scratch1); |
+ __ movq(Operand(rsp, stack_offset), arg); |
+ __ jmp(&done); |
+ |
+ // Check if the argument is a safe string wrapper. |
+ __ bind(¬_cached); |
+ __ JumpIfSmi(arg, slow); |
+ __ CmpObjectType(arg, JS_VALUE_TYPE, scratch1); // map -> scratch1. |
+ __ j(not_equal, slow); |
+ __ testb(FieldOperand(scratch1, Map::kBitField2Offset), |
+ Immediate(1 << Map::kStringWrapperSafeForDefaultValueOf)); |
+ __ j(zero, slow); |
+ __ movq(arg, FieldOperand(arg, JSValue::kValueOffset)); |
+ __ movq(Operand(rsp, stack_offset), arg); |
+ |
+ __ bind(&done); |
+} |
+ |
+ |
void StringHelper::GenerateCopyCharacters(MacroAssembler* masm, |
Register dest, |
Register src, |