From ee1dc27795d4f1a75c6bab9429f62b8cf6191328 Mon Sep 17 00:00:00 2001 From: "vitalyr@chromium.org" Date: Thu, 26 Aug 2010 13:59:37 +0000 Subject: [PATCH] Fast string construct stub (ia32 only for now). Review URL: http://codereview.chromium.org/3211002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5357 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/builtins-arm.cc | 7 ++ src/bootstrapper.cc | 2 + src/builtins.h | 7 +- src/ia32/builtins-ia32.cc | 141 ++++++++++++++++++++++++++++--- src/ia32/macro-assembler-ia32.cc | 24 ++++++ src/ia32/macro-assembler-ia32.h | 7 ++ src/ia32/stub-cache-ia32.cc | 12 +-- src/runtime.cc | 8 ++ src/runtime.h | 1 + src/v8-counters.h | 5 ++ src/x64/builtins-x64.cc | 7 ++ 11 files changed, 197 insertions(+), 24 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index a279687a76..a902fc21d6 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -481,6 +481,13 @@ 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); +} + + void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : number of arguments diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index fd53673a55..a82d1d6966 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -720,6 +720,8 @@ void Genesis::InitializeGlobal(Handle inner_global, InstallFunction(global, "String", JS_VALUE_TYPE, JSValue::kSize, Top::initial_object_prototype(), Builtins::Illegal, true); + string_fun->shared()->set_construct_stub( + Builtins::builtin(Builtins::StringConstructCode)); global_context()->set_string_function(*string_fun); // Add 'length' property to strings. Handle string_descriptors = diff --git a/src/builtins.h b/src/builtins.h index 375e8f3f89..7e49f3133a 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -117,7 +117,10 @@ enum BuiltinExtraArguments { V(FunctionApply, BUILTIN, UNINITIALIZED) \ \ V(ArrayCode, BUILTIN, UNINITIALIZED) \ - V(ArrayConstructCode, BUILTIN, UNINITIALIZED) + V(ArrayConstructCode, BUILTIN, UNINITIALIZED) \ + \ + V(StringConstructCode, BUILTIN, UNINITIALIZED) + #ifdef ENABLE_DEBUGGER_SUPPORT // Define list of builtins used by the debugger implemented in assembly. @@ -258,6 +261,8 @@ class Builtins : public AllStatic { static void Generate_ArrayCode(MacroAssembler* masm); static void Generate_ArrayConstructCode(MacroAssembler* masm); + + static void Generate_StringConstructCode(MacroAssembler* masm); }; } } // namespace v8::internal diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 1c2eb94cf5..11c419a55f 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -29,6 +29,7 @@ #if defined(V8_TARGET_ARCH_IA32) +#include "code-stubs-ia32.h" #include "codegen-inl.h" namespace v8 { @@ -695,17 +696,6 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { } -// Load the built-in Array function from the current context. -static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { - // Load the global context. - __ mov(result, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); - __ mov(result, FieldOperand(result, GlobalObject::kGlobalContextOffset)); - // Load the Array function from the global context. - __ mov(result, - Operand(result, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); -} - - // Number of empty elements to allocate for an empty array. static const int kPreallocatedArrayElements = 4; @@ -1095,7 +1085,7 @@ void Builtins::Generate_ArrayCode(MacroAssembler* masm) { Label generic_array_code; // Get the Array function. - GenerateLoadArrayFunction(masm, edi); + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, edi); if (FLAG_debug_code) { // Initial map for the builtin Array function shoud be a map. @@ -1131,7 +1121,7 @@ void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { if (FLAG_debug_code) { // The array construct code is only set for the builtin Array function which // does always have a map. - GenerateLoadArrayFunction(masm, ebx); + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ebx); __ cmp(edi, Operand(ebx)); __ Assert(equal, "Unexpected Array function"); // Initial map for the builtin Array function should be a map. @@ -1155,6 +1145,131 @@ void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { } +void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : number of arguments + // -- edi : constructor function + // -- esp[0] : return address + // -- esp[(argc - n) * 4] : arg[n] (zero-based) + // -- esp[(argc + 1) * 4] : receiver + // ----------------------------------- + __ IncrementCounter(&Counters::string_ctor_calls, 1); + + if (FLAG_debug_code) { + __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, ecx); + __ cmp(edi, Operand(ecx)); + __ Assert(equal, "Unexpected String function"); + } + + // Load the first argument into eax and get rid of the rest + // (including the receiver). + Label no_arguments; + __ test(eax, Operand(eax)); + __ j(zero, &no_arguments); + __ mov(ebx, Operand(esp, eax, times_pointer_size, 0)); + __ pop(ecx); + __ lea(esp, Operand(esp, eax, times_pointer_size, kPointerSize)); + __ push(ecx); + __ mov(eax, ebx); + + // Lookup the argument in the number to string cache. + Label not_cached, argument_is_string; + NumberToStringStub::GenerateLookupNumberStringCache( + masm, + eax, // Input. + ebx, // Result. + ecx, // Scratch 1. + edx, // 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 ------------- + // -- ebx : argument converted to string + // -- edi : constructor function + // -- esp[0] : return address + // ----------------------------------- + + // Allocate a JSValue and put the tagged pointer into eax. + Label gc_required; + __ AllocateInNewSpace(JSValue::kSize, + eax, // Result. + ecx, // New allocation top (we ignore it). + no_reg, + &gc_required, + TAG_OBJECT); + + // Set the map. + __ LoadGlobalFunctionInitialMap(edi, ecx); + if (FLAG_debug_code) { + __ cmpb(FieldOperand(ecx, Map::kInstanceSizeOffset), + JSValue::kSize >> kPointerSizeLog2); + __ Assert(equal, "Unexpected string wrapper instance size"); + __ cmpb(FieldOperand(ecx, Map::kUnusedPropertyFieldsOffset), 0); + __ Assert(equal, "Unexpected unused properties of string wrapper"); + } + __ mov(FieldOperand(eax, HeapObject::kMapOffset), ecx); + + // Set properties and elements. + __ Set(ecx, Immediate(Factory::empty_fixed_array())); + __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx); + __ mov(FieldOperand(eax, JSObject::kElementsOffset), ecx); + + // Set the value. + __ mov(FieldOperand(eax, JSValue::kValueOffset), ebx); + + // 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); + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &convert_argument); + Condition is_string = masm->IsObjectStringType(eax, ebx, ecx); + __ j(NegateCondition(is_string), &convert_argument); + __ mov(ebx, eax); + __ IncrementCounter(&Counters::string_ctor_string_value, 1); + __ jmp(&argument_is_string); + + // Invoke the conversion builtin and put the result into ebx. + __ bind(&convert_argument); + __ IncrementCounter(&Counters::string_ctor_conversions, 1); + __ EnterInternalFrame(); + __ push(edi); // Preserve the function. + __ push(eax); + __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION); + __ pop(edi); + __ LeaveInternalFrame(); + __ mov(ebx, eax); + __ jmp(&argument_is_string); + + // Load the empty string into ebx, remove the receiver from the + // stack, and jump back to the case where the argument is a string. + __ bind(&no_arguments); + __ Set(ebx, Immediate(Factory::empty_string())); + __ pop(ecx); + __ lea(esp, Operand(esp, kPointerSize)); + __ push(ecx); + __ 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); + __ EnterInternalFrame(); + __ push(ebx); + __ CallRuntime(Runtime::kNewStringWrapper, 1); + __ LeaveInternalFrame(); + __ ret(0); +} + + static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { __ push(ebp); __ mov(ebp, Operand(esp)); diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 79b40641b2..ff48d54f15 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -1369,6 +1369,30 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) { } +void MacroAssembler::LoadGlobalFunction(int index, Register function) { + // Load the global or builtins object from the current context. + mov(function, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); + // Load the global context from the global or builtins object. + mov(function, FieldOperand(function, GlobalObject::kGlobalContextOffset)); + // Load the function from the global context. + mov(function, Operand(function, Context::SlotOffset(index))); +} + + +void MacroAssembler::LoadGlobalFunctionInitialMap(Register function, + Register map) { + // Load the initial map. The global functions all have initial maps. + mov(map, FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); + if (FLAG_debug_code) { + Label ok, fail; + CheckMap(map, Factory::meta_map(), &fail, false); + jmp(&ok); + bind(&fail); + Abort("Global functions must have initial map"); + bind(&ok); + } +} + void MacroAssembler::Ret() { ret(0); diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index e5abfb4686..cd2f0f59fa 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -140,6 +140,13 @@ class MacroAssembler: public Assembler { // Find the function context up the context chain. void LoadContext(Register dst, int context_chain_length); + // Load the global function with the given index. + void LoadGlobalFunction(int index, Register function); + + // Load the initial map from the global function. The registers + // function and map can be the same. + void LoadGlobalFunctionInitialMap(Register function, Register map); + // --------------------------------------------------------------------------- // JavaScript invokes diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index c6c65f076e..7fc3f8114d 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -257,16 +257,8 @@ void StubCache::GenerateProbe(MacroAssembler* masm, void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, int index, Register prototype) { - // Load the global or builtins object from the current context. - __ mov(prototype, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); - // Load the global context from the global or builtins object. - __ mov(prototype, - FieldOperand(prototype, GlobalObject::kGlobalContextOffset)); - // Load the function from the global context. - __ mov(prototype, Operand(prototype, Context::SlotOffset(index))); - // Load the initial map. The global functions all have initial maps. - __ mov(prototype, - FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); + __ LoadGlobalFunction(index, prototype); + __ LoadGlobalFunctionInitialMap(prototype, prototype); // Load the prototype from the initial map. __ mov(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); } diff --git a/src/runtime.cc b/src/runtime.cc index f908036469..2fd5af59c6 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -5575,6 +5575,14 @@ static Object* Runtime_StringToArray(Arguments args) { } +static Object* Runtime_NewStringWrapper(Arguments args) { + NoHandleAllocation ha; + ASSERT(args.length() == 1); + CONVERT_CHECKED(String, value, args[0]); + return value->ToObject(); +} + + bool Runtime::IsUpperCaseChar(uint16_t ch) { unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth]; int char_length = to_upper_mapping.get(ch, 0, chars); diff --git a/src/runtime.h b/src/runtime.h index 71d95cf30a..312907adc5 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -174,6 +174,7 @@ namespace internal { F(StringMatch, 3, 1) \ F(StringTrim, 3, 1) \ F(StringToArray, 1, 1) \ + F(NewStringWrapper, 1, 1) \ \ /* Numbers */ \ F(NumberToRadixString, 2, 1) \ diff --git a/src/v8-counters.h b/src/v8-counters.h index a7cb0ba8eb..4cbc78195a 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -85,6 +85,11 @@ namespace internal { SC(compilation_cache_misses, V8.CompilationCacheMisses) \ SC(regexp_cache_hits, V8.RegExpCacheHits) \ SC(regexp_cache_misses, V8.RegExpCacheMisses) \ + SC(string_ctor_calls, V8.StringConstructorCalls) \ + SC(string_ctor_conversions, V8.StringConstructorConversions) \ + SC(string_ctor_cached_number, V8.StringConstructorCachedNumber) \ + SC(string_ctor_string_value, V8.StringConstructorStringValue) \ + SC(string_ctor_gc_required, V8.StringConstructorGCRequired) \ /* Amount of evaled source code. */ \ SC(total_eval_size, V8.TotalEvalSize) \ /* Amount of loaded source code. */ \ diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index e5d9d04b4b..85ad63719a 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -875,6 +875,13 @@ 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); +} + + void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax: number of arguments