Index: src/code-stubs-hydrogen.cc |
diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc |
index 2192e3fc2b71a4ae4c701631daede0a9945c0aab..683a4c526eab763aaa4b7dca85313f329e915dc4 100644 |
--- a/src/code-stubs-hydrogen.cc |
+++ b/src/code-stubs-hydrogen.cc |
@@ -882,6 +882,191 @@ HValue* CodeStubGraphBuilder<FastArrayPushStub>::BuildCodeStub() { |
Handle<Code> FastArrayPushStub::GenerateCode() { return DoGenerateCode(this); } |
template <> |
+HValue* CodeStubGraphBuilder<FastFunctionBindStub>::BuildCodeStub() { |
+ // TODO(verwaest): Fix deoptimizer messages. |
+ HValue* argc = GetArgumentsLength(); |
+ HInstruction* argument_elements = Add<HArgumentsElements>(false, false); |
+ HInstruction* object = Add<HAccessArgumentsAt>(argument_elements, argc, |
+ graph()->GetConstantMinus1()); |
+ BuildCheckHeapObject(object); |
+ HValue* map = Add<HLoadNamedField>(object, nullptr, HObjectAccess::ForMap()); |
+ Add<HCheckInstanceType>(object, HCheckInstanceType::IS_JS_FUNCTION); |
+ |
+ // Disallow binding of slow-mode functions. We need to figure out whether the |
+ // length and name property are in the original state. |
+ { |
+ HValue* bit_field3 = |
+ Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField3()); |
+ HValue* mask = Add<HConstant>(static_cast<int>(Map::DictionaryMap::kMask)); |
+ HValue* bit = AddUncasted<HBitwise>(Token::BIT_AND, bit_field3, mask); |
+ IfBuilder check(this); |
+ check.If<HCompareNumericAndBranch>(bit, mask, Token::EQ); |
+ check.ThenDeopt(Deoptimizer::kFastFunctionBindFailed); |
+ check.End(); |
+ } |
+ |
+ // Check whether the length and name properties are still present as |
+ // AccessorInfo objects. In that case, their value can be recomputed even if |
+ // the actual value on the object changes. |
+ { |
+ HValue* descriptors = |
+ Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapDescriptors()); |
+ |
+ HValue* descriptors_length = Add<HLoadNamedField>( |
+ descriptors, nullptr, HObjectAccess::ForFixedArrayLength()); |
+ IfBuilder range(this); |
+ range.If<HCompareNumericAndBranch>(descriptors_length, |
+ graph()->GetConstant1(), Token::LTE); |
+ range.ThenDeopt(Deoptimizer::kFastFunctionBindFailed); |
+ range.End(); |
+ |
+ // Verify .length. |
+ const int length_index = JSFunction::kLengthDescriptorIndex; |
+ HValue* maybe_length = Add<HLoadKeyed>( |
+ descriptors, Add<HConstant>(DescriptorArray::ToKeyIndex(length_index)), |
+ nullptr, nullptr, FAST_ELEMENTS); |
+ Unique<Name> length_string = Unique<Name>::CreateUninitialized( |
+ isolate()->factory()->length_string()); |
+ Add<HCheckValue>(maybe_length, length_string, false); |
+ |
+ HValue* maybe_length_accessor = Add<HLoadKeyed>( |
+ descriptors, |
+ Add<HConstant>(DescriptorArray::ToValueIndex(length_index)), nullptr, |
+ nullptr, FAST_ELEMENTS); |
+ BuildCheckHeapObject(maybe_length_accessor); |
+ Add<HCheckMaps>(maybe_length_accessor, |
+ isolate()->factory()->accessor_info_map()); |
+ |
+ // Verify .name. |
+ const int name_index = JSFunction::kNameDescriptorIndex; |
+ HValue* maybe_name = Add<HLoadKeyed>( |
+ descriptors, Add<HConstant>(DescriptorArray::ToKeyIndex(name_index)), |
+ nullptr, nullptr, FAST_ELEMENTS); |
+ Unique<Name> name_string = |
+ Unique<Name>::CreateUninitialized(isolate()->factory()->name_string()); |
+ Add<HCheckValue>(maybe_name, name_string, false); |
+ |
+ HValue* maybe_name_accessor = Add<HLoadKeyed>( |
+ descriptors, Add<HConstant>(DescriptorArray::ToValueIndex(name_index)), |
+ nullptr, nullptr, FAST_ELEMENTS); |
+ BuildCheckHeapObject(maybe_name_accessor); |
+ Add<HCheckMaps>(maybe_name_accessor, |
+ isolate()->factory()->accessor_info_map()); |
+ } |
+ |
+ // Choose the right bound function map based on whether the target is |
+ // constructable. |
+ { |
+ HValue* bit_field = |
+ Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField()); |
+ HValue* mask = Add<HConstant>(static_cast<int>(1 << Map::kIsConstructor)); |
+ HValue* bits = AddUncasted<HBitwise>(Token::BIT_AND, bit_field, mask); |
+ |
+ HValue* native_context = BuildGetNativeContext(); |
+ IfBuilder is_constructor(this); |
+ is_constructor.If<HCompareNumericAndBranch>(bits, mask, Token::EQ); |
+ is_constructor.Then(); |
+ { |
+ HValue* map = Add<HLoadNamedField>( |
+ native_context, nullptr, |
+ HObjectAccess::ForContextSlot( |
+ Context::BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX)); |
+ environment()->Push(map); |
+ } |
+ is_constructor.Else(); |
+ { |
+ HValue* map = Add<HLoadNamedField>( |
+ native_context, nullptr, |
+ HObjectAccess::ForContextSlot( |
+ Context::BOUND_FUNCTION_WITHOUT_CONSTRUCTOR_MAP_INDEX)); |
+ environment()->Push(map); |
+ } |
+ is_constructor.End(); |
+ } |
+ HValue* bound_function_map = environment()->Pop(); |
+ |
+ // Verify that __proto__ matches that of a the target bound function. |
+ { |
+ HValue* prototype = |
+ Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForPrototype()); |
+ HValue* expected_prototype = Add<HLoadNamedField>( |
+ bound_function_map, nullptr, HObjectAccess::ForPrototype()); |
+ IfBuilder equal_prototype(this); |
+ equal_prototype.IfNot<HCompareObjectEqAndBranch>(prototype, |
+ expected_prototype); |
+ equal_prototype.ThenDeopt(Deoptimizer::kFastFunctionBindFailed); |
+ equal_prototype.End(); |
+ } |
+ |
+ // Allocate the arguments array. |
+ IfBuilder empty_args(this); |
+ empty_args.If<HCompareNumericAndBranch>(argc, graph()->GetConstant1(), |
+ Token::LTE); |
+ empty_args.Then(); |
+ { environment()->Push(Add<HLoadRoot>(Heap::kEmptyFixedArrayRootIndex)); } |
+ empty_args.Else(); |
+ { |
+ HValue* elements_length = AddUncasted<HSub>(argc, graph()->GetConstant1()); |
+ HValue* elements = |
+ BuildAllocateAndInitializeArray(FAST_ELEMENTS, elements_length); |
+ |
+ LoopBuilder builder(this, context(), LoopBuilder::kPostIncrement); |
+ HValue* start = graph()->GetConstant1(); |
+ HValue* key = builder.BeginBody(start, argc, Token::LT); |
+ { |
+ HValue* argument = Add<HAccessArgumentsAt>(argument_elements, argc, key); |
+ HValue* index = AddUncasted<HSub>(key, graph()->GetConstant1()); |
+ AddElementAccess(elements, index, argument, elements, nullptr, |
+ FAST_ELEMENTS, STORE); |
+ } |
+ builder.EndBody(); |
+ environment()->Push(elements); |
+ } |
+ empty_args.End(); |
+ HValue* elements = environment()->Pop(); |
+ |
+ // Find the 'this' to bind. |
+ IfBuilder no_receiver(this); |
+ no_receiver.If<HCompareNumericAndBranch>(argc, graph()->GetConstant0(), |
+ Token::EQ); |
+ no_receiver.Then(); |
+ { environment()->Push(Add<HLoadRoot>(Heap::kUndefinedValueRootIndex)); } |
+ no_receiver.Else(); |
+ { |
+ environment()->Push(Add<HAccessArgumentsAt>(argument_elements, argc, |
+ graph()->GetConstant0())); |
+ } |
+ no_receiver.End(); |
+ HValue* receiver = environment()->Pop(); |
+ |
+ // Allocate the resulting bound function. |
+ HValue* size = Add<HConstant>(JSBoundFunction::kSize); |
+ HValue* bound_function = |
+ Add<HAllocate>(size, HType::JSObject(), NOT_TENURED, |
+ JS_BOUND_FUNCTION_TYPE, graph()->GetConstant0()); |
+ Add<HStoreNamedField>(bound_function, HObjectAccess::ForMap(), |
+ bound_function_map); |
+ HValue* empty_fixed_array = Add<HLoadRoot>(Heap::kEmptyFixedArrayRootIndex); |
+ Add<HStoreNamedField>(bound_function, HObjectAccess::ForPropertiesPointer(), |
+ empty_fixed_array); |
+ Add<HStoreNamedField>(bound_function, HObjectAccess::ForElementsPointer(), |
+ empty_fixed_array); |
+ Add<HStoreNamedField>(bound_function, HObjectAccess::ForBoundTargetFunction(), |
+ object); |
+ |
+ Add<HStoreNamedField>(bound_function, HObjectAccess::ForBoundThis(), |
+ receiver); |
+ Add<HStoreNamedField>(bound_function, HObjectAccess::ForBoundArguments(), |
+ elements); |
+ |
+ return bound_function; |
+} |
+ |
+Handle<Code> FastFunctionBindStub::GenerateCode() { |
+ return DoGenerateCode(this); |
+} |
+ |
+template <> |
HValue* CodeStubGraphBuilder<GrowArrayElementsStub>::BuildCodeStub() { |
ElementsKind kind = casted_stub()->elements_kind(); |
if (IsFastDoubleElementsKind(kind)) { |