diff options
Diffstat (limited to 'deps/v8/src/code-stub-assembler.cc')
-rw-r--r-- | deps/v8/src/code-stub-assembler.cc | 1083 |
1 files changed, 867 insertions, 216 deletions
diff --git a/deps/v8/src/code-stub-assembler.cc b/deps/v8/src/code-stub-assembler.cc index 7b062b6bf6..edfe2de86c 100644 --- a/deps/v8/src/code-stub-assembler.cc +++ b/deps/v8/src/code-stub-assembler.cc @@ -43,41 +43,45 @@ void CodeStubAssembler::HandleBreakOnNode() { BreakOnNode(node_id); } -void CodeStubAssembler::Assert(const NodeGenerator& codition_body, +void CodeStubAssembler::Assert(const NodeGenerator& condition_body, const char* message, const char* file, int line) { #if defined(DEBUG) if (FLAG_debug_code) { - Label ok(this); - Label not_ok(this, Label::kDeferred); - if (message != nullptr && FLAG_code_comments) { - Comment("[ Assert: %s", message); + Check(condition_body, message, file, line); + } +#endif +} + +void CodeStubAssembler::Check(const NodeGenerator& condition_body, + const char* message, const char* file, int line) { + Label ok(this); + Label not_ok(this, Label::kDeferred); + if (message != nullptr && FLAG_code_comments) { + Comment("[ Assert: %s", message); + } else { + Comment("[ Assert"); + } + Node* condition = condition_body(); + DCHECK_NOT_NULL(condition); + Branch(condition, &ok, ¬_ok); + BIND(¬_ok); + if (message != nullptr) { + char chars[1024]; + Vector<char> buffer(chars); + if (file != nullptr) { + SNPrintF(buffer, "CSA_ASSERT failed: %s [%s:%d]\n", message, file, line); } else { - Comment("[ Assert"); + SNPrintF(buffer, "CSA_ASSERT failed: %s\n", message); } - Node* condition = codition_body(); - DCHECK_NOT_NULL(condition); - Branch(condition, &ok, ¬_ok); - BIND(¬_ok); - if (message != nullptr) { - char chars[1024]; - Vector<char> buffer(chars); - if (file != nullptr) { - SNPrintF(buffer, "CSA_ASSERT failed: %s [%s:%d]\n", message, file, - line); - } else { - SNPrintF(buffer, "CSA_ASSERT failed: %s\n", message); - } - CallRuntime( - Runtime::kGlobalPrint, SmiConstant(Smi::kZero), - HeapConstant(factory()->NewStringFromAsciiChecked(&(buffer[0])))); - } - DebugBreak(); - Goto(&ok); - BIND(&ok); - Comment("] Assert"); + CallRuntime( + Runtime::kGlobalPrint, SmiConstant(Smi::kZero), + HeapConstant(factory()->NewStringFromAsciiChecked(&(buffer[0])))); } -#endif + DebugBreak(); + Goto(&ok); + BIND(&ok); + Comment("] Assert"); } Node* CodeStubAssembler::Select(Node* condition, const NodeGenerator& true_body, @@ -593,12 +597,12 @@ Node* CodeStubAssembler::TrySmiDiv(Node* dividend, Node* divisor, Branch(WordEqual(dividend, SmiConstant(0)), ÷nd_is_zero, ÷nd_is_not_zero); - Bind(÷nd_is_zero); + BIND(÷nd_is_zero); { GotoIf(SmiLessThan(divisor, SmiConstant(0)), bailout); Goto(÷nd_is_not_zero); } - Bind(÷nd_is_not_zero); + BIND(÷nd_is_not_zero); Node* untagged_divisor = SmiToWord32(divisor); Node* untagged_dividend = SmiToWord32(dividend); @@ -609,7 +613,7 @@ Node* CodeStubAssembler::TrySmiDiv(Node* dividend, Node* divisor, Branch(Word32Equal(untagged_divisor, Int32Constant(-1)), &divisor_is_minus_one, &divisor_is_not_minus_one); - Bind(&divisor_is_minus_one); + BIND(&divisor_is_minus_one); { GotoIf(Word32Equal( untagged_dividend, @@ -617,7 +621,7 @@ Node* CodeStubAssembler::TrySmiDiv(Node* dividend, Node* divisor, bailout); Goto(&divisor_is_not_minus_one); } - Bind(&divisor_is_not_minus_one); + BIND(&divisor_is_not_minus_one); Node* untagged_result = Int32Div(untagged_dividend, untagged_divisor); Node* truncated = Int32Mul(untagged_result, untagged_divisor); @@ -654,8 +658,16 @@ Node* CodeStubAssembler::TaggedIsPositiveSmi(Node* a) { Node* CodeStubAssembler::WordIsWordAligned(Node* word) { return WordEqual(IntPtrConstant(0), - WordAnd(word, IntPtrConstant((1 << kPointerSizeLog2) - 1))); + WordAnd(word, IntPtrConstant(kPointerSize - 1))); +} + +#if DEBUG +void CodeStubAssembler::Bind(Label* label, AssemblerDebugInfo debug_info) { + CodeAssembler::Bind(label, debug_info); } +#else +void CodeStubAssembler::Bind(Label* label) { CodeAssembler::Bind(label); } +#endif // DEBUG void CodeStubAssembler::BranchIfPrototypesHaveNoElements( Node* receiver_map, Label* definitely_no_elements, @@ -1238,6 +1250,16 @@ Node* CodeStubAssembler::LoadStringLength(Node* object) { return LoadObjectField(object, String::kLengthOffset); } +Node* CodeStubAssembler::PointerToSeqStringData(Node* seq_string) { + CSA_ASSERT(this, IsString(seq_string)); + CSA_ASSERT(this, + IsSequentialStringInstanceType(LoadInstanceType(seq_string))); + STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize); + return IntPtrAdd( + BitcastTaggedToWord(seq_string), + IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); +} + Node* CodeStubAssembler::LoadJSValueValue(Node* object) { CSA_ASSERT(this, IsJSValue(object)); return LoadObjectField(object, JSValue::kValueOffset); @@ -1393,7 +1415,7 @@ Node* CodeStubAssembler::LoadContextElement(Node* context, int slot_index) { Node* CodeStubAssembler::LoadContextElement(Node* context, Node* slot_index) { Node* offset = - IntPtrAdd(WordShl(slot_index, kPointerSizeLog2), + IntPtrAdd(TimesPointerSize(slot_index), IntPtrConstant(Context::kHeaderSize - kHeapObjectTag)); return Load(MachineType::AnyTagged(), context, offset); } @@ -1407,7 +1429,7 @@ Node* CodeStubAssembler::StoreContextElement(Node* context, int slot_index, Node* CodeStubAssembler::StoreContextElement(Node* context, Node* slot_index, Node* value) { Node* offset = - IntPtrAdd(WordShl(slot_index, kPointerSizeLog2), + IntPtrAdd(TimesPointerSize(slot_index), IntPtrConstant(Context::kHeaderSize - kHeapObjectTag)); return Store(context, offset, value); } @@ -1430,6 +1452,27 @@ Node* CodeStubAssembler::LoadJSArrayElementsMap(ElementsKind kind, return LoadContextElement(native_context, Context::ArrayMapIndex(kind)); } +Node* CodeStubAssembler::LoadJSFunctionPrototype(Node* function, + Label* if_bailout) { + CSA_ASSERT(this, TaggedIsNotSmi(function)); + CSA_ASSERT(this, IsJSFunction(function)); + CSA_ASSERT(this, IsClearWord32(LoadMapBitField(LoadMap(function)), + 1 << Map::kHasNonInstancePrototype)); + Node* proto_or_map = + LoadObjectField(function, JSFunction::kPrototypeOrInitialMapOffset); + GotoIf(IsTheHole(proto_or_map), if_bailout); + + VARIABLE(var_result, MachineRepresentation::kTagged, proto_or_map); + Label done(this, &var_result); + GotoIfNot(IsMap(proto_or_map), &done); + + var_result.Bind(LoadMapPrototype(proto_or_map)); + Goto(&done); + + BIND(&done); + return var_result.value(); +} + Node* CodeStubAssembler::StoreHeapNumberValue(Node* object, Node* value) { return StoreObjectFieldNoWriteBarrier(object, HeapNumber::kValueOffset, value, MachineRepresentation::kFloat64); @@ -1523,8 +1566,57 @@ Node* CodeStubAssembler::StoreFixedDoubleArrayElement( return StoreNoWriteBarrier(rep, object, offset, value); } -Node* CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* context, - Node* array, +void CodeStubAssembler::EnsureArrayLengthWritable(Node* map, Label* bailout) { + // 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. + Node* descriptors = LoadMapDescriptors(map); + Node* details = + LoadFixedArrayElement(descriptors, DescriptorArray::ToDetailsIndex(0)); + GotoIf(IsSetSmi(details, PropertyDetails::kAttributesReadOnlyMask), bailout); +} + +Node* CodeStubAssembler::EnsureArrayPushable(Node* receiver, Label* bailout) { + // Disallow pushing onto prototypes. It might be the JSArray prototype. + // Disallow pushing onto non-extensible objects. + Comment("Disallow pushing onto prototypes"); + Node* map = LoadMap(receiver); + Node* bit_field2 = LoadMapBitField2(map); + int mask = static_cast<int>(Map::IsPrototypeMapBits::kMask) | + (1 << Map::kIsExtensible); + Node* test = Word32And(bit_field2, Int32Constant(mask)); + GotoIf(Word32NotEqual(test, Int32Constant(1 << Map::kIsExtensible)), bailout); + + // Disallow pushing onto arrays in dictionary named property mode. We need + // to figure out whether the length property is still writable. + Comment("Disallow pushing onto arrays in dictionary named property mode"); + GotoIf(IsDictionaryMap(map), bailout); + + EnsureArrayLengthWritable(map, bailout); + + Node* kind = DecodeWord32<Map::ElementsKindBits>(bit_field2); + return kind; +} + +void CodeStubAssembler::PossiblyGrowElementsCapacity( + ParameterMode mode, ElementsKind kind, Node* array, Node* length, + Variable* var_elements, Node* growth, Label* bailout) { + Label fits(this, var_elements); + Node* capacity = + TaggedToParameter(LoadFixedArrayBaseLength(var_elements->value()), mode); + // length and growth nodes are already in a ParameterMode appropriate + // representation. + Node* new_length = IntPtrOrSmiAdd(growth, length, mode); + GotoIfNot(IntPtrOrSmiGreaterThan(new_length, capacity, mode), &fits); + Node* new_capacity = CalculateNewElementsCapacity(new_length, mode); + var_elements->Bind(GrowElementsCapacity(array, var_elements->value(), kind, + kind, capacity, new_capacity, mode, + bailout)); + Goto(&fits); + BIND(&fits); +} + +Node* CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* array, CodeStubArguments& args, Variable& arg_index, Label* bailout) { @@ -1536,46 +1628,22 @@ Node* CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* context, VARIABLE(var_length, OptimalParameterRepresentation(), TaggedToParameter(LoadJSArrayLength(array), mode)); VARIABLE(var_elements, MachineRepresentation::kTagged, LoadElements(array)); - Node* capacity = - TaggedToParameter(LoadFixedArrayBaseLength(var_elements.value()), mode); // Resize the capacity of the fixed array if it doesn't fit. - Label fits(this, &var_elements); Node* first = arg_index.value(); - Node* growth = IntPtrSub(args.GetLength(), first); - Node* new_length = - IntPtrOrSmiAdd(WordToParameter(growth, mode), var_length.value(), mode); - GotoIfNot(IntPtrOrSmiGreaterThan(new_length, capacity, mode), &fits); - Node* new_capacity = CalculateNewElementsCapacity(new_length, mode); - var_elements.Bind(GrowElementsCapacity(array, var_elements.value(), kind, - kind, capacity, new_capacity, mode, - &pre_bailout)); - Goto(&fits); - BIND(&fits); - Node* elements = var_elements.value(); + Node* growth = WordToParameter(IntPtrSub(args.GetLength(), first), mode); + PossiblyGrowElementsCapacity(mode, kind, array, var_length.value(), + &var_elements, growth, &pre_bailout); // Push each argument onto the end of the array now that there is enough // capacity. CodeStubAssembler::VariableList push_vars({&var_length}, zone()); + Node* elements = var_elements.value(); args.ForEach( push_vars, [this, kind, mode, elements, &var_length, &pre_bailout](Node* arg) { - if (IsFastSmiElementsKind(kind)) { - GotoIf(TaggedIsNotSmi(arg), &pre_bailout); - } else if (IsFastDoubleElementsKind(kind)) { - GotoIfNotNumber(arg, &pre_bailout); - } - if (IsFastDoubleElementsKind(kind)) { - Node* double_value = ChangeNumberToFloat64(arg); - StoreFixedDoubleArrayElement(elements, var_length.value(), - Float64SilenceNaN(double_value), mode); - } else { - WriteBarrierMode barrier_mode = IsFastSmiElementsKind(kind) - ? SKIP_WRITE_BARRIER - : UPDATE_WRITE_BARRIER; - StoreFixedArrayElement(elements, var_length.value(), arg, - barrier_mode, 0, mode); - } + TryStoreArrayElement(kind, mode, &pre_bailout, elements, + var_length.value(), arg); Increment(var_length, 1, mode); }, first, nullptr); @@ -1600,6 +1668,49 @@ Node* CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* context, return var_tagged_length.value(); } +void CodeStubAssembler::TryStoreArrayElement(ElementsKind kind, + ParameterMode mode, Label* bailout, + Node* elements, Node* index, + Node* value) { + if (IsFastSmiElementsKind(kind)) { + GotoIf(TaggedIsNotSmi(value), bailout); + } else if (IsFastDoubleElementsKind(kind)) { + GotoIfNotNumber(value, bailout); + } + if (IsFastDoubleElementsKind(kind)) { + Node* double_value = ChangeNumberToFloat64(value); + StoreFixedDoubleArrayElement(elements, index, + Float64SilenceNaN(double_value), mode); + } else { + WriteBarrierMode barrier_mode = + IsFastSmiElementsKind(kind) ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER; + StoreFixedArrayElement(elements, index, value, barrier_mode, 0, mode); + } +} + +void CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, Node* array, + Node* value, Label* bailout) { + Comment("BuildAppendJSArray: %s", ElementsKindToString(kind)); + ParameterMode mode = OptimalParameterMode(); + VARIABLE(var_length, OptimalParameterRepresentation(), + TaggedToParameter(LoadJSArrayLength(array), mode)); + VARIABLE(var_elements, MachineRepresentation::kTagged, LoadElements(array)); + + // Resize the capacity of the fixed array if it doesn't fit. + Node* growth = IntPtrOrSmiConstant(1, mode); + PossiblyGrowElementsCapacity(mode, kind, array, var_length.value(), + &var_elements, growth, bailout); + + // Push each argument onto the end of the array now that there is enough + // capacity. + TryStoreArrayElement(kind, mode, bailout, var_elements.value(), + var_length.value(), value); + Increment(var_length, 1, mode); + + Node* length = ParameterToTagged(var_length.value(), mode); + StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); +} + Node* CodeStubAssembler::AllocateHeapNumber(MutableMode mode) { Node* result = Allocate(HeapNumber::kSize, kNone); Heap::RootListIndex heap_map_index = @@ -1939,14 +2050,15 @@ Node* CodeStubAssembler::AllocateNameDictionary(Node* at_least_space_for) { CSA_ASSERT(this, UintPtrLessThanOrEqual( at_least_space_for, IntPtrConstant(NameDictionary::kMaxCapacity))); - Node* capacity = HashTableComputeCapacity(at_least_space_for); - CSA_ASSERT(this, WordIsPowerOfTwo(capacity)); + return AllocateNameDictionaryWithCapacity(capacity); +} +Node* CodeStubAssembler::AllocateNameDictionaryWithCapacity(Node* capacity) { + CSA_ASSERT(this, WordIsPowerOfTwo(capacity)); Node* length = EntryToIndex<NameDictionary>(capacity); - Node* store_size = - IntPtrAdd(WordShl(length, IntPtrConstant(kPointerSizeLog2)), - IntPtrConstant(NameDictionary::kHeaderSize)); + Node* store_size = IntPtrAdd(TimesPointerSize(length), + IntPtrConstant(NameDictionary::kHeaderSize)); Node* result = AllocateInNewSpace(store_size); Comment("Initialize NameDictionary"); @@ -1983,12 +2095,27 @@ Node* CodeStubAssembler::AllocateNameDictionary(Node* at_least_space_for) { return result; } +Node* CodeStubAssembler::CopyNameDictionary(Node* dictionary, + Label* large_object_fallback) { + CSA_ASSERT(this, IsHashTable(dictionary)); + Comment("Copy boilerplate property dict"); + Node* capacity = SmiUntag(GetCapacity<NameDictionary>(dictionary)); + CSA_ASSERT(this, IntPtrGreaterThanOrEqual(capacity, IntPtrConstant(0))); + GotoIf(UintPtrGreaterThan( + capacity, IntPtrConstant(NameDictionary::kMaxRegularCapacity)), + large_object_fallback); + Node* properties = AllocateNameDictionaryWithCapacity(capacity); + Node* length = SmiUntag(LoadFixedArrayBaseLength(dictionary)); + CopyFixedArrayElements(FAST_ELEMENTS, dictionary, properties, length, + SKIP_WRITE_BARRIER, INTPTR_PARAMETERS); + return properties; +} + Node* CodeStubAssembler::AllocateJSObjectFromMap(Node* map, Node* properties, Node* elements, AllocationFlags flags) { CSA_ASSERT(this, IsMap(map)); - Node* size = - IntPtrMul(LoadMapInstanceSize(map), IntPtrConstant(kPointerSize)); + Node* size = TimesPointerSize(LoadMapInstanceSize(map)); Node* object = AllocateInNewSpace(size, flags); StoreMapNoWriteBarrier(object, map); InitializeJSObjectFromMap(object, map, size, properties, elements); @@ -2839,6 +2966,10 @@ Node* CodeStubAssembler::ChangeNumberToIntPtr(Node* value) { return result.value(); } +Node* CodeStubAssembler::TimesPointerSize(Node* value) { + return WordShl(value, IntPtrConstant(kPointerSizeLog2)); +} + Node* CodeStubAssembler::ToThisValue(Node* context, Node* value, PrimitiveType primitive_type, char const* method_name) { @@ -2900,10 +3031,27 @@ Node* CodeStubAssembler::ToThisValue(Node* context, Node* value, BIND(&done_throw); { + const char* primitive_name = nullptr; + switch (primitive_type) { + case PrimitiveType::kBoolean: + primitive_name = "Boolean"; + break; + case PrimitiveType::kNumber: + primitive_name = "Number"; + break; + case PrimitiveType::kString: + primitive_name = "String"; + break; + case PrimitiveType::kSymbol: + primitive_name = "Symbol"; + break; + } + CHECK_NOT_NULL(primitive_name); + // The {value} is not a compatible receiver for this method. - CallRuntime(Runtime::kThrowNotGeneric, context, - HeapConstant(factory()->NewStringFromAsciiChecked(method_name, - TENURED))); + CallRuntime(Runtime::kThrowTypeError, context, + SmiConstant(MessageTemplate::kNotGeneric), + CStringConstant(method_name), CStringConstant(primitive_name)); Unreachable(); } @@ -3009,6 +3157,20 @@ Node* CodeStubAssembler::IsSequentialStringInstanceType(Node* instance_type) { Int32Constant(kSeqStringTag)); } +Node* CodeStubAssembler::IsConsStringInstanceType(Node* instance_type) { + CSA_ASSERT(this, IsStringInstanceType(instance_type)); + return Word32Equal( + Word32And(instance_type, Int32Constant(kStringRepresentationMask)), + Int32Constant(kConsStringTag)); +} + +Node* CodeStubAssembler::IsIndirectStringInstanceType(Node* instance_type) { + CSA_ASSERT(this, IsStringInstanceType(instance_type)); + STATIC_ASSERT(kIsIndirectStringMask == 0x1); + STATIC_ASSERT(kIsIndirectStringTag == 0x1); + return Word32And(instance_type, Int32Constant(kIsIndirectStringMask)); +} + Node* CodeStubAssembler::IsExternalStringInstanceType(Node* instance_type) { CSA_ASSERT(this, IsStringInstanceType(instance_type)); return Word32Equal( @@ -3031,20 +3193,23 @@ Node* CodeStubAssembler::IsJSReceiverInstanceType(Node* instance_type) { Int32Constant(FIRST_JS_RECEIVER_TYPE)); } +Node* CodeStubAssembler::IsJSReceiverMap(Node* map) { + return IsJSReceiverInstanceType(LoadMapInstanceType(map)); +} + Node* CodeStubAssembler::IsJSReceiver(Node* object) { - STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); - return IsJSReceiverInstanceType(LoadInstanceType(object)); + return IsJSReceiverMap(LoadMap(object)); } -Node* CodeStubAssembler::IsJSReceiverMap(Node* map) { +Node* CodeStubAssembler::IsJSObjectMap(Node* map) { STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); - return IsJSReceiverInstanceType(LoadMapInstanceType(map)); + CSA_ASSERT(this, IsMap(map)); + return Int32GreaterThanOrEqual(LoadMapInstanceType(map), + Int32Constant(FIRST_JS_OBJECT_TYPE)); } Node* CodeStubAssembler::IsJSObject(Node* object) { - STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE); - return Int32GreaterThanOrEqual(LoadInstanceType(object), - Int32Constant(FIRST_JS_RECEIVER_TYPE)); + return IsJSObjectMap(LoadMap(object)); } Node* CodeStubAssembler::IsJSGlobalProxy(Node* object) { @@ -3056,12 +3221,28 @@ Node* CodeStubAssembler::IsMap(Node* map) { return HasInstanceType(map, MAP_TYPE); } -Node* CodeStubAssembler::IsJSValue(Node* map) { - return HasInstanceType(map, JS_VALUE_TYPE); +Node* CodeStubAssembler::IsJSValueInstanceType(Node* instance_type) { + return Word32Equal(instance_type, Int32Constant(JS_VALUE_TYPE)); +} + +Node* CodeStubAssembler::IsJSValue(Node* object) { + return IsJSValueMap(LoadMap(object)); +} + +Node* CodeStubAssembler::IsJSValueMap(Node* map) { + return IsJSValueInstanceType(LoadMapInstanceType(map)); +} + +Node* CodeStubAssembler::IsJSArrayInstanceType(Node* instance_type) { + return Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)); } Node* CodeStubAssembler::IsJSArray(Node* object) { - return HasInstanceType(object, JS_ARRAY_TYPE); + return IsJSArrayMap(LoadMap(object)); +} + +Node* CodeStubAssembler::IsJSArrayMap(Node* map) { + return IsJSArrayInstanceType(LoadMapInstanceType(map)); } Node* CodeStubAssembler::IsWeakCell(Node* object) { @@ -3076,6 +3257,10 @@ Node* CodeStubAssembler::IsPropertyCell(Node* object) { return IsPropertyCellMap(LoadMap(object)); } +Node* CodeStubAssembler::IsAccessorInfo(Node* object) { + return IsAccessorInfoMap(LoadMap(object)); +} + Node* CodeStubAssembler::IsAccessorPair(Node* object) { return IsAccessorPairMap(LoadMap(object)); } @@ -3084,6 +3269,10 @@ Node* CodeStubAssembler::IsHeapNumber(Node* object) { return IsHeapNumberMap(LoadMap(object)); } +Node* CodeStubAssembler::IsFeedbackVector(Node* object) { + return IsFeedbackVectorMap(LoadMap(object)); +} + Node* CodeStubAssembler::IsName(Node* object) { return Int32LessThanOrEqual(LoadInstanceType(object), Int32Constant(LAST_NAME_TYPE)); @@ -3131,14 +3320,26 @@ Node* CodeStubAssembler::IsUnseededNumberDictionary(Node* object) { LoadRoot(Heap::kUnseededNumberDictionaryMapRootIndex)); } +Node* CodeStubAssembler::IsJSFunctionInstanceType(Node* instance_type) { + return Word32Equal(instance_type, Int32Constant(JS_FUNCTION_TYPE)); +} + Node* CodeStubAssembler::IsJSFunction(Node* object) { - return HasInstanceType(object, JS_FUNCTION_TYPE); + return IsJSFunctionMap(LoadMap(object)); +} + +Node* CodeStubAssembler::IsJSFunctionMap(Node* map) { + return IsJSFunctionInstanceType(LoadMapInstanceType(map)); } Node* CodeStubAssembler::IsJSTypedArray(Node* object) { return HasInstanceType(object, JS_TYPED_ARRAY_TYPE); } +Node* CodeStubAssembler::IsJSArrayBuffer(Node* object) { + return HasInstanceType(object, JS_ARRAY_BUFFER_TYPE); +} + Node* CodeStubAssembler::IsFixedTypedArray(Node* object) { Node* instance_type = LoadInstanceType(object); return Word32And( @@ -3181,6 +3382,18 @@ Node* CodeStubAssembler::IsNumberNormalized(Node* number) { return var_result.value(); } +Node* CodeStubAssembler::IsNumberPositive(Node* number) { + CSA_ASSERT(this, IsNumber(number)); + Node* const float_zero = Float64Constant(0.); + return Select(TaggedIsSmi(number), + [=] { return TaggedIsPositiveSmi(number); }, + [=] { + Node* v = LoadHeapNumberValue(number); + return Float64GreaterThanOrEqual(v, float_zero); + }, + MachineRepresentation::kWord32); +} + Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index, ParameterMode parameter_mode) { if (parameter_mode == SMI_PARAMETERS) CSA_ASSERT(this, TaggedIsSmi(index)); @@ -3298,64 +3511,59 @@ Node* CodeStubAssembler::StringFromCharCode(Node* code) { return var_result.value(); } -namespace { - // A wrapper around CopyStringCharacters which determines the correct string // encoding, allocates a corresponding sequential string, and then copies the // given character range using CopyStringCharacters. // |from_string| must be a sequential string. |from_index| and // |character_count| must be Smis s.t. // 0 <= |from_index| <= |from_index| + |character_count| < from_string.length. -Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context, - Node* from, Node* from_instance_type, - Node* from_index, Node* character_count) { - typedef CodeStubAssembler::Label Label; - typedef CodeStubAssembler::Variable Variable; - - Label end(a), one_byte_sequential(a), two_byte_sequential(a); - Variable var_result(a, MachineRepresentation::kTagged); +Node* CodeStubAssembler::AllocAndCopyStringCharacters(Node* context, Node* from, + Node* from_instance_type, + Node* from_index, + Node* character_count) { + Label end(this), one_byte_sequential(this), two_byte_sequential(this); + Variable var_result(this, MachineRepresentation::kTagged); - Node* const smi_zero = a->SmiConstant(Smi::kZero); + Node* const smi_zero = SmiConstant(Smi::kZero); - a->Branch(a->IsOneByteStringInstanceType(from_instance_type), - &one_byte_sequential, &two_byte_sequential); + Branch(IsOneByteStringInstanceType(from_instance_type), &one_byte_sequential, + &two_byte_sequential); // The subject string is a sequential one-byte string. - a->BIND(&one_byte_sequential); + BIND(&one_byte_sequential); { Node* result = - a->AllocateSeqOneByteString(context, a->SmiToWord(character_count)); - a->CopyStringCharacters(from, result, from_index, smi_zero, character_count, - String::ONE_BYTE_ENCODING, - String::ONE_BYTE_ENCODING, - CodeStubAssembler::SMI_PARAMETERS); + AllocateSeqOneByteString(context, SmiToWord(character_count)); + CopyStringCharacters(from, result, from_index, smi_zero, character_count, + String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING, + CodeStubAssembler::SMI_PARAMETERS); var_result.Bind(result); - a->Goto(&end); + Goto(&end); } // The subject string is a sequential two-byte string. - a->BIND(&two_byte_sequential); + BIND(&two_byte_sequential); { Node* result = - a->AllocateSeqTwoByteString(context, a->SmiToWord(character_count)); - a->CopyStringCharacters(from, result, from_index, smi_zero, character_count, - String::TWO_BYTE_ENCODING, - String::TWO_BYTE_ENCODING, - CodeStubAssembler::SMI_PARAMETERS); + AllocateSeqTwoByteString(context, SmiToWord(character_count)); + CopyStringCharacters(from, result, from_index, smi_zero, character_count, + String::TWO_BYTE_ENCODING, String::TWO_BYTE_ENCODING, + CodeStubAssembler::SMI_PARAMETERS); var_result.Bind(result); - a->Goto(&end); + Goto(&end); } - a->BIND(&end); + BIND(&end); return var_result.value(); } -} // namespace Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, - Node* to) { + Node* to, SubStringFlags flags) { + DCHECK(flags == SubStringFlags::NONE || + flags == SubStringFlags::FROM_TO_ARE_BOUNDED); VARIABLE(var_result, MachineRepresentation::kTagged); ToDirectStringAssembler to_direct(state(), string); Label end(this), runtime(this); @@ -3366,8 +3574,13 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, // Make sure that both from and to are non-negative smis. - GotoIfNot(TaggedIsPositiveSmi(from), &runtime); - GotoIfNot(TaggedIsPositiveSmi(to), &runtime); + if (flags == SubStringFlags::NONE) { + GotoIfNot(TaggedIsPositiveSmi(from), &runtime); + GotoIfNot(TaggedIsPositiveSmi(to), &runtime); + } else { + CSA_ASSERT(this, TaggedIsPositiveSmi(from)); + CSA_ASSERT(this, TaggedIsPositiveSmi(to)); + } Node* const substr_length = SmiSub(to, from); Node* const string_length = LoadStringLength(string); @@ -3435,7 +3648,7 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, GotoIf(to_direct.is_external(), &external_string); var_result.Bind(AllocAndCopyStringCharacters( - this, context, direct_string, instance_type, offset, substr_length)); + context, direct_string, instance_type, offset, substr_length)); Counters* counters = isolate()->counters(); IncrementCounter(counters->sub_string_native(), 1); @@ -3448,9 +3661,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, { Node* const fake_sequential_string = to_direct.PointerToString(&runtime); - var_result.Bind( - AllocAndCopyStringCharacters(this, context, fake_sequential_string, - instance_type, offset, substr_length)); + var_result.Bind(AllocAndCopyStringCharacters( + context, fake_sequential_string, instance_type, offset, substr_length)); Counters* counters = isolate()->counters(); IncrementCounter(counters->sub_string_native(), 1); @@ -3468,8 +3680,14 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, BIND(&original_string_or_invalid_length); { - // Longer than original string's length or negative: unsafe arguments. - GotoIf(SmiAbove(substr_length, string_length), &runtime); + if (flags == SubStringFlags::NONE) { + // Longer than original string's length or negative: unsafe arguments. + GotoIf(SmiAbove(substr_length, string_length), &runtime); + } else { + // with flag SubStringFlags::FROM_TO_ARE_BOUNDED, the only way we can + // get here is if substr_length is equal to string_length. + CSA_ASSERT(this, SmiEqual(substr_length, string_length)); + } // Equal length - check if {from, to} == {0, str.length}. GotoIf(SmiAbove(from, SmiConstant(Smi::kZero)), &runtime); @@ -3496,12 +3714,13 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, } ToDirectStringAssembler::ToDirectStringAssembler( - compiler::CodeAssemblerState* state, Node* string) + compiler::CodeAssemblerState* state, Node* string, Flags flags) : CodeStubAssembler(state), var_string_(this, MachineRepresentation::kTagged, string), var_instance_type_(this, MachineRepresentation::kWord32), var_offset_(this, MachineType::PointerRepresentation()), - var_is_external_(this, MachineRepresentation::kWord32) { + var_is_external_(this, MachineRepresentation::kWord32), + flags_(flags) { CSA_ASSERT(this, TaggedIsNotSmi(string)); CSA_ASSERT(this, IsString(string)); @@ -3558,16 +3777,20 @@ Node* ToDirectStringAssembler::TryToDirect(Label* if_bailout) { // Sliced string. Fetch parent and correct start index by offset. BIND(&if_issliced); { - Node* const string = var_string_.value(); - Node* const sliced_offset = - LoadAndUntagObjectField(string, SlicedString::kOffsetOffset); - var_offset_.Bind(IntPtrAdd(var_offset_.value(), sliced_offset)); + if (flags_ & kDontUnpackSlicedStrings) { + Goto(if_bailout); + } else { + Node* const string = var_string_.value(); + Node* const sliced_offset = + LoadAndUntagObjectField(string, SlicedString::kOffsetOffset); + var_offset_.Bind(IntPtrAdd(var_offset_.value(), sliced_offset)); - Node* const parent = LoadObjectField(string, SlicedString::kParentOffset); - var_string_.Bind(parent); - var_instance_type_.Bind(LoadInstanceType(parent)); + Node* const parent = LoadObjectField(string, SlicedString::kParentOffset); + var_string_.Bind(parent); + var_instance_type_.Bind(LoadInstanceType(parent)); - Goto(&dispatch); + Goto(&dispatch); + } } // Thin string. Fetch the actual string. @@ -4269,6 +4492,22 @@ Node* CodeStubAssembler::ToString(Node* context, Node* input) { return result.value(); } +Node* CodeStubAssembler::ToString_Inline(Node* const context, + Node* const input) { + VARIABLE(var_result, MachineRepresentation::kTagged, input); + Label stub_call(this, Label::kDeferred), out(this); + + GotoIf(TaggedIsSmi(input), &stub_call); + Branch(IsString(input), &out, &stub_call); + + BIND(&stub_call); + var_result.Bind(CallBuiltin(Builtins::kToString, context, input)); + Goto(&out); + + BIND(&out); + return var_result.value(); +} + Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) { Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this); VARIABLE(result, MachineRepresentation::kTagged); @@ -4346,6 +4585,15 @@ Node* CodeStubAssembler::ToSmiLength(Node* input, Node* const context, return result.value(); } +Node* CodeStubAssembler::ToLength_Inline(Node* const context, + Node* const input) { + Node* const smi_zero = SmiConstant(0); + return Select( + TaggedIsSmi(input), [=] { return SmiMax(input, smi_zero); }, + [=] { return CallBuiltin(Builtins::kToLength, context, input); }, + MachineRepresentation::kTagged); +} + Node* CodeStubAssembler::ToInteger(Node* context, Node* input, ToIntegerTruncationMode mode) { // We might need to loop once for ToNumber conversion. @@ -4461,7 +4709,8 @@ void CodeStubAssembler::Use(Label* label) { void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex, Variable* var_index, Label* if_keyisunique, - Variable* var_unique, Label* if_bailout) { + Variable* var_unique, Label* if_bailout, + Label* if_notinternalized) { DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep()); DCHECK_EQ(MachineRepresentation::kTagged, var_unique->rep()); Comment("TryToName"); @@ -4500,7 +4749,8 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex, STATIC_ASSERT(kNotInternalizedTag != 0); Node* not_internalized = Word32And(key_instance_type, Int32Constant(kIsNotInternalizedMask)); - GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), if_bailout); + GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), + if_notinternalized != nullptr ? if_notinternalized : if_bailout); Goto(if_keyisunique); BIND(&if_thinstring); @@ -4512,6 +4762,30 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex, Goto(if_keyisindex); } +void CodeStubAssembler::TryInternalizeString( + Node* string, Label* if_index, Variable* var_index, Label* if_internalized, + Variable* var_internalized, Label* if_not_internalized, Label* if_bailout) { + DCHECK(var_index->rep() == MachineType::PointerRepresentation()); + DCHECK(var_internalized->rep() == MachineRepresentation::kTagged); + Node* function = ExternalConstant( + ExternalReference::try_internalize_string_function(isolate())); + Node* result = CallCFunction1(MachineType::AnyTagged(), + MachineType::AnyTagged(), function, string); + Label internalized(this); + GotoIf(TaggedIsNotSmi(result), &internalized); + Node* word_result = SmiUntag(result); + GotoIf(WordEqual(word_result, IntPtrConstant(ResultSentinel::kNotFound)), + if_not_internalized); + GotoIf(WordEqual(word_result, IntPtrConstant(ResultSentinel::kUnsupported)), + if_bailout); + var_index->Bind(word_result); + Goto(if_index); + + BIND(&internalized); + var_internalized->Bind(result); + Goto(if_internalized); +} + template <typename Dictionary> Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) { Node* entry_index = IntPtrMul(entry, IntPtrConstant(Dictionary::kEntrySize)); @@ -4524,9 +4798,10 @@ template Node* CodeStubAssembler::EntryToIndex<GlobalDictionary>(Node*, int); template Node* CodeStubAssembler::EntryToIndex<SeededNumberDictionary>(Node*, int); +// This must be kept in sync with HashTableBase::ComputeCapacity(). Node* CodeStubAssembler::HashTableComputeCapacity(Node* at_least_space_for) { - Node* capacity = IntPtrRoundUpToPowerOfTwo32( - WordShl(at_least_space_for, IntPtrConstant(1))); + Node* capacity = IntPtrRoundUpToPowerOfTwo32(IntPtrAdd( + at_least_space_for, WordShr(at_least_space_for, IntPtrConstant(1)))); return IntPtrMax(capacity, IntPtrConstant(HashTableBase::kMinCapacity)); } @@ -4541,29 +4816,6 @@ Node* CodeStubAssembler::IntPtrMin(Node* left, Node* right) { } template <class Dictionary> -Node* CodeStubAssembler::GetNumberOfElements(Node* dictionary) { - return LoadFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex); -} - -template <class Dictionary> -void CodeStubAssembler::SetNumberOfElements(Node* dictionary, - Node* num_elements_smi) { - StoreFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex, - num_elements_smi, SKIP_WRITE_BARRIER); -} - -template <class Dictionary> -Node* CodeStubAssembler::GetNumberOfDeletedElements(Node* dictionary) { - return LoadFixedArrayElement(dictionary, - Dictionary::kNumberOfDeletedElementsIndex); -} - -template <class Dictionary> -Node* CodeStubAssembler::GetCapacity(Node* dictionary) { - return LoadFixedArrayElement(dictionary, Dictionary::kCapacityIndex); -} - -template <class Dictionary> Node* CodeStubAssembler::GetNextEnumerationIndex(Node* dictionary) { return LoadFixedArrayElement(dictionary, Dictionary::kNextEnumerationIndexIndex); @@ -5115,10 +5367,9 @@ void CodeStubAssembler::LoadPropertyFromFastObject(Node* object, Node* map, BIND(&if_inobject); { Comment("if_inobject"); - Node* field_offset = - IntPtrMul(IntPtrSub(LoadMapInstanceSize(map), - IntPtrSub(inobject_properties, field_index)), - IntPtrConstant(kPointerSize)); + Node* field_offset = TimesPointerSize( + IntPtrAdd(IntPtrSub(LoadMapInstanceSize(map), inobject_properties), + field_index)); Label if_double(this), if_tagged(this); Branch(Word32NotEqual(representation, @@ -5226,18 +5477,17 @@ Node* CodeStubAssembler::CallGetterIfAccessor(Node* value, Node* details, Node* context, Node* receiver, Label* if_bailout) { VARIABLE(var_value, MachineRepresentation::kTagged, value); - Label done(this); + Label done(this), if_accessor_info(this, Label::kDeferred); Node* kind = DecodeWord32<PropertyDetails::KindField>(details); GotoIf(Word32Equal(kind, Int32Constant(kData)), &done); // Accessor case. + GotoIfNot(IsAccessorPair(value), &if_accessor_info); + + // AccessorPair case. { Node* accessor_pair = value; - GotoIf(Word32Equal(LoadInstanceType(accessor_pair), - Int32Constant(ACCESSOR_INFO_TYPE)), - if_bailout); - CSA_ASSERT(this, IsAccessorPair(accessor_pair)); Node* getter = LoadObjectField(accessor_pair, AccessorPair::kGetterOffset); Node* getter_map = LoadMap(getter); Node* instance_type = LoadMapInstanceType(getter_map); @@ -5257,6 +5507,62 @@ Node* CodeStubAssembler::CallGetterIfAccessor(Node* value, Node* details, Goto(&done); } + // AccessorInfo case. + BIND(&if_accessor_info); + { + Node* accessor_info = value; + CSA_ASSERT(this, IsAccessorInfo(value)); + CSA_ASSERT(this, TaggedIsNotSmi(receiver)); + Label if_array(this), if_function(this), if_value(this); + + // Dispatch based on {receiver} instance type. + Node* receiver_map = LoadMap(receiver); + Node* receiver_instance_type = LoadMapInstanceType(receiver_map); + GotoIf(IsJSArrayInstanceType(receiver_instance_type), &if_array); + GotoIf(IsJSFunctionInstanceType(receiver_instance_type), &if_function); + Branch(IsJSValueInstanceType(receiver_instance_type), &if_value, + if_bailout); + + // JSArray AccessorInfo case. + BIND(&if_array); + { + // We only deal with the "length" accessor on JSArray. + GotoIfNot(IsLengthString( + LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), + if_bailout); + var_value.Bind(LoadJSArrayLength(receiver)); + Goto(&done); + } + + // JSFunction AccessorInfo case. + BIND(&if_function); + { + // We only deal with the "prototype" accessor on JSFunction here. + GotoIfNot(IsPrototypeString( + LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), + if_bailout); + GotoIf(IsSetWord32(LoadMapBitField(receiver_map), + 1 << Map::kHasNonInstancePrototype), + if_bailout); + var_value.Bind(LoadJSFunctionPrototype(receiver, if_bailout)); + Goto(&done); + } + + // JSValue AccessorInfo case. + BIND(&if_value); + { + // We only deal with the "length" accessor on JSValue string wrappers. + GotoIfNot(IsLengthString( + LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), + if_bailout); + Node* receiver_value = LoadJSValueValue(receiver); + GotoIfNot(TaggedIsNotSmi(receiver_value), if_bailout); + GotoIfNot(IsString(receiver_value), if_bailout); + var_value.Bind(LoadStringLength(receiver_value)); + Goto(&done); + } + } + BIND(&done); return var_value.value(); } @@ -5746,6 +6052,26 @@ void CodeStubAssembler::UpdateFeedback(Node* feedback, Node* feedback_vector, SKIP_WRITE_BARRIER); } +void CodeStubAssembler::CombineFeedback(Variable* existing_feedback, + Node* feedback) { + existing_feedback->Bind(SmiOr(existing_feedback->value(), feedback)); +} + +void CodeStubAssembler::CheckForAssociatedProtector(Node* name, + Label* if_protector) { + // This list must be kept in sync with LookupIterator::UpdateProtector! + // TODO(jkummerow): Would it be faster to have a bit in Symbol::flags()? + GotoIf(WordEqual(name, LoadRoot(Heap::kconstructor_stringRootIndex)), + if_protector); + GotoIf(WordEqual(name, LoadRoot(Heap::kiterator_symbolRootIndex)), + if_protector); + GotoIf(WordEqual(name, LoadRoot(Heap::kspecies_symbolRootIndex)), + if_protector); + GotoIf(WordEqual(name, LoadRoot(Heap::kis_concat_spreadable_symbolRootIndex)), + if_protector); + // Fall through if no case matched. +} + Node* CodeStubAssembler::LoadReceiverMap(Node* receiver) { return Select(TaggedIsSmi(receiver), [=] { return LoadRoot(Heap::kHeapNumberMapRootIndex); }, @@ -6367,6 +6693,78 @@ Node* CodeStubAssembler::CreateWeakCellInFeedbackVector(Node* feedback_vector, return cell; } +void CodeStubAssembler::HandleSlackTracking(Node* context, Node* object, + Node* initial_map, + int start_offset) { + Node* instance_size_words = ChangeUint32ToWord(LoadObjectField( + initial_map, Map::kInstanceSizeOffset, MachineType::Uint8())); + Node* instance_size = TimesPointerSize(instance_size_words); + + // Perform in-object slack tracking if requested. + Node* bit_field3 = LoadMapBitField3(initial_map); + Label end(this), slack_tracking(this), finalize(this, Label::kDeferred); + GotoIf(IsSetWord32<Map::ConstructionCounter>(bit_field3), &slack_tracking); + + // Initialize remaining fields. + { + Comment("no slack tracking"); + InitializeFieldsWithRoot(object, IntPtrConstant(start_offset), + instance_size, Heap::kUndefinedValueRootIndex); + Goto(&end); + } + + { + BIND(&slack_tracking); + + // Decrease generous allocation count. + STATIC_ASSERT(Map::ConstructionCounter::kNext == 32); + Comment("update allocation count"); + Node* new_bit_field3 = Int32Sub( + bit_field3, Int32Constant(1 << Map::ConstructionCounter::kShift)); + StoreObjectFieldNoWriteBarrier(initial_map, Map::kBitField3Offset, + new_bit_field3, + MachineRepresentation::kWord32); + GotoIf(IsClearWord32<Map::ConstructionCounter>(new_bit_field3), &finalize); + + Node* unused_fields = LoadObjectField( + initial_map, Map::kUnusedPropertyFieldsOffset, MachineType::Uint8()); + Node* used_size = IntPtrSub( + instance_size, TimesPointerSize(ChangeUint32ToWord(unused_fields))); + + Comment("initialize filler fields (no finalize)"); + InitializeFieldsWithRoot(object, used_size, instance_size, + Heap::kOnePointerFillerMapRootIndex); + + Comment("initialize undefined fields (no finalize)"); + InitializeFieldsWithRoot(object, IntPtrConstant(start_offset), used_size, + Heap::kUndefinedValueRootIndex); + Goto(&end); + } + + { + // Finalize the instance size. + BIND(&finalize); + + Node* unused_fields = LoadObjectField( + initial_map, Map::kUnusedPropertyFieldsOffset, MachineType::Uint8()); + Node* used_size = IntPtrSub( + instance_size, TimesPointerSize(ChangeUint32ToWord(unused_fields))); + + Comment("initialize filler fields (finalize)"); + InitializeFieldsWithRoot(object, used_size, instance_size, + Heap::kOnePointerFillerMapRootIndex); + + Comment("initialize undefined fields (finalize)"); + InitializeFieldsWithRoot(object, IntPtrConstant(start_offset), used_size, + Heap::kUndefinedValueRootIndex); + + CallRuntime(Runtime::kFinalizeInstanceSize, context, initial_map); + Goto(&end); + } + + BIND(&end); +} + Node* CodeStubAssembler::BuildFastLoop( const CodeStubAssembler::VariableList& vars, Node* start_index, Node* end_index, const FastLoopBody& body, int increment, @@ -6593,7 +6991,8 @@ void CodeStubAssembler::GotoUnlessNumberLessThan(Node* lhs, Node* rhs, Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, Node* lhs, Node* rhs, - Node* context) { + Node* context, + Variable* var_type_feedback) { Label return_true(this), return_false(this), end(this); VARIABLE(result, MachineRepresentation::kTagged); @@ -6606,8 +7005,14 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, // conversions. VARIABLE(var_lhs, MachineRepresentation::kTagged, lhs); VARIABLE(var_rhs, MachineRepresentation::kTagged, rhs); - Variable* loop_vars[2] = {&var_lhs, &var_rhs}; - Label loop(this, 2, loop_vars); + VariableList loop_variable_list({&var_lhs, &var_rhs}, zone()); + if (var_type_feedback != nullptr) { + // Initialize the type feedback to None. The current feedback is combined + // with the previous feedback. + var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kNone)); + loop_variable_list.Add(var_type_feedback, zone()); + } + Label loop(this, loop_variable_list); Goto(&loop); BIND(&loop); { @@ -6628,6 +7033,10 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, BIND(&if_rhsissmi); { // Both {lhs} and {rhs} are Smi, so just perform a fast Smi comparison. + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kSignedSmall)); + } switch (mode) { case kLessThan: BranchIfSmiLessThan(lhs, rhs, &return_true, &return_false); @@ -6657,6 +7066,10 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, { // Convert the {lhs} and {rhs} to floating point values, and // perform a floating point comparison. + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kNumber)); + } var_fcmp_lhs.Bind(SmiToFloat64(lhs)); var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); Goto(&do_fcmp); @@ -6664,6 +7077,11 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, BIND(&if_rhsisnotnumber); { + // The {rhs} is not a HeapNumber and {lhs} is an Smi. + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } // Convert the {rhs} to a Number; we don't need to perform the // dedicated ToPrimitive(rhs, hint Number) operation, as the // ToNumber(rhs) will by itself already invoke ToPrimitive with @@ -6694,6 +7112,10 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, { // Convert the {lhs} and {rhs} to floating point values, and // perform a floating point comparison. + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kNumber)); + } var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); var_fcmp_rhs.Bind(SmiToFloat64(rhs)); Goto(&do_fcmp); @@ -6701,6 +7123,11 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, BIND(&if_lhsisnotnumber); { + // The {lhs} is not a HeapNumber and {rhs} is an Smi. + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } // Convert the {lhs} to a Number; we don't need to perform the // dedicated ToPrimitive(lhs, hint Number) operation, as the // ToNumber(lhs) will by itself already invoke ToPrimitive with @@ -6731,6 +7158,10 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, { // Convert the {lhs} and {rhs} to floating point values, and // perform a floating point comparison. + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kNumber)); + } var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); Goto(&do_fcmp); @@ -6738,6 +7169,11 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, BIND(&if_rhsisnotnumber); { + // The {rhs} is not a HeapNumber and {lhs} is a HeapNumber. + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } // Convert the {rhs} to a Number; we don't need to perform // dedicated ToPrimitive(rhs, hint Number) operation, as the // ToNumber(rhs) will by itself already invoke ToPrimitive with @@ -6772,6 +7208,10 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, BIND(&if_rhsisstring); { // Both {lhs} and {rhs} are strings. + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kString)); + } switch (mode) { case kLessThan: result.Bind(CallStub(CodeFactory::StringLessThan(isolate()), @@ -6801,6 +7241,11 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, BIND(&if_rhsisnotstring); { + // The {lhs} is a String and {rhs} is not a String. + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } // The {lhs} is a String, while {rhs} is neither a Number nor a // String, so we need to call ToPrimitive(rhs, hint Number) if // {rhs} is a receiver or ToNumber(lhs) and ToNumber(rhs) in the @@ -6833,6 +7278,41 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, BIND(&if_lhsisnotstring); { + if (var_type_feedback != nullptr) { + // The {lhs} is not an Smi, HeapNumber or String and {rhs} is not + // an Smi: collect NumberOrOddball feedback if {lhs} is an Oddball + // and {rhs} is either a HeapNumber or Oddball. + Label collect_any_feedback(this), collect_oddball_feedback(this), + collect_feedback_done(this); + GotoIfNot( + Word32Equal(lhs_instance_type, Int32Constant(ODDBALL_TYPE)), + &collect_any_feedback); + + Node* rhs_instance_type = LoadMapInstanceType(rhs_map); + GotoIf(Word32Equal(rhs_instance_type, + Int32Constant(HEAP_NUMBER_TYPE)), + &collect_oddball_feedback); + Branch( + Word32Equal(rhs_instance_type, Int32Constant(ODDBALL_TYPE)), + &collect_oddball_feedback, &collect_any_feedback); + + BIND(&collect_oddball_feedback); + { + CombineFeedback( + var_type_feedback, + SmiConstant(CompareOperationFeedback::kNumberOrOddball)); + Goto(&collect_feedback_done); + } + + BIND(&collect_any_feedback); + { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + Goto(&collect_feedback_done); + } + + BIND(&collect_feedback_done); + } // The {lhs} is neither a Number nor a String, so we need to call // ToPrimitive(lhs, hint Number) if {lhs} is a receiver or // ToNumber(lhs) and ToNumber(rhs) in the other cases. @@ -6905,52 +7385,91 @@ Node* CodeStubAssembler::RelationalComparison(RelationalComparisonMode mode, return result.value(); } -namespace { +Node* CodeStubAssembler::CollectFeedbackForString(Node* instance_type) { + Node* feedback = SelectSmiConstant( + Word32Equal( + Word32And(instance_type, Int32Constant(kIsNotInternalizedMask)), + Int32Constant(kInternalizedTag)), + CompareOperationFeedback::kInternalizedString, + CompareOperationFeedback::kString); + return feedback; +} -void GenerateEqual_Same(CodeStubAssembler* assembler, Node* value, - CodeStubAssembler::Label* if_equal, - CodeStubAssembler::Label* if_notequal) { +void CodeStubAssembler::GenerateEqual_Same(Node* value, Label* if_equal, + Label* if_notequal, + Variable* var_type_feedback) { // In case of abstract or strict equality checks, we need additional checks // for NaN values because they are not considered equal, even if both the // left and the right hand side reference exactly the same value. - typedef CodeStubAssembler::Label Label; - // Check if {value} is a Smi or a HeapObject. - Label if_valueissmi(assembler), if_valueisnotsmi(assembler); - assembler->Branch(assembler->TaggedIsSmi(value), &if_valueissmi, - &if_valueisnotsmi); + Label if_valueissmi(this), if_valueisnotsmi(this); + Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi); - assembler->BIND(&if_valueisnotsmi); + BIND(&if_valueisnotsmi); { // Load the map of {value}. - Node* value_map = assembler->LoadMap(value); + Node* value_map = LoadMap(value); // Check if {value} (and therefore {rhs}) is a HeapNumber. - Label if_valueisnumber(assembler), if_valueisnotnumber(assembler); - assembler->Branch(assembler->IsHeapNumberMap(value_map), &if_valueisnumber, - &if_valueisnotnumber); + Label if_valueisnumber(this), if_valueisnotnumber(this); + Branch(IsHeapNumberMap(value_map), &if_valueisnumber, &if_valueisnotnumber); - assembler->BIND(&if_valueisnumber); + BIND(&if_valueisnumber); { + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kNumber)); + } + // Convert {value} (and therefore {rhs}) to floating point value. - Node* value_value = assembler->LoadHeapNumberValue(value); + Node* value_value = LoadHeapNumberValue(value); // Check if the HeapNumber value is a NaN. - assembler->BranchIfFloat64IsNaN(value_value, if_notequal, if_equal); + BranchIfFloat64IsNaN(value_value, if_notequal, if_equal); } - assembler->BIND(&if_valueisnotnumber); - assembler->Goto(if_equal); + BIND(&if_valueisnotnumber); + if (var_type_feedback != nullptr) { + // Collect type feedback. + Node* instance_type = LoadMapInstanceType(value_map); + + Label if_valueisstring(this), if_valueisnotstring(this); + Branch(IsStringInstanceType(instance_type), &if_valueisstring, + &if_valueisnotstring); + + BIND(&if_valueisstring); + { + CombineFeedback(var_type_feedback, + CollectFeedbackForString(instance_type)); + Goto(if_equal); + } + + BIND(&if_valueisnotstring); + { + var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kAny)); + GotoIfNot(IsJSReceiverInstanceType(instance_type), if_equal); + + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kReceiver)); + Goto(if_equal); + } + } else { + Goto(if_equal); + } } - assembler->BIND(&if_valueissmi); - assembler->Goto(if_equal); + BIND(&if_valueissmi); + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kSignedSmall)); + } + Goto(if_equal); } -} // namespace // ES6 section 7.2.12 Abstract Equality Comparison -Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { +Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context, + Variable* var_type_feedback) { // This is a slightly optimized version of Object::Equals represented as // scheduled TurboFan graph utilizing the CodeStubAssembler. Whenever you // change something functionality wise in here, remember to update the @@ -6969,8 +7488,14 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { // conversions. VARIABLE(var_lhs, MachineRepresentation::kTagged, lhs); VARIABLE(var_rhs, MachineRepresentation::kTagged, rhs); - Variable* loop_vars[2] = {&var_lhs, &var_rhs}; - Label loop(this, 2, loop_vars); + VariableList loop_variable_list({&var_lhs, &var_rhs}, zone()); + if (var_type_feedback != nullptr) { + // Initialize the type feedback to None. The current feedback is combined + // with the previous feedback. + var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kNone)); + loop_variable_list.Add(var_type_feedback, zone()); + } + Label loop(this, loop_variable_list); Goto(&loop); BIND(&loop); { @@ -6986,7 +7511,7 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { { // The {lhs} and {rhs} reference the exact same value, yet we need special // treatment for HeapNumber, as NaN is not equal to NaN. - GenerateEqual_Same(this, lhs, &if_equal, &if_notequal); + GenerateEqual_Same(lhs, &if_equal, &if_notequal, var_type_feedback); } BIND(&if_notsame); @@ -7004,6 +7529,10 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { BIND(&if_rhsissmi); // We have already checked for {lhs} and {rhs} being the same value, so // if both are Smis when we get here they must not be equal. + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kSignedSmall)); + } Goto(&if_notequal); BIND(&if_rhsisnotsmi); @@ -7021,11 +7550,21 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { // perform a floating point comparison. var_fcmp_lhs.Bind(SmiToFloat64(lhs)); var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kNumber)); + } Goto(&do_fcmp); } BIND(&if_rhsisnotnumber); { + // The {lhs} is Smi and {rhs} is not HeapNumber or Smi. + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } + // Load the instance type of the {rhs}. Node* rhs_instance_type = LoadMapInstanceType(rhs_map); @@ -7132,7 +7671,7 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { Switch(lhs_instance_type, &if_lhsisreceiver, case_values, case_labels, arraysize(case_values)); for (int32_t i = 0; i < FIRST_NONSTRING_TYPE; ++i) { - Bind(case_labels[i]); + BIND(case_labels[i]); Goto(&if_lhsisstring); delete case_labels[i]; } @@ -7151,6 +7690,14 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { // string comparison then. Callable callable = CodeFactory::StringEqual(isolate()); result.Bind(CallStub(callable, context, lhs, rhs)); + if (var_type_feedback != nullptr) { + Node* lhs_feedback = + CollectFeedbackForString(lhs_instance_type); + Node* rhs_feedback = + CollectFeedbackForString(rhs_instance_type); + CombineFeedback(var_type_feedback, + SmiOr(lhs_feedback, rhs_feedback)); + } Goto(&end); } @@ -7162,6 +7709,10 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { // handling below (for {rhs} being a String). var_lhs.Bind(rhs); var_rhs.Bind(lhs); + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } Goto(&loop); } } @@ -7179,6 +7730,10 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { // perform a floating point comparison. var_fcmp_lhs.Bind(LoadHeapNumberValue(lhs)); var_fcmp_rhs.Bind(LoadHeapNumberValue(rhs)); + if (var_type_feedback != nullptr) { + CombineFeedback(var_type_feedback, + SmiConstant(CompareOperationFeedback::kNumber)); + } Goto(&do_fcmp); } @@ -7187,6 +7742,12 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { // The {lhs} is a Number, the {rhs} is some other HeapObject. Label if_rhsisstring(this, Label::kDeferred), if_rhsisnotstring(this); + + if (var_type_feedback != nullptr) { + // The {lhs} is number and {rhs} is not Smi or HeapNumber. + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, &if_rhsisnotstring); @@ -7242,6 +7803,11 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { BIND(&if_lhsisoddball); { + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } + // The {lhs} is an Oddball and {rhs} is some other HeapObject. Label if_lhsisboolean(this), if_lhsisnotboolean(this); Node* boolean_map = BooleanMapConstant(); @@ -7285,6 +7851,11 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { BIND(&if_lhsissymbol); { + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } + // Check if the {rhs} is a JSReceiver. Label if_rhsisreceiver(this), if_rhsisnotreceiver(this); STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); @@ -7320,6 +7891,13 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { BIND(&if_rhsisreceiver); { + if (var_type_feedback != nullptr) { + // The {lhs} and {rhs} are receivers. + CombineFeedback( + var_type_feedback, + SmiConstant(CompareOperationFeedback::kReceiver)); + } + // Both {lhs} and {rhs} are different JSReceiver references, so // this cannot be considered equal. Goto(&if_notequal); @@ -7327,6 +7905,11 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { BIND(&if_rhsisnotreceiver); { + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kAny)); + } + // Check if {rhs} is Null or Undefined (an undetectable check // is sufficient here, since we already know that {rhs} is not // a JSReceiver). @@ -7399,7 +7982,8 @@ Node* CodeStubAssembler::Equal(Node* lhs, Node* rhs, Node* context) { return result.value(); } -Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { +Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs, + Variable* var_type_feedback) { // Here's pseudo-code for the algorithm below in case of kDontNegateResult // mode; for kNegateResult mode we properly negate the result. // @@ -7455,7 +8039,10 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { { // The {lhs} and {rhs} reference the exact same value, yet we need special // treatment for HeapNumber, as NaN is not equal to NaN. - GenerateEqual_Same(this, lhs, &if_equal, &if_notequal); + if (var_type_feedback != nullptr) { + var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kNone)); + } + GenerateEqual_Same(lhs, &if_equal, &if_notequal, var_type_feedback); } BIND(&if_notsame); @@ -7463,6 +8050,10 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { // The {lhs} and {rhs} reference different objects, yet for Smi, HeapNumber // and String they can still be considered equal. + if (var_type_feedback != nullptr) { + var_type_feedback->Bind(SmiConstant(CompareOperationFeedback::kAny)); + } + // Check if {lhs} is a Smi or a HeapObject. Label if_lhsissmi(this), if_lhsisnotsmi(this); Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); @@ -7488,6 +8079,11 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { Node* lhs_value = LoadHeapNumberValue(lhs); Node* rhs_value = SmiToFloat64(rhs); + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kNumber)); + } + // Perform a floating point comparison of {lhs} and {rhs}. Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); } @@ -7507,6 +8103,11 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { Node* lhs_value = LoadHeapNumberValue(lhs); Node* rhs_value = LoadHeapNumberValue(rhs); + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kNumber)); + } + // Perform a floating point comparison of {lhs} and {rhs}. Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); } @@ -7530,6 +8131,9 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { // Load the instance type of {lhs}. Node* lhs_instance_type = LoadMapInstanceType(lhs_map); + // Load the instance type of {rhs}. + Node* rhs_instance_type = LoadInstanceType(rhs); + // Check if {lhs} is a String. Label if_lhsisstring(this), if_lhsisnotstring(this); Branch(IsStringInstanceType(lhs_instance_type), &if_lhsisstring, @@ -7537,9 +8141,6 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { BIND(&if_lhsisstring); { - // Load the instance type of {rhs}. - Node* rhs_instance_type = LoadInstanceType(rhs); - // Check if {rhs} is also a String. Label if_rhsisstring(this, Label::kDeferred), if_rhsisnotstring(this); @@ -7549,6 +8150,13 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { BIND(&if_rhsisstring); { Callable callable = CodeFactory::StringEqual(isolate()); + if (var_type_feedback != nullptr) { + Node* lhs_feedback = + CollectFeedbackForString(lhs_instance_type); + Node* rhs_feedback = + CollectFeedbackForString(rhs_instance_type); + var_type_feedback->Bind(SmiOr(lhs_feedback, rhs_feedback)); + } result.Bind(CallStub(callable, NoContextConstant(), lhs, rhs)); Goto(&end); } @@ -7558,6 +8166,14 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { } BIND(&if_lhsisnotstring); + if (var_type_feedback != nullptr) { + GotoIfNot(IsJSReceiverInstanceType(lhs_instance_type), + &if_notequal); + GotoIfNot(IsJSReceiverInstanceType(rhs_instance_type), + &if_notequal); + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kReceiver)); + } Goto(&if_notequal); } } @@ -7574,6 +8190,10 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); BIND(&if_rhsissmi); + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kSignedSmall)); + } Goto(&if_notequal); BIND(&if_rhsisnotsmi); @@ -7591,6 +8211,11 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs) { Node* lhs_value = SmiToFloat64(lhs); Node* rhs_value = LoadHeapNumberValue(rhs); + if (var_type_feedback != nullptr) { + var_type_feedback->Bind( + SmiConstant(CompareOperationFeedback::kNumber)); + } + // Perform a floating point comparison of {lhs} and {rhs}. Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); } @@ -8333,14 +8958,13 @@ Node* CodeStubAssembler::AllocateJSArrayIterator(Node* array, Node* array_map, return iterator; } -Node* CodeStubAssembler::ArraySpeciesCreate(Node* context, Node* originalArray, - Node* len) { - // TODO(mvstanton): Install a fast path as well, which avoids the runtime +Node* CodeStubAssembler::TypedArraySpeciesCreateByLength(Node* context, + Node* originalArray, + Node* len) { + // TODO(tebbi): Install a fast path as well, which avoids the runtime // call. - Node* constructor = - CallRuntime(Runtime::kArraySpeciesConstructor, context, originalArray); - return ConstructJS(CodeFactory::Construct(isolate()), context, constructor, - len); + return CallRuntime(Runtime::kTypedArraySpeciesCreateByLength, context, + originalArray, len); } Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) { @@ -8358,8 +8982,8 @@ CodeStubArguments::CodeStubArguments(CodeStubAssembler* assembler, Node* argc, argc_mode_(mode), argc_(argc), arguments_(nullptr), - fp_(fp != nullptr ? fp : assembler->LoadFramePointer()) { - Node* offset = assembler->ElementOffsetFromIndex( + fp_(fp != nullptr ? fp : assembler_->LoadFramePointer()) { + Node* offset = assembler_->ElementOffsetFromIndex( argc_, FAST_ELEMENTS, mode, (StandardFrameConstants::kFixedSlotCountAboveFp - 1) * kPointerSize); arguments_ = assembler_->IntPtrAdd(fp_, offset); @@ -8392,6 +9016,28 @@ Node* CodeStubArguments::AtIndex(int index) const { return AtIndex(assembler_->IntPtrConstant(index)); } +Node* CodeStubArguments::GetOptionalArgumentValue(int index, + Node* default_value) { + typedef CodeStubAssembler::Variable Variable; + Variable result(assembler_, MachineRepresentation::kTagged); + CodeStubAssembler::Label argument_missing(assembler_), + argument_done(assembler_, &result); + + assembler_->GotoIf(assembler_->UintPtrOrSmiGreaterThanOrEqual( + assembler_->IntPtrOrSmiConstant(index, argc_mode_), + argc_, argc_mode_), + &argument_missing); + result.Bind(AtIndex(index)); + assembler_->Goto(&argument_done); + + assembler_->BIND(&argument_missing); + result.Bind(default_value); + assembler_->Goto(&argument_done); + + assembler_->BIND(&argument_done); + return result.value(); +} + void CodeStubArguments::ForEach( const CodeStubAssembler::VariableList& vars, const CodeStubArguments::ForEachBodyFunction& body, Node* first, Node* last, @@ -8442,6 +9088,11 @@ Node* CodeStubAssembler::IsHoleyFastElementsKind(Node* elements_kind) { return Word32Equal(holey_elements, Int32Constant(1)); } +Node* CodeStubAssembler::IsElementsKindGreaterThan( + Node* target_kind, ElementsKind reference_kind) { + return Int32GreaterThan(target_kind, Int32Constant(reference_kind)); +} + Node* CodeStubAssembler::IsDebugActive() { Node* is_debug_active = Load( MachineType::Uint8(), |