Index: src/code-stubs-hydrogen.cc |
diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc |
index ffb55358f1048b4df6e12f620386b16d001bc74e..fdd2a1d76c26838a0429c4482ac30a370352238f 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,194 @@ 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* bits = AddUncasted<HBitwise>(Token::BIT_AND, bit_field2, mask); |
+ IfBuilder check(this); |
+ check.If<HCompareNumericAndBranch>( |
+ bits, 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 property on arrays. It's nonconfigurable, hence is |
+ // 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 readonly(this); |
+ readonly.If<HCompareNumericAndBranch>(bit, mask, Token::EQ); |
+ readonly.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
+ readonly.End(); |
+ } |
+ |
+ HValue* null = Add<HLoadRoot>(Heap::kNullValueRootIndex); |
+ HValue* empty = Add<HLoadRoot>(Heap::kEmptyFixedArrayRootIndex); |
+ 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, null); |
+ 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>(LAST_CUSTOM_ELEMENTS_RECEIVER), |
+ Token::LTE); |
+ check_instance_type.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
+ check_instance_type.End(); |
+ |
+ HValue* elements = Add<HLoadNamedField>( |
+ prototype, nullptr, HObjectAccess::ForElementsPointer()); |
+ IfBuilder no_elements(this); |
+ no_elements.IfNot<HCompareObjectEqAndBranch>(elements, empty); |
+ no_elements.ThenDeopt(Deoptimizer::kFastArrayPushFailed); |
+ no_elements.End(); |
+ |
+ environment()->Push(prototype_map); |
+ } |
+ check_prototypes.EndBody(); |
+ |
+ HValue* bit_field2 = |
+ Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField2()); |
+ HValue* kind = BuildDecodeField<Map::ElementsKindBits>(bit_field2); |
+ |
+ // Below we only check the upper bound of the relevant ranges to include both |
+ // holey and non-holey versions. We check them in order smi, object, double |
+ // since smi < object < double. |
+ STATIC_ASSERT(FAST_SMI_ELEMENTS < FAST_HOLEY_SMI_ELEMENTS); |
+ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS < FAST_HOLEY_ELEMENTS); |
+ STATIC_ASSERT(FAST_ELEMENTS < FAST_HOLEY_ELEMENTS); |
+ STATIC_ASSERT(FAST_HOLEY_ELEMENTS < FAST_HOLEY_DOUBLE_ELEMENTS); |
+ STATIC_ASSERT(FAST_DOUBLE_ELEMENTS < FAST_HOLEY_DOUBLE_ELEMENTS); |
+ IfBuilder has_smi_elements(this); |
+ has_smi_elements.If<HCompareNumericAndBranch>( |
+ kind, Add<HConstant>(FAST_HOLEY_SMI_ELEMENTS), Token::LTE); |
+ has_smi_elements.Then(); |
+ { |
+ HValue* smi_value = |
+ AddUncasted<HForceRepresentation>(value, Representation::Smi()); |
+ HValue* key = BuildPushElement(object, smi_value, FAST_HOLEY_SMI_ELEMENTS); |
+ environment()->Push(key); |
+ } |
+ has_smi_elements.Else(); |
+ { |
+ IfBuilder has_object_elements(this); |
+ has_object_elements.If<HCompareNumericAndBranch>( |
+ kind, Add<HConstant>(FAST_HOLEY_ELEMENTS), Token::LTE); |
+ has_object_elements.Then(); |
+ { |
+ HValue* key = BuildPushElement(object, value, FAST_HOLEY_ELEMENTS); |
+ environment()->Push(key); |
+ } |
+ has_object_elements.Else(); |
+ { |
+ IfBuilder has_double_elements(this); |
+ has_double_elements.If<HCompareNumericAndBranch>( |
+ kind, Add<HConstant>(FAST_HOLEY_DOUBLE_ELEMENTS), Token::LTE); |
+ has_double_elements.Then(); |
+ { |
+ HValue* double_value = |
+ AddUncasted<HForceRepresentation>(value, Representation::Double()); |
+ HValue* key = |
+ BuildPushElement(object, double_value, FAST_HOLEY_DOUBLE_ELEMENTS); |
+ environment()->Push(key); |
+ } |
+ has_double_elements.ElseDeopt(Deoptimizer::kFastArrayPushFailed); |
+ has_double_elements.End(); |
+ } |
+ has_object_elements.End(); |
+ } |
+ has_smi_elements.End(); |
+ |
+ HValue* key = environment()->Pop(); |
+ return AddUncasted<HAdd>(key, graph()->GetConstant1()); |
+} |
+ |
+Handle<Code> FastArrayPushStub::GenerateCode() { return DoGenerateCode(this); } |
template <> |
HValue* CodeStubGraphBuilder<GrowArrayElementsStub>::BuildCodeStub() { |