diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index f0cf4f1175..3d7af82ad0 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -231,7 +231,8 @@ enum ScaleFactor { times_8 = 3, times_int_size = times_4, times_half_pointer_size = times_2, - times_pointer_size = times_4 + times_pointer_size = times_4, + times_twice_pointer_size = times_8 }; diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index f1964595cb..9d7319c752 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -7639,7 +7639,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { switch (op_) { case Token::ADD: { // Test for string arguments before calling runtime. - Label not_strings, not_string1, string1; + Label not_strings, not_string1, string1, string1_smi2; Result answer; __ test(edx, Immediate(kSmiTagMask)); __ j(zero, ¬_string1); @@ -7648,15 +7648,56 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { // First argument is a string, test second. __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &string1); + __ j(zero, &string1_smi2); __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx); __ j(above_equal, &string1); // First and second argument are strings. Jump to the string add stub. - StringAddStub stub(NO_STRING_CHECK_IN_STUB); - __ TailCallStub(&stub); + 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. + // Load the number string cache. + ExternalReference roots_address = ExternalReference::roots_address(); + __ mov(ecx, Immediate(Heap::kNumberStringCacheRootIndex)); + __ mov(ebx, + Operand::StaticArray(ecx, times_pointer_size, roots_address)); + // Make the hash mask from the length of the number string cache. It + // contains two elements (number and string) for each cache entry. + __ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset)); + __ shr(ecx, 1); // Divide length by two (length is not a smi). + __ sub(Operand(ecx), Immediate(1)); // Make mask. + // Calculate the entry in the number string cache. The hash value in the + // number string cache for smis is just the smi value. + __ mov(edi, eax); + __ SmiUntag(edi); + __ and_(edi, Operand(ecx)); + // Check if the entry is the smi we are looking for. + __ cmp(eax, + FieldOperand(ebx, + edi, + times_twice_pointer_size, + FixedArray::kHeaderSize)); + __ IncrementCounter(equal, &Counters::string_plus_smi_hit, 1); + __ IncrementCounter(not_equal, &Counters::string_plus_smi_miss, 1); + __ j(not_equal, &string1); + + // Get the string from the cache and call the string add stub to make the + // result. + __ mov(edi, + FieldOperand(ebx, + edi, + times_twice_pointer_size, + FixedArray::kHeaderSize + kPointerSize)); + __ EnterInternalFrame(); + __ push(edx); // Original first argument. + __ push(edi); // Number to string result for second argument. + __ CallStub(&string_add_stub); + __ LeaveInternalFrame(); + __ ret(2 * kPointerSize); - // Only first argument is a string. __ bind(&string1); __ InvokeBuiltin( HasArgsReversed() ? diff --git a/src/v8-counters.h b/src/v8-counters.h index 3d5e84527c..60b52fcfaf 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -167,7 +167,9 @@ namespace internal { SC(string_compare_native, V8.StringCompareNative) \ SC(string_compare_runtime, V8.StringCompareRuntime) \ SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \ - SC(regexp_entry_native, V8.RegExpEntryNative) + SC(regexp_entry_native, V8.RegExpEntryNative) \ + SC(string_plus_smi_hit, V8.StringPlusSmiHit) \ + SC(string_plus_smi_miss, V8.StringPlusSmiMiss) // This file contains all the v8 counters that are in use. class Counters : AllStatic {