Chromium Code Reviews| Index: src/code-stubs-hydrogen.cc |
| diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc |
| index a968c90c22fe500bfaba8c030cbbdad23f467d2d..db01dd50654398309731cdaa007a6086e879c0a3 100644 |
| --- a/src/code-stubs-hydrogen.cc |
| +++ b/src/code-stubs-hydrogen.cc |
| @@ -78,6 +78,8 @@ class CodeStubGraphBuilderBase : public HGraphBuilder { |
| Representation representation, |
| bool transition_to_field); |
| + HValue* BuildPushElement(HValue* object, HValue* value, ElementsKind kind); |
| + |
| enum ArgumentClass { |
| NONE, |
| SINGLE, |
| @@ -780,6 +782,184 @@ Handle<Code> StoreScriptContextFieldStub::GenerateCode() { |
| return DoGenerateCode(this); |
| } |
| +HValue* CodeStubGraphBuilderBase::BuildPushElement(HValue* object, |
| + HValue* value, |
| + ElementsKind kind) { |
| + HValue* length = Add<HLoadNamedField>(object, nullptr, |
| + HObjectAccess::ForArrayLength(kind)); |
| + HValue* key = length; |
| + HValue* elements = Add<HLoadNamedField>(object, nullptr, |
| + HObjectAccess::ForElementsPointer()); |
| + elements = BuildCheckForCapacityGrow(object, elements, kind, length, key, |
| + true, STORE); |
| + AddElementAccess(elements, key, value, object, nullptr, kind, STORE); |
| + return key; |
| +} |
| + |
| +template <> |
| +HValue* CodeStubGraphBuilder<FastArrayPushStub>::BuildCodeStub() { |
| + // TODO(verwaest): Fix deoptimizer messages. |
| + HValue* argc = GetArgumentsLength(); |
| + IfBuilder arg_check(this); |
| + arg_check.If<HCompareNumericAndBranch>(argc, graph()->GetConstant1(), |
| + Token::NE); |
| + arg_check.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
| + arg_check.End(); |
| + |
| + HInstruction* argument_elements = Add<HArgumentsElements>(false, false); |
| + HInstruction* object = Add<HAccessArgumentsAt>(argument_elements, argc, |
| + graph()->GetConstantMinus1()); |
| + HInstruction* value = |
| + Add<HAccessArgumentsAt>(argument_elements, argc, graph()->GetConstant0()); |
| + |
| + BuildCheckHeapObject(object); |
| + HValue* map = Add<HLoadNamedField>(object, nullptr, HObjectAccess::ForMap()); |
| + Add<HCheckInstanceType>(object, HCheckInstanceType::IS_JS_ARRAY); |
| + |
| + // Disallow pushing onto prototypes. It might be the JSArray prototype. |
| + // Disallow pushing onto non-extensible objects. |
| + { |
| + HValue* bit_field2 = |
| + Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField2()); |
| + HValue* mask = |
| + Add<HConstant>(static_cast<int>(Map::IsPrototypeMapBits::kMask) | |
| + (1 << Map::kIsExtensible)); |
| + HValue* bit = AddUncasted<HBitwise>(Token::BIT_AND, bit_field2, mask); |
|
Jakob Kummerow
2016/03/21 15:12:51
nit: call it "bits", could be more than one.
|
| + IfBuilder check(this); |
| + check.If<HCompareNumericAndBranch>( |
| + bit, Add<HConstant>(1 << Map::kIsExtensible), Token::NE); |
| + check.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
| + check.End(); |
| + } |
| + |
| + // Disallow pushing onto observed objects. |
| + { |
| + HValue* bit_field = |
| + Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField()); |
| + HValue* mask = Add<HConstant>(1 << Map::kIsObserved); |
| + HValue* bit = AddUncasted<HBitwise>(Token::BIT_AND, bit_field, mask); |
| + IfBuilder check(this); |
| + check.If<HCompareNumericAndBranch>(bit, mask, Token::EQ); |
| + check.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
| + check.End(); |
| + } |
| + |
| + // Disallow pushing onto arrays in dictionary named property mode. We need to |
| + // figure out whether the length property is still writable. |
| + { |
| + 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::kFastArrayPushFailed); |
| + check.End(); |
| + } |
| + |
| + // Check whether the length property is writable. The length property is the |
| + // only default named properties on arrays. It's nonconfigurable, hence is |
|
Jakob Kummerow
2016/03/21 15:12:51
nit: s/properties/property/
|
| + // guaranteed to stay the first property. |
| + { |
| + HValue* descriptors = |
| + Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapDescriptors()); |
| + HValue* details = Add<HLoadKeyed>( |
| + descriptors, Add<HConstant>(DescriptorArray::ToDetailsIndex(0)), |
| + nullptr, nullptr, FAST_SMI_ELEMENTS); |
| + HValue* mask = |
| + Add<HConstant>(READ_ONLY << PropertyDetails::AttributesField::kShift); |
| + HValue* bit = AddUncasted<HBitwise>(Token::BIT_AND, details, mask); |
| + IfBuilder writable(this); |
|
Jakob Kummerow
2016/03/21 15:12:51
nit: this should be called "readonly" (to make the
|
| + writable.If<HCompareNumericAndBranch>(bit, mask, Token::EQ); |
| + writable.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
| + writable.End(); |
| + } |
| + |
| + environment()->Push(map); |
| + LoopBuilder check_prototypes(this); |
| + check_prototypes.BeginBody(1); |
| + { |
| + HValue* parent_map = environment()->Pop(); |
| + HValue* prototype = Add<HLoadNamedField>(parent_map, nullptr, |
| + HObjectAccess::ForPrototype()); |
| + |
| + IfBuilder is_null(this); |
| + is_null.If<HCompareObjectEqAndBranch>(prototype, |
| + graph()->GetConstantNull()); |
| + is_null.Then(); |
| + check_prototypes.Break(); |
| + is_null.End(); |
| + |
| + HValue* prototype_map = |
| + Add<HLoadNamedField>(prototype, nullptr, HObjectAccess::ForMap()); |
| + HValue* instance_type = Add<HLoadNamedField>( |
| + prototype_map, nullptr, HObjectAccess::ForMapInstanceType()); |
| + IfBuilder check_instance_type(this); |
| + check_instance_type.If<HCompareNumericAndBranch>( |
| + instance_type, Add<HConstant>(JS_VALUE_TYPE), Token::LTE); |
|
Jakob Kummerow
2016/03/21 15:12:51
This looks like a use case for the recently introd
|
| + check_instance_type.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
| + check_instance_type.End(); |
| + |
| + HValue* elements = Add<HLoadNamedField>( |
| + prototype, nullptr, HObjectAccess::ForElementsPointer()); |
| + IfBuilder no_elements(this); |
| + no_elements.If<HCompareObjectEqAndBranch>( |
| + elements, Add<HConstant>(isolate()->factory()->empty_fixed_array())); |
| + no_elements.Then(); |
| + no_elements.ElseDeopt(Deoptimizer::kFastArrayPushFailed); |
|
Jakob Kummerow
2016/03/21 15:12:51
nit: you could also use IfNot/ThenDeopt. Up to you
|
| + no_elements.End(); |
| + |
| + environment()->Push(prototype_map); |
| + } |
| + check_prototypes.EndBody(); |
| + |
| + HValue* bit_field2 = |
| + Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField2()); |
|
Jakob Kummerow
2016/03/21 15:12:51
This is duplicated from line 823. Suggestion: hois
|
| + HValue* kind = BuildDecodeField<Map::ElementsKindBits>(bit_field2); |
| + |
| + IfBuilder is_smi(this); |
|
Jakob Kummerow
2016/03/21 15:12:51
nit: I'd prefer the name "has_smi_elements".
|
| + is_smi.If<HCompareNumericAndBranch>( |
| + kind, Add<HConstant>(FAST_HOLEY_SMI_ELEMENTS), Token::LTE); |
|
Jakob Kummerow
2016/03/21 15:12:51
nit: a comment and a check would be nice, roughly:
|
| + is_smi.Then(); |
| + { |
| + HValue* smi_value = |
| + AddUncasted<HForceRepresentation>(value, Representation::Smi()); |
| + HValue* key = BuildPushElement(object, smi_value, FAST_HOLEY_SMI_ELEMENTS); |
| + environment()->Push(key); |
| + } |
| + is_smi.Else(); |
| + { |
| + IfBuilder is_object(this); |
|
Jakob Kummerow
2016/03/21 15:12:50
nit: has_object_elements
|
| + is_object.If<HCompareNumericAndBranch>( |
| + kind, Add<HConstant>(FAST_HOLEY_ELEMENTS), Token::LTE); |
| + is_object.Then(); |
| + { |
| + HValue* key = BuildPushElement(object, value, FAST_HOLEY_ELEMENTS); |
| + environment()->Push(key); |
| + } |
| + is_object.Else(); |
| + { |
| + IfBuilder is_double(this); |
| + is_double.If<HCompareNumericAndBranch>( |
| + kind, Add<HConstant>(FAST_HOLEY_DOUBLE_ELEMENTS), Token::GT); |
| + is_double.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
| + is_double.End(); |
| + |
| + HValue* double_value = |
| + AddUncasted<HForceRepresentation>(value, Representation::Double()); |
| + HValue* key = |
| + BuildPushElement(object, double_value, FAST_HOLEY_DOUBLE_ELEMENTS); |
| + environment()->Push(key); |
| + } |
| + is_object.End(); |
| + } |
| + is_smi.End(); |
| + |
| + HValue* key = environment()->Pop(); |
| + return AddUncasted<HAdd>(key, graph()->GetConstant1()); |
| +} |
| + |
| +Handle<Code> FastArrayPushStub::GenerateCode() { return DoGenerateCode(this); } |
| template <> |
| HValue* CodeStubGraphBuilder<GrowArrayElementsStub>::BuildCodeStub() { |