From: bmeurer Date: Wed, 23 Sep 2015 13:49:09 +0000 (-0700) Subject: [builtins] Re-add similar String wrapper optimization for StringAdd. X-Git-Tag: upstream/4.7.83~150 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=17f598782d7db06dbd06c29c96ac9316a1a27aab;p=platform%2Fupstream%2Fv8.git [builtins] Re-add similar String wrapper optimization for StringAdd. For string wrappers (JSValue instances with [[StringData]] internal fields), we can shortcirciut the ToPrimitive if (a) the {input} map matches the initial map of the String function, (b) the {input} [[Prototype]] is the unmodified %StringPrototype% (i.e. no one monkey-patched toString, @@toPrimitive or valueOf), and (c) the %ObjectPrototype% (i.e. the [[Prototype]] of the %StringPrototype%) is also unmodified, that is no one sneaked a @@toPrimitive into the %ObjectPrototype%. If all these assumptions hold, we can just take the [[StringData]] value and return it. This just repairs a regression introduced by removing the weird (and broken) intrinsic %_IsStringWrapperSafeForDefaultValue, which was intendend to something similar to this, although less efficient and wrong in the presence of @@toPrimitive. Long-term we might want to move into the direction of having a ToPrimitiveStub that can do common cases while staying in JavaScript land (i.e. not going to C++). R=jarin@chromium.org BUG=chromium:532524 LOG=n Review URL: https://codereview.chromium.org/1366563002 Cr-Commit-Position: refs/heads/master@{#30890} --- diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 386d8ca..b40a96a 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -2334,7 +2334,15 @@ bool Genesis::InstallNatives(ContextType context_type) { USE_CUSTOM_MINIMUM_CAPACITY); native_context()->set_function_cache(*function_cache); - // Store the map for the string prototype after the natives has been compiled + // Store the map for the %ObjectPrototype% after the natives has been compiled + // and the Object function has been set up. + Handle object_function(native_context()->object_function()); + DCHECK(JSObject::cast(object_function->initial_map()->prototype()) + ->HasFastProperties()); + native_context()->set_object_function_prototype_map( + HeapObject::cast(object_function->initial_map()->prototype())->map()); + + // Store the map for the %StringPrototype% after the natives has been compiled // and the String function has been set up. Handle string_function(native_context()->string_function()); DCHECK(JSObject::cast( diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index 59d3a8b..9f78222 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -113,7 +113,8 @@ class CodeStubGraphBuilderBase : public HGraphBuilder { HValue* shared_info, HValue* native_context); - HValue* CheckString(HValue* input, bool convert); + HValue* BuildToString(HValue* input, bool convert); + HValue* BuildToPrimitive(HValue* input, HValue* input_map); private: HValue* BuildArraySingleArgumentConstructor(JSArrayBuilder* builder); @@ -1458,7 +1459,7 @@ Handle BinaryOpWithAllocationSiteStub::GenerateCode() { } -HValue* CodeStubGraphBuilderBase::CheckString(HValue* input, bool convert) { +HValue* CodeStubGraphBuilderBase::BuildToString(HValue* input, bool convert) { if (!convert) return BuildCheckString(input); IfBuilder if_inputissmi(this); HValue* inputissmi = if_inputissmi.If(input); @@ -1496,11 +1497,8 @@ HValue* CodeStubGraphBuilderBase::CheckString(HValue* input, bool convert) { } if_inputisprimitive.Else(); { - // TODO(bmeurer): Add support for fast ToPrimitive conversion using - // a dedicated ToPrimitiveStub. - Add(input); - Push(Add(Runtime::FunctionForId(Runtime::kToPrimitive), - 1)); + // Convert the input to a primitive. + Push(BuildToPrimitive(input, input_map)); } if_inputisprimitive.End(); // Convert the primitive to a string value. @@ -1518,6 +1516,83 @@ HValue* CodeStubGraphBuilderBase::CheckString(HValue* input, bool convert) { } +HValue* CodeStubGraphBuilderBase::BuildToPrimitive(HValue* input, + HValue* input_map) { + // Get the native context of the caller. + HValue* native_context = BuildGetNativeContext(); + + // Determine the initial map of the %ObjectPrototype%. + HValue* object_function_prototype_map = + Add(native_context, nullptr, + HObjectAccess::ForContextSlot( + Context::OBJECT_FUNCTION_PROTOTYPE_MAP_INDEX)); + + // Determine the initial map of the %StringPrototype%. + HValue* string_function_prototype_map = + Add(native_context, nullptr, + HObjectAccess::ForContextSlot( + Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); + + // Determine the initial map of the String function. + HValue* string_function = Add( + native_context, nullptr, + HObjectAccess::ForContextSlot(Context::STRING_FUNCTION_INDEX)); + HValue* string_function_initial_map = Add( + string_function, nullptr, HObjectAccess::ForPrototypeOrInitialMap()); + + // Determine the map of the [[Prototype]] of {input}. + HValue* input_prototype = + Add(input_map, nullptr, HObjectAccess::ForPrototype()); + HValue* input_prototype_map = + Add(input_prototype, nullptr, HObjectAccess::ForMap()); + + // For string wrappers (JSValue instances with [[StringData]] internal + // fields), we can shortcirciut the ToPrimitive if + // + // (a) the {input} map matches the initial map of the String function, + // (b) the {input} [[Prototype]] is the unmodified %StringPrototype% (i.e. + // no one monkey-patched toString, @@toPrimitive or valueOf), and + // (c) the %ObjectPrototype% (i.e. the [[Prototype]] of the + // %StringPrototype%) is also unmodified, that is no one sneaked a + // @@toPrimitive into the %ObjectPrototype%. + // + // If all these assumptions hold, we can just take the [[StringData]] value + // and return it. + // TODO(bmeurer): This just repairs a regression introduced by removing the + // weird (and broken) intrinsic %_IsStringWrapperSafeForDefaultValue, which + // was intendend to something similar to this, although less efficient and + // wrong in the presence of @@toPrimitive. Long-term we might want to move + // into the direction of having a ToPrimitiveStub that can do common cases + // while staying in JavaScript land (i.e. not going to C++). + IfBuilder if_inputisstringwrapper(this); + if_inputisstringwrapper.If( + input_map, string_function_initial_map); + if_inputisstringwrapper.And(); + if_inputisstringwrapper.If( + input_prototype_map, string_function_prototype_map); + if_inputisstringwrapper.And(); + if_inputisstringwrapper.If( + Add(Add(input_prototype_map, nullptr, + HObjectAccess::ForPrototype()), + nullptr, HObjectAccess::ForMap()), + object_function_prototype_map); + if_inputisstringwrapper.Then(); + { + Push(BuildLoadNamedField( + input, FieldIndex::ForInObjectOffset(JSValue::kValueOffset))); + } + if_inputisstringwrapper.Else(); + { + // TODO(bmeurer): Add support for fast ToPrimitive conversion using + // a dedicated ToPrimitiveStub. + Add(input); + Push(Add(Runtime::FunctionForId(Runtime::kToPrimitive), 1)); + } + if_inputisstringwrapper.End(); + return Pop(); +} + + template <> HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { StringAddStub* stub = casted_stub(); @@ -1530,11 +1605,11 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { // Make sure that both arguments are strings if not known in advance. if ((flags & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) { left = - CheckString(left, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT); + BuildToString(left, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT); } if ((flags & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) { - right = - CheckString(right, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT); + right = BuildToString(right, + (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT); } return BuildStringAdd(left, right, HAllocationMode(pretenure_flag)); diff --git a/src/contexts.h b/src/contexts.h index 1692038..62c1880 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -219,6 +219,7 @@ enum BindingFlags { V(NORMALIZED_MAP_CACHE_INDEX, Object, normalized_map_cache) \ V(NUMBER_FUNCTION_INDEX, JSFunction, number_function) \ V(OBJECT_FUNCTION_INDEX, JSFunction, object_function) \ + V(OBJECT_FUNCTION_PROTOTYPE_MAP_INDEX, Map, object_function_prototype_map) \ V(OPAQUE_REFERENCE_FUNCTION_INDEX, JSFunction, opaque_reference_function) \ V(REGEXP_FUNCTION_INDEX, JSFunction, regexp_function) \ V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map) \