// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/ic/accessor-assembler.h" #include "src/code-factory.h" #include "src/code-stubs.h" #include "src/counters.h" #include "src/ic/handler-configuration.h" #include "src/ic/ic.h" #include "src/ic/stub-cache.h" #include "src/objects-inl.h" #include "src/objects/module.h" namespace v8 { namespace internal { using compiler::CodeAssemblerState; using compiler::Node; template <typename T> using TNode = compiler::TNode<T>; template <typename T> using SloppyTNode = compiler::SloppyTNode<T>; //////////////////// Private helpers. // Loads dataX field from the DataHandler object. TNode<MaybeObject> AccessorAssembler::LoadHandlerDataField( SloppyTNode<DataHandler> handler, int data_index) { #ifdef DEBUG TNode<Map> handler_map = LoadMap(handler); TNode<Int32T> instance_type = LoadMapInstanceType(handler_map); #endif CSA_ASSERT(this, Word32Or(InstanceTypeEqual(instance_type, LOAD_HANDLER_TYPE), InstanceTypeEqual(instance_type, STORE_HANDLER_TYPE))); int offset = 0; int minimum_size = 0; switch (data_index) { case 1: offset = DataHandler::kData1Offset; minimum_size = DataHandler::kSizeWithData1; break; case 2: offset = DataHandler::kData2Offset; minimum_size = DataHandler::kSizeWithData2; break; case 3: offset = DataHandler::kData3Offset; minimum_size = DataHandler::kSizeWithData3; break; default: UNREACHABLE(); break; } USE(minimum_size); CSA_ASSERT(this, UintPtrGreaterThanOrEqual( LoadMapInstanceSizeInWords(handler_map), IntPtrConstant(minimum_size / kPointerSize))); return LoadMaybeWeakObjectField(handler, offset); } TNode<MaybeObject> AccessorAssembler::TryMonomorphicCase( Node* slot, Node* vector, Node* receiver_map, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss) { Comment("TryMonomorphicCase"); DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep()); // TODO(ishell): add helper class that hides offset computations for a series // of loads. CSA_ASSERT(this, IsFeedbackVector(vector), vector); int32_t header_size = FeedbackVector::kFeedbackSlotsOffset - kHeapObjectTag; // Adding |header_size| with a separate IntPtrAdd rather than passing it // into ElementOffsetFromIndex() allows it to be folded into a single // [base, index, offset] indirect memory access on x64. Node* offset = ElementOffsetFromIndex(slot, HOLEY_ELEMENTS, SMI_PARAMETERS); TNode<MaybeObject> feedback = ReinterpretCast<MaybeObject>( Load(MachineType::AnyTagged(), vector, IntPtrAdd(offset, IntPtrConstant(header_size)))); // Try to quickly handle the monomorphic case without knowing for sure // if we have a weak reference in feedback. GotoIf(IsNotWeakReferenceTo(feedback, CAST(receiver_map)), if_miss); TNode<MaybeObject> handler = UncheckedCast<MaybeObject>( Load(MachineType::AnyTagged(), vector, IntPtrAdd(offset, IntPtrConstant(header_size + kPointerSize)))); *var_handler = handler; Goto(if_handler); return feedback; } void AccessorAssembler::HandlePolymorphicCase( Node* receiver_map, TNode<WeakFixedArray> feedback, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss, int min_feedback_capacity) { Comment("HandlePolymorphicCase"); DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep()); // Deferred so the unrolled case can omit frame construction in bytecode // handler. Label loop(this, Label::kDeferred); // Iterate {feedback} array. const int kEntrySize = 2; // Loading feedback's length is delayed until we need it when looking past // the first {min_feedback_capacity} (map, handler) pairs. Node* length = nullptr; CSA_ASSERT(this, SmiGreaterThanOrEqual( LoadWeakFixedArrayLength(feedback), SmiConstant(min_feedback_capacity * kEntrySize))); const int kUnrolledIterations = IC::kMaxPolymorphicMapCount; for (int i = 0; i < kUnrolledIterations; i++) { int map_index = i * kEntrySize; int handler_index = i * kEntrySize + 1; if (i >= min_feedback_capacity) { if (length == nullptr) length = LoadWeakFixedArrayLength(feedback); GotoIf(SmiGreaterThanOrEqual(SmiConstant(handler_index), CAST(length)), if_miss); } Label next_entry(this); TNode<MaybeObject> maybe_cached_map = LoadWeakFixedArrayElement(feedback, map_index); CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_cached_map)); GotoIf(IsNotWeakReferenceTo(maybe_cached_map, CAST(receiver_map)), &next_entry); // Found, now call handler. TNode<MaybeObject> handler = LoadWeakFixedArrayElement(feedback, handler_index); *var_handler = handler; Goto(if_handler); BIND(&next_entry); } Goto(&loop); // Loop from {kUnrolledIterations}*kEntrySize to {length}. BIND(&loop); Node* start_index = IntPtrConstant(kUnrolledIterations * kEntrySize); Node* end_index = LoadAndUntagWeakFixedArrayLength(feedback); BuildFastLoop( start_index, end_index, [this, receiver_map, feedback, if_handler, var_handler](Node* index) { Label next_entry(this); TNode<MaybeObject> maybe_cached_map = LoadWeakFixedArrayElement(feedback, index); CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_cached_map)); GotoIf(IsNotWeakReferenceTo(maybe_cached_map, CAST(receiver_map)), &next_entry); // Found, now call handler. TNode<MaybeObject> handler = LoadWeakFixedArrayElement(feedback, index, kPointerSize); *var_handler = handler; Goto(if_handler); BIND(&next_entry); }, kEntrySize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); // The loop falls through if no handler was found. Goto(if_miss); } void AccessorAssembler::HandleLoadICHandlerCase( const LoadICParameters* p, TNode<Object> handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode, OnNonExistent on_nonexistent, ElementSupport support_elements) { Comment("have_handler"); VARIABLE(var_holder, MachineRepresentation::kTagged, p->holder); VARIABLE(var_smi_handler, MachineRepresentation::kTagged, handler); Variable* vars[] = {&var_holder, &var_smi_handler}; Label if_smi_handler(this, 2, vars); Label try_proto_handler(this, Label::kDeferred), call_handler(this, Label::kDeferred); Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_handler); // |handler| is a Smi, encoding what to do. See SmiHandler methods // for the encoding format. BIND(&if_smi_handler); { HandleLoadICSmiHandlerCase(p, var_holder.value(), var_smi_handler.value(), handler, miss, exit_point, on_nonexistent, support_elements); } BIND(&try_proto_handler); { GotoIf(IsCodeMap(LoadMap(CAST(handler))), &call_handler); HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler, &if_smi_handler, miss, exit_point, ic_mode); } BIND(&call_handler); { exit_point->ReturnCallStub(LoadWithVectorDescriptor{}, handler, p->context, p->receiver, p->name, p->slot, p->vector); } } void AccessorAssembler::HandleLoadCallbackProperty(const LoadICParameters* p, TNode<JSObject> holder, TNode<WordT> handler_word, ExitPoint* exit_point) { Comment("native_data_property_load"); Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word); Label runtime(this, Label::kDeferred); Callable callable = CodeFactory::ApiGetter(isolate()); TNode<AccessorInfo> accessor_info = CAST(LoadDescriptorValue(LoadMap(holder), descriptor)); GotoIf(IsRuntimeCallStatsEnabled(), &runtime); exit_point->ReturnCallStub(callable, p->context, p->receiver, holder, accessor_info); BIND(&runtime); exit_point->ReturnCallRuntime(Runtime::kLoadCallbackProperty, p->context, p->receiver, holder, accessor_info, p->name); } void AccessorAssembler::HandleLoadAccessor( const LoadICParameters* p, TNode<CallHandlerInfo> call_handler_info, TNode<WordT> handler_word, TNode<DataHandler> handler, TNode<IntPtrT> handler_kind, ExitPoint* exit_point) { Comment("api_getter"); Label runtime(this, Label::kDeferred); // Context is stored either in data2 or data3 field depending on whether // the access check is enabled for this handler or not. TNode<MaybeObject> maybe_context = Select<MaybeObject>( IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word), [=] { return LoadHandlerDataField(handler, 3); }, [=] { return LoadHandlerDataField(handler, 2); }); CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_context)); CSA_CHECK(this, IsNotClearedWeakHeapObject(maybe_context)); TNode<Object> context = ToWeakHeapObject(maybe_context); GotoIf(IsRuntimeCallStatsEnabled(), &runtime); { TNode<Foreign> foreign = CAST( LoadObjectField(call_handler_info, CallHandlerInfo::kJsCallbackOffset)); TNode<WordT> callback = TNode<WordT>::UncheckedCast(LoadObjectField( foreign, Foreign::kForeignAddressOffset, MachineType::Pointer())); TNode<Object> data = LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset); VARIABLE(api_holder, MachineRepresentation::kTagged, p->receiver); Label load(this); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)), &load); CSA_ASSERT( this, WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype))); api_holder.Bind(LoadMapPrototype(LoadMap(p->receiver))); Goto(&load); BIND(&load); Callable callable = CodeFactory::CallApiCallback(isolate(), 0); exit_point->Return(CallStub(callable, nullptr, context, data, api_holder.value(), callback, p->receiver)); } BIND(&runtime); exit_point->ReturnCallRuntime(Runtime::kLoadAccessorProperty, context, p->receiver, SmiTag(handler_kind), call_handler_info); } void AccessorAssembler::HandleLoadField(Node* holder, Node* handler_word, Variable* var_double_value, Label* rebox_double, ExitPoint* exit_point) { Comment("field_load"); Node* index = DecodeWord<LoadHandler::FieldIndexBits>(handler_word); Node* offset = IntPtrMul(index, IntPtrConstant(kPointerSize)); Label inobject(this), out_of_object(this); Branch(IsSetWord<LoadHandler::IsInobjectBits>(handler_word), &inobject, &out_of_object); BIND(&inobject); { Label is_double(this); GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double); exit_point->Return(LoadObjectField(holder, offset)); BIND(&is_double); if (FLAG_unbox_double_fields) { var_double_value->Bind( LoadObjectField(holder, offset, MachineType::Float64())); } else { Node* mutable_heap_number = LoadObjectField(holder, offset); var_double_value->Bind(LoadHeapNumberValue(mutable_heap_number)); } Goto(rebox_double); } BIND(&out_of_object); { Label is_double(this); Node* properties = LoadFastProperties(holder); Node* value = LoadObjectField(properties, offset); GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double); exit_point->Return(value); BIND(&is_double); var_double_value->Bind(LoadHeapNumberValue(value)); Goto(rebox_double); } } TNode<Object> AccessorAssembler::LoadDescriptorValue(TNode<Map> map, Node* descriptor) { return CAST(LoadDescriptorValueOrFieldType(map, descriptor)); } TNode<MaybeObject> AccessorAssembler::LoadDescriptorValueOrFieldType( TNode<Map> map, SloppyTNode<IntPtrT> descriptor) { TNode<DescriptorArray> descriptors = LoadMapDescriptors(map); TNode<IntPtrT> scaled_descriptor = IntPtrMul(descriptor, IntPtrConstant(DescriptorArray::kEntrySize)); TNode<IntPtrT> value_index = IntPtrAdd( scaled_descriptor, IntPtrConstant(DescriptorArray::kFirstIndex + DescriptorArray::kEntryValueIndex)); CSA_ASSERT(this, UintPtrLessThan(descriptor, LoadAndUntagWeakFixedArrayLength( descriptors))); return LoadWeakFixedArrayElement(descriptors, value_index); } void AccessorAssembler::HandleLoadICSmiHandlerCase( const LoadICParameters* p, Node* holder, SloppyTNode<Smi> smi_handler, SloppyTNode<Object> handler, Label* miss, ExitPoint* exit_point, OnNonExistent on_nonexistent, ElementSupport support_elements) { VARIABLE(var_double_value, MachineRepresentation::kFloat64); Label rebox_double(this, &var_double_value); TNode<WordT> handler_word = SmiUntag(smi_handler); TNode<IntPtrT> handler_kind = Signed(DecodeWord<LoadHandler::KindBits>(handler_word)); if (support_elements == kSupportElements) { Label if_element(this), if_indexed_string(this), if_property(this); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)), &if_element); Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kIndexedString)), &if_indexed_string, &if_property); BIND(&if_element); Comment("element_load"); Node* intptr_index = TryToIntptr(p->name, miss); Node* elements = LoadElements(holder); Node* is_jsarray_condition = IsSetWord<LoadHandler::IsJsArrayBits>(handler_word); Node* elements_kind = DecodeWord32FromWord<LoadHandler::ElementsKindBits>(handler_word); Label if_hole(this), unimplemented_elements_kind(this), if_oob(this, Label::kDeferred); EmitElementLoad(holder, elements, elements_kind, intptr_index, is_jsarray_condition, &if_hole, &rebox_double, &var_double_value, &unimplemented_elements_kind, &if_oob, miss, exit_point); BIND(&unimplemented_elements_kind); { // Smi handlers should only be installed for supported elements kinds. // Crash if we get here. DebugBreak(); Goto(miss); } BIND(&if_oob); { Comment("out of bounds elements access"); Label return_undefined(this); // Check if we're allowed to handle OOB accesses. Node* allow_out_of_bounds = IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word); GotoIfNot(allow_out_of_bounds, miss); // Negative indices aren't valid array indices (according to // the ECMAScript specification), and are stored as properties // in V8, not elements. So we cannot handle them here, except // in case of typed arrays, where integer indexed properties // aren't looked up in the prototype chain. GotoIf(IsJSTypedArray(holder), &return_undefined); GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), miss); // For all other receivers we need to check that the prototype chain // doesn't contain any elements. BranchIfPrototypesHaveNoElements(LoadMap(holder), &return_undefined, miss); BIND(&return_undefined); exit_point->Return(UndefinedConstant()); } BIND(&if_hole); { Comment("convert hole"); GotoIfNot(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss); GotoIf(IsNoElementsProtectorCellInvalid(), miss); exit_point->Return(UndefinedConstant()); } BIND(&if_indexed_string); { Label if_oob(this, Label::kDeferred); Comment("indexed string"); Node* intptr_index = TryToIntptr(p->name, miss); Node* length = LoadStringLengthAsWord(holder); GotoIf(UintPtrGreaterThanOrEqual(intptr_index, length), &if_oob); TNode<Int32T> code = StringCharCodeAt(holder, intptr_index); TNode<String> result = StringFromSingleCharCode(code); Return(result); BIND(&if_oob); Node* allow_out_of_bounds = IsSetWord<LoadHandler::AllowOutOfBoundsBits>(handler_word); GotoIfNot(allow_out_of_bounds, miss); GotoIf(IsNoElementsProtectorCellInvalid(), miss); Return(UndefinedConstant()); } BIND(&if_property); Comment("property_load"); } Label constant(this), field(this), normal(this, Label::kDeferred), interceptor(this, Label::kDeferred), nonexistent(this), accessor(this, Label::kDeferred), global(this, Label::kDeferred), module_export(this, Label::kDeferred), proxy(this, Label::kDeferred), native_data_property(this), api_getter(this); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)), &constant); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)), &nonexistent); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)), &normal); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)), &accessor); GotoIf( WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNativeDataProperty)), &native_data_property); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetter)), &api_getter); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kApiGetterHolderIsPrototype)), &api_getter); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)), &global); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)), &proxy); Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kModuleExport)), &module_export, &interceptor); BIND(&field); HandleLoadField(holder, handler_word, &var_double_value, &rebox_double, exit_point); BIND(&nonexistent); // This is a handler for a load of a non-existent value. if (on_nonexistent == OnNonExistent::kThrowReferenceError) { exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context, p->name); } else { DCHECK_EQ(OnNonExistent::kReturnUndefined, on_nonexistent); exit_point->Return(UndefinedConstant()); } BIND(&constant); { Comment("constant_load"); Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word); Node* value = LoadDescriptorValue(LoadMap(holder), descriptor); exit_point->Return(value); } BIND(&normal); { Comment("load_normal"); TNode<NameDictionary> properties = CAST(LoadSlowProperties(holder)); TVARIABLE(IntPtrT, var_name_index); Label found(this, &var_name_index); NameDictionaryLookup<NameDictionary>(properties, CAST(p->name), &found, &var_name_index, miss); BIND(&found); { VARIABLE(var_details, MachineRepresentation::kWord32); VARIABLE(var_value, MachineRepresentation::kTagged); LoadPropertyFromNameDictionary(properties, var_name_index.value(), &var_details, &var_value); Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(), p->context, p->receiver, miss); exit_point->Return(value); } } BIND(&accessor); { Comment("accessor_load"); Node* descriptor = DecodeWord<LoadHandler::DescriptorBits>(handler_word); Node* accessor_pair = LoadDescriptorValue(LoadMap(holder), descriptor); CSA_ASSERT(this, IsAccessorPair(accessor_pair)); Node* getter = LoadObjectField(accessor_pair, AccessorPair::kGetterOffset); CSA_ASSERT(this, Word32BinaryNot(IsTheHole(getter))); Callable callable = CodeFactory::Call(isolate()); exit_point->Return(CallJS(callable, p->context, getter, p->receiver)); } BIND(&native_data_property); HandleLoadCallbackProperty(p, CAST(holder), handler_word, exit_point); BIND(&api_getter); HandleLoadAccessor(p, CAST(holder), handler_word, CAST(handler), handler_kind, exit_point); BIND(&proxy); { VARIABLE(var_index, MachineType::PointerRepresentation()); VARIABLE(var_unique, MachineRepresentation::kTagged); Label if_index(this), if_unique_name(this), to_name_failed(this, Label::kDeferred); if (support_elements == kSupportElements) { DCHECK_NE(on_nonexistent, OnNonExistent::kThrowReferenceError); TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &to_name_failed); BIND(&if_unique_name); exit_point->ReturnCallStub( Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty), p->context, holder, var_unique.value(), p->receiver, SmiConstant(on_nonexistent)); BIND(&if_index); // TODO(mslekova): introduce TryToName that doesn't try to compute // the intptr index value Goto(&to_name_failed); BIND(&to_name_failed); exit_point->ReturnCallRuntime(Runtime::kGetPropertyWithReceiver, p->context, holder, p->name, p->receiver, SmiConstant(on_nonexistent)); } else { exit_point->ReturnCallStub( Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty), p->context, holder, p->name, p->receiver, SmiConstant(on_nonexistent)); } } BIND(&global); { CSA_ASSERT(this, IsPropertyCell(holder)); // Ensure the property cell doesn't contain the hole. Node* value = LoadObjectField(holder, PropertyCell::kValueOffset); Node* details = LoadAndUntagToWord32ObjectField(holder, PropertyCell::kDetailsOffset); GotoIf(IsTheHole(value), miss); exit_point->Return( CallGetterIfAccessor(value, details, p->context, p->receiver, miss)); } BIND(&interceptor); { Comment("load_interceptor"); exit_point->ReturnCallRuntime(Runtime::kLoadPropertyWithInterceptor, p->context, p->name, p->receiver, holder, p->slot, p->vector); } BIND(&module_export); { Comment("module export"); Node* index = DecodeWord<LoadHandler::ExportsIndexBits>(handler_word); Node* module = LoadObjectField(p->receiver, JSModuleNamespace::kModuleOffset, MachineType::TaggedPointer()); TNode<ObjectHashTable> exports = CAST(LoadObjectField( module, Module::kExportsOffset, MachineType::TaggedPointer())); Node* cell = LoadFixedArrayElement(exports, index); // The handler is only installed for exports that exist. CSA_ASSERT(this, IsCell(cell)); Node* value = LoadCellValue(cell); Label is_the_hole(this, Label::kDeferred); GotoIf(IsTheHole(value), &is_the_hole); exit_point->Return(value); BIND(&is_the_hole); { Node* message = SmiConstant(MessageTemplate::kNotDefined); exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context, message, p->name); } } BIND(&rebox_double); exit_point->Return(AllocateHeapNumberWithValue(var_double_value.value())); } // Performs actions common to both load and store handlers: // 1. Checks prototype validity cell. // 2. If |on_code_handler| is provided, then it checks if the sub handler is // a smi or code and if it's a code then it calls |on_code_handler| to // generate a code that handles Code handlers. // If |on_code_handler| is not provided, then only smi sub handler are // expected. // 3. Does access check on receiver if ICHandler::DoAccessCheckOnReceiverBits // bit is set in the smi handler. // 4. Does dictionary lookup on receiver if ICHandler::LookupOnReceiverBits bit // is set in the smi handler. If |on_found_on_receiver| is provided then // it calls it to generate a code that handles the "found on receiver case" // or just misses if the |on_found_on_receiver| is not provided. // 5. Falls through in a case of a smi handler which is returned from this // function (tagged!). // TODO(ishell): Remove templatezation once we move common bits from // Load/StoreHandler to the base class. template <typename ICHandler, typename ICParameters> Node* AccessorAssembler::HandleProtoHandler( const ICParameters* p, Node* handler, const OnCodeHandler& on_code_handler, const OnFoundOnReceiver& on_found_on_receiver, Label* miss, ICMode ic_mode) { // // Check prototype validity cell. // { Node* maybe_validity_cell = LoadObjectField(handler, ICHandler::kValidityCellOffset); CheckPrototypeValidityCell(maybe_validity_cell, miss); } // // Check smi handler bits. // { Node* smi_or_code_handler = LoadObjectField(handler, ICHandler::kSmiHandlerOffset); if (on_code_handler) { Label if_smi_handler(this); GotoIf(TaggedIsSmi(smi_or_code_handler), &if_smi_handler); CSA_ASSERT(this, IsCodeMap(LoadMap(smi_or_code_handler))); on_code_handler(smi_or_code_handler); BIND(&if_smi_handler); } else { CSA_ASSERT(this, TaggedIsSmi(smi_or_code_handler)); } Node* handler_flags = SmiUntag(smi_or_code_handler); // Lookup on receiver and access checks are not necessary for global ICs // because in the former case the validity cell check guards modifications // of the global object and the latter is not applicable to the global // object. int mask = ICHandler::LookupOnReceiverBits::kMask | ICHandler::DoAccessCheckOnReceiverBits::kMask; if (ic_mode == ICMode::kGlobalIC) { CSA_ASSERT(this, IsClearWord(handler_flags, mask)); } else { DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode); Label done(this), if_do_access_check(this), if_lookup_on_receiver(this); GotoIf(IsClearWord(handler_flags, mask), &done); // Only one of the bits can be set at a time. CSA_ASSERT(this, WordNotEqual(WordAnd(handler_flags, IntPtrConstant(mask)), IntPtrConstant(mask))); Branch(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags), &if_do_access_check, &if_lookup_on_receiver); BIND(&if_do_access_check); { TNode<MaybeObject> data2 = LoadHandlerDataField(handler, 2); CSA_ASSERT(this, IsWeakOrClearedHeapObject(data2)); TNode<Object> expected_native_context = ToWeakHeapObject(data2, miss); EmitAccessCheck(expected_native_context, p->context, p->receiver, &done, miss); } // Dictionary lookup on receiver is not necessary for Load/StoreGlobalIC // because prototype validity cell check already guards modifications of // the global object. BIND(&if_lookup_on_receiver); { DCHECK_EQ(ICMode::kNonGlobalIC, ic_mode); CSA_ASSERT(this, Word32BinaryNot(HasInstanceType( p->receiver, JS_GLOBAL_OBJECT_TYPE))); TNode<NameDictionary> properties = CAST(LoadSlowProperties(p->receiver)); TVARIABLE(IntPtrT, var_name_index); Label found(this, &var_name_index); NameDictionaryLookup<NameDictionary>(properties, CAST(p->name), &found, &var_name_index, &done); BIND(&found); { if (on_found_on_receiver) { on_found_on_receiver(properties, var_name_index.value()); } else { Goto(miss); } } } BIND(&done); } return smi_or_code_handler; } } void AccessorAssembler::HandleLoadICProtoHandler( const LoadICParameters* p, Node* handler, Variable* var_holder, Variable* var_smi_handler, Label* if_smi_handler, Label* miss, ExitPoint* exit_point, ICMode ic_mode) { DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep()); DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep()); Node* smi_handler = HandleProtoHandler<LoadHandler>( p, handler, // Code sub-handlers are not expected in LoadICs, so no |on_code_handler|. nullptr, // on_found_on_receiver [=](Node* properties, Node* name_index) { VARIABLE(var_details, MachineRepresentation::kWord32); VARIABLE(var_value, MachineRepresentation::kTagged); LoadPropertyFromNameDictionary(properties, name_index, &var_details, &var_value); Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(), p->context, p->receiver, miss); exit_point->Return(value); }, miss, ic_mode); TNode<MaybeObject> maybe_holder = LoadHandlerDataField(handler, 1); Label load_from_cached_holder(this), done(this); Branch(IsStrongReferenceTo(maybe_holder, NullConstant()), &done, &load_from_cached_holder); BIND(&load_from_cached_holder); { // For regular holders, having passed the receiver map check and the // validity cell check implies that |holder| is alive. However, for global // object receivers, |maybe_holder| may be cleared. CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_holder)); Node* holder = ToWeakHeapObject(maybe_holder, miss); var_holder->Bind(holder); Goto(&done); } BIND(&done); { var_smi_handler->Bind(smi_handler); Goto(if_smi_handler); } } void AccessorAssembler::EmitAccessCheck(Node* expected_native_context, Node* context, Node* receiver, Label* can_access, Label* miss) { CSA_ASSERT(this, IsNativeContext(expected_native_context)); Node* native_context = LoadNativeContext(context); GotoIf(WordEqual(expected_native_context, native_context), can_access); // If the receiver is not a JSGlobalProxy then we miss. GotoIfNot(IsJSGlobalProxy(receiver), miss); // For JSGlobalProxy receiver try to compare security tokens of current // and expected native contexts. Node* expected_token = LoadContextElement(expected_native_context, Context::SECURITY_TOKEN_INDEX); Node* current_token = LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX); Branch(WordEqual(expected_token, current_token), can_access, miss); } void AccessorAssembler::JumpIfDataProperty(Node* details, Label* writable, Label* readonly) { // Accessor properties never have the READ_ONLY attribute set. GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask), readonly); Node* kind = DecodeWord32<PropertyDetails::KindField>(details); GotoIf(Word32Equal(kind, Int32Constant(kData)), writable); // Fall through if it's an accessor property. } void AccessorAssembler::HandleStoreICNativeDataProperty( const StoreICParameters* p, Node* holder, Node* handler_word) { Comment("native_data_property_store"); Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word); Node* accessor_info = LoadDescriptorValue(LoadMap(holder), descriptor); CSA_CHECK(this, IsAccessorInfo(accessor_info)); Node* language_mode = GetLanguageMode(p->vector, p->slot); TailCallRuntime(Runtime::kStoreCallbackProperty, p->context, p->receiver, holder, accessor_info, p->name, p->value, language_mode); } void AccessorAssembler::HandleStoreICHandlerCase( const StoreICParameters* p, TNode<MaybeObject> handler, Label* miss, ICMode ic_mode, ElementSupport support_elements) { Label if_smi_handler(this), if_nonsmi_handler(this); Label if_proto_handler(this), if_element_handler(this), call_handler(this), store_transition_or_global(this); Branch(TaggedIsSmi(handler), &if_smi_handler, &if_nonsmi_handler); // |handler| is a Smi, encoding what to do. See SmiHandler methods // for the encoding format. BIND(&if_smi_handler); { Node* holder = p->receiver; Node* handler_word = SmiUntag(CAST(handler)); Label if_fast_smi(this), if_proxy(this); STATIC_ASSERT(StoreHandler::kGlobalProxy + 1 == StoreHandler::kNormal); STATIC_ASSERT(StoreHandler::kNormal + 1 == StoreHandler::kProxy); STATIC_ASSERT(StoreHandler::kProxy + 1 == StoreHandler::kKindsNumber); Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word); GotoIf(IntPtrLessThan(handler_kind, IntPtrConstant(StoreHandler::kGlobalProxy)), &if_fast_smi); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)), &if_proxy); CSA_ASSERT(this, WordEqual(handler_kind, IntPtrConstant(StoreHandler::kNormal))); TNode<NameDictionary> properties = CAST(LoadSlowProperties(holder)); TVARIABLE(IntPtrT, var_name_index); Label dictionary_found(this, &var_name_index); NameDictionaryLookup<NameDictionary>( properties, CAST(p->name), &dictionary_found, &var_name_index, miss); BIND(&dictionary_found); { Node* details = LoadDetailsByKeyIndex<NameDictionary>( properties, var_name_index.value()); // Check that the property is a writable data property (no accessor). const int kTypeAndReadOnlyMask = PropertyDetails::KindField::kMask | PropertyDetails::kAttributesReadOnlyMask; STATIC_ASSERT(kData == 0); GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss); StoreValueByKeyIndex<NameDictionary>(properties, var_name_index.value(), p->value); Return(p->value); } BIND(&if_fast_smi); { Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word); Label data(this), accessor(this), native_data_property(this); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kAccessor)), &accessor); Branch(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kNativeDataProperty)), &native_data_property, &data); BIND(&accessor); HandleStoreAccessor(p, holder, handler_word); BIND(&native_data_property); HandleStoreICNativeDataProperty(p, holder, handler_word); BIND(&data); // Handle non-transitioning field stores. HandleStoreICSmiHandlerCase(handler_word, holder, p->value, miss); } BIND(&if_proxy); HandleStoreToProxy(p, holder, miss, support_elements); } BIND(&if_nonsmi_handler); { GotoIf(IsWeakOrClearedHeapObject(handler), &store_transition_or_global); TNode<HeapObject> strong_handler = CAST(handler); TNode<Map> handler_map = LoadMap(strong_handler); Branch(IsCodeMap(handler_map), &call_handler, &if_proto_handler); BIND(&if_proto_handler); { HandleStoreICProtoHandler(p, CAST(strong_handler), miss, ic_mode, support_elements); } // |handler| is a heap object. Must be code, call it. BIND(&call_handler); { TailCallStub(StoreWithVectorDescriptor{}, CAST(strong_handler), CAST(p->context), p->receiver, p->name, p->value, p->slot, p->vector); } } BIND(&store_transition_or_global); { // Load value or miss if the {handler} weak cell is cleared. CSA_ASSERT(this, IsWeakOrClearedHeapObject(handler)); TNode<HeapObject> map_or_property_cell = ToWeakHeapObject(handler, miss); Label store_global(this), store_transition(this); Branch(IsMap(map_or_property_cell), &store_transition, &store_global); BIND(&store_global); { TNode<PropertyCell> property_cell = CAST(map_or_property_cell); ExitPoint direct_exit(this); StoreGlobalIC_PropertyCellCase(property_cell, p->value, &direct_exit, miss); } BIND(&store_transition); { TNode<Map> map = CAST(map_or_property_cell); HandleStoreICTransitionMapHandlerCase(p, map, miss, false); Return(p->value); } } } void AccessorAssembler::HandleStoreICTransitionMapHandlerCase( const StoreICParameters* p, TNode<Map> transition_map, Label* miss, bool validate_transition_handler) { Node* maybe_validity_cell = LoadObjectField(transition_map, Map::kPrototypeValidityCellOffset); CheckPrototypeValidityCell(maybe_validity_cell, miss); TNode<Uint32T> bitfield3 = LoadMapBitField3(transition_map); CSA_ASSERT(this, IsClearWord32<Map::IsDictionaryMapBit>(bitfield3)); GotoIf(IsSetWord32<Map::IsDeprecatedBit>(bitfield3), miss); // Load last descriptor details. Node* nof = DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3); CSA_ASSERT(this, WordNotEqual(nof, IntPtrConstant(0))); TNode<DescriptorArray> descriptors = LoadMapDescriptors(transition_map); Node* factor = IntPtrConstant(DescriptorArray::kEntrySize); TNode<IntPtrT> last_key_index = UncheckedCast<IntPtrT>(IntPtrAdd( IntPtrConstant(DescriptorArray::ToKeyIndex(-1)), IntPtrMul(nof, factor))); if (validate_transition_handler) { Node* key = LoadWeakFixedArrayElement(descriptors, last_key_index); GotoIf(WordNotEqual(key, p->name), miss); } else { CSA_ASSERT(this, WordEqual(BitcastMaybeObjectToWord(LoadWeakFixedArrayElement( descriptors, last_key_index)), p->name)); } Node* details = LoadDetailsByKeyIndex(descriptors, last_key_index); if (validate_transition_handler) { // Follow transitions only in the following cases: // 1) name is a non-private symbol and attributes equal to NONE, // 2) name is a private symbol and attributes equal to DONT_ENUM. Label attributes_ok(this); const int kAttributesDontDeleteReadOnlyMask = PropertyDetails::kAttributesDontDeleteMask | PropertyDetails::kAttributesReadOnlyMask; // Both DontDelete and ReadOnly attributes must not be set. GotoIf(IsSetWord32(details, kAttributesDontDeleteReadOnlyMask), miss); // DontEnum attribute is allowed only for private symbols and vice versa. Branch(Word32Equal( IsSetWord32(details, PropertyDetails::kAttributesDontEnumMask), IsPrivateSymbol(p->name)), &attributes_ok, miss); BIND(&attributes_ok); } OverwriteExistingFastDataProperty(p->receiver, transition_map, descriptors, last_key_index, details, p->value, miss, true); } void AccessorAssembler::CheckFieldType(TNode<DescriptorArray> descriptors, Node* name_index, Node* representation, Node* value, Label* bailout) { Label r_smi(this), r_double(this), r_heapobject(this), all_fine(this); // Ignore FLAG_track_fields etc. and always emit code for all checks, // because this builtin is part of the snapshot and therefore should // be flag independent. GotoIf(Word32Equal(representation, Int32Constant(Representation::kSmi)), &r_smi); GotoIf(Word32Equal(representation, Int32Constant(Representation::kDouble)), &r_double); GotoIf( Word32Equal(representation, Int32Constant(Representation::kHeapObject)), &r_heapobject); GotoIf(Word32Equal(representation, Int32Constant(Representation::kNone)), bailout); CSA_ASSERT(this, Word32Equal(representation, Int32Constant(Representation::kTagged))); Goto(&all_fine); BIND(&r_smi); { Branch(TaggedIsSmi(value), &all_fine, bailout); } BIND(&r_double); { GotoIf(TaggedIsSmi(value), &all_fine); Node* value_map = LoadMap(value); // While supporting mutable HeapNumbers would be straightforward, such // objects should not end up here anyway. CSA_ASSERT(this, WordNotEqual(value_map, LoadRoot(Heap::kMutableHeapNumberMapRootIndex))); Branch(IsHeapNumberMap(value_map), &all_fine, bailout); } BIND(&r_heapobject); { GotoIf(TaggedIsSmi(value), bailout); TNode<MaybeObject> field_type = LoadFieldTypeByKeyIndex( descriptors, UncheckedCast<IntPtrT>(name_index)); intptr_t kNoneType = reinterpret_cast<intptr_t>(FieldType::None()); intptr_t kAnyType = reinterpret_cast<intptr_t>(FieldType::Any()); DCHECK_NE(kNoneType, kClearedWeakHeapObject); DCHECK_NE(kAnyType, kClearedWeakHeapObject); // FieldType::None can't hold any value. GotoIf(WordEqual(BitcastMaybeObjectToWord(field_type), IntPtrConstant(kNoneType)), bailout); // FieldType::Any can hold any value. GotoIf(WordEqual(BitcastMaybeObjectToWord(field_type), IntPtrConstant(kAnyType)), &all_fine); // Cleared weak references count as FieldType::None, which can't hold any // value. TNode<Map> field_type_map = CAST(ToWeakHeapObject(field_type, bailout)); // FieldType::Class(...) performs a map check. Branch(WordEqual(LoadMap(value), field_type_map), &all_fine, bailout); } BIND(&all_fine); } void AccessorAssembler::OverwriteExistingFastDataProperty( Node* object, Node* object_map, Node* descriptors, Node* descriptor_name_index, Node* details, Node* value, Label* slow, bool do_transitioning_store) { Label done(this), if_field(this), if_descriptor(this); CSA_ASSERT(this, Word32Equal(DecodeWord32<PropertyDetails::KindField>(details), Int32Constant(kData))); Branch(Word32Equal(DecodeWord32<PropertyDetails::LocationField>(details), Int32Constant(kField)), &if_field, &if_descriptor); BIND(&if_field); { if (FLAG_track_constant_fields && !do_transitioning_store) { // TODO(ishell): Taking the slow path is not necessary if new and old // values are identical. GotoIf(Word32Equal( DecodeWord32<PropertyDetails::ConstnessField>(details), Int32Constant(static_cast<int32_t>(VariableMode::kConst))), slow); } Node* representation = DecodeWord32<PropertyDetails::RepresentationField>(details); CheckFieldType(CAST(descriptors), descriptor_name_index, representation, value, slow); Node* field_index = DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details); field_index = IntPtrAdd(field_index, LoadMapInobjectPropertiesStartInWords(object_map)); Node* instance_size_in_words = LoadMapInstanceSizeInWords(object_map); Label inobject(this), backing_store(this); Branch(UintPtrLessThan(field_index, instance_size_in_words), &inobject, &backing_store); BIND(&inobject); { Node* field_offset = TimesPointerSize(field_index); Label tagged_rep(this), double_rep(this); Branch( Word32Equal(representation, Int32Constant(Representation::kDouble)), &double_rep, &tagged_rep); BIND(&double_rep); { Node* double_value = ChangeNumberToFloat64(value); if (FLAG_unbox_double_fields) { if (do_transitioning_store) { StoreMap(object, object_map); } StoreObjectFieldNoWriteBarrier(object, field_offset, double_value, MachineRepresentation::kFloat64); } else { if (do_transitioning_store) { Node* mutable_heap_number = AllocateMutableHeapNumberWithValue(double_value); StoreMap(object, object_map); StoreObjectField(object, field_offset, mutable_heap_number); } else { Node* mutable_heap_number = LoadObjectField(object, field_offset); StoreHeapNumberValue(mutable_heap_number, double_value); } } Goto(&done); } BIND(&tagged_rep); { if (do_transitioning_store) { StoreMap(object, object_map); } StoreObjectField(object, field_offset, value); Goto(&done); } } BIND(&backing_store); { Node* backing_store_index = IntPtrSub(field_index, instance_size_in_words); if (do_transitioning_store) { // Allocate mutable heap number before extending properties backing // store to ensure that heap verifier will not see the heap in // inconsistent state. VARIABLE(var_value, MachineRepresentation::kTagged, value); { Label cont(this); GotoIf(Word32NotEqual(representation, Int32Constant(Representation::kDouble)), &cont); { Node* double_value = ChangeNumberToFloat64(value); Node* mutable_heap_number = AllocateMutableHeapNumberWithValue(double_value); var_value.Bind(mutable_heap_number); Goto(&cont); } BIND(&cont); } TNode<PropertyArray> properties = CAST(ExtendPropertiesBackingStore(object, backing_store_index)); StorePropertyArrayElement(properties, backing_store_index, var_value.value()); StoreMap(object, object_map); Goto(&done); } else { Label tagged_rep(this), double_rep(this); TNode<PropertyArray> properties = CAST(LoadFastProperties(object)); Branch( Word32Equal(representation, Int32Constant(Representation::kDouble)), &double_rep, &tagged_rep); BIND(&double_rep); { Node* mutable_heap_number = LoadPropertyArrayElement(properties, backing_store_index); Node* double_value = ChangeNumberToFloat64(value); StoreHeapNumberValue(mutable_heap_number, double_value); Goto(&done); } BIND(&tagged_rep); { StorePropertyArrayElement(properties, backing_store_index, value); Goto(&done); } } } } BIND(&if_descriptor); { // Check that constant matches value. Node* constant = LoadValueByKeyIndex( CAST(descriptors), UncheckedCast<IntPtrT>(descriptor_name_index)); GotoIf(WordNotEqual(value, constant), slow); if (do_transitioning_store) { StoreMap(object, object_map); } Goto(&done); } BIND(&done); } void AccessorAssembler::CheckPrototypeValidityCell(Node* maybe_validity_cell, Label* miss) { Label done(this); GotoIf(WordEqual(maybe_validity_cell, SmiConstant(Map::kPrototypeChainValid)), &done); CSA_ASSERT(this, TaggedIsNotSmi(maybe_validity_cell)); Node* cell_value = LoadObjectField(maybe_validity_cell, Cell::kValueOffset); Branch(WordEqual(cell_value, SmiConstant(Map::kPrototypeChainValid)), &done, miss); BIND(&done); } void AccessorAssembler::HandleStoreAccessor(const StoreICParameters* p, Node* holder, Node* handler_word) { Comment("accessor_store"); Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word); Node* accessor_pair = LoadDescriptorValue(LoadMap(holder), descriptor); CSA_ASSERT(this, IsAccessorPair(accessor_pair)); Node* setter = LoadObjectField(accessor_pair, AccessorPair::kSetterOffset); CSA_ASSERT(this, Word32BinaryNot(IsTheHole(setter))); Callable callable = CodeFactory::Call(isolate()); Return(CallJS(callable, p->context, setter, p->receiver, p->value)); } void AccessorAssembler::HandleStoreICProtoHandler( const StoreICParameters* p, TNode<StoreHandler> handler, Label* miss, ICMode ic_mode, ElementSupport support_elements) { Comment("HandleStoreICProtoHandler"); OnCodeHandler on_code_handler; if (support_elements == kSupportElements) { // Code sub-handlers are expected only in KeyedStoreICs. on_code_handler = [=](Node* code_handler) { // This is either element store or transitioning element store. Label if_element_store(this), if_transitioning_element_store(this); Branch(IsStoreHandler0Map(LoadMap(handler)), &if_element_store, &if_transitioning_element_store); BIND(&if_element_store); { TailCallStub(StoreWithVectorDescriptor{}, code_handler, p->context, p->receiver, p->name, p->value, p->slot, p->vector); } BIND(&if_transitioning_element_store); { TNode<MaybeObject> maybe_transition_map = LoadHandlerDataField(handler, 1); TNode<Map> transition_map = CAST(ToWeakHeapObject(maybe_transition_map, miss)); GotoIf(IsDeprecatedMap(transition_map), miss); TailCallStub(StoreTransitionDescriptor{}, code_handler, p->context, p->receiver, p->name, transition_map, p->value, p->slot, p->vector); } }; } Node* smi_handler = HandleProtoHandler<StoreHandler>( p, handler, on_code_handler, // on_found_on_receiver [=](Node* properties, Node* name_index) { Node* details = LoadDetailsByKeyIndex<NameDictionary>(properties, name_index); // Check that the property is a writable data property (no accessor). const int kTypeAndReadOnlyMask = PropertyDetails::KindField::kMask | PropertyDetails::kAttributesReadOnlyMask; STATIC_ASSERT(kData == 0); GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss); StoreValueByKeyIndex<NameDictionary>( CAST(properties), UncheckedCast<IntPtrT>(name_index), p->value); Return(p->value); }, miss, ic_mode); { Label if_add_normal(this), if_store_global_proxy(this), if_api_setter(this), if_accessor(this), if_native_data_property(this); CSA_ASSERT(this, TaggedIsSmi(smi_handler)); Node* handler_word = SmiUntag(smi_handler); Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kNormal)), &if_add_normal); TNode<MaybeObject> maybe_holder = LoadHandlerDataField(handler, 1); CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_holder)); TNode<Object> holder = ToWeakHeapObject(maybe_holder, miss); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kGlobalProxy)), &if_store_global_proxy); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kAccessor)), &if_accessor); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kNativeDataProperty)), &if_native_data_property); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kApiSetter)), &if_api_setter); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kApiSetterHolderIsPrototype)), &if_api_setter); CSA_ASSERT(this, WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy))); HandleStoreToProxy(p, holder, miss, support_elements); BIND(&if_add_normal); { // This is a case of "transitioning store" to a dictionary mode object // when the property is still does not exist. The "existing property" // case is covered above by LookupOnReceiver bit handling of the smi // handler. Label slow(this); TNode<Map> receiver_map = LoadMap(p->receiver); InvalidateValidityCellIfPrototype(receiver_map); TNode<NameDictionary> properties = CAST(LoadSlowProperties(p->receiver)); Add<NameDictionary>(properties, CAST(p->name), p->value, &slow); Return(p->value); BIND(&slow); TailCallRuntime(Runtime::kAddDictionaryProperty, p->context, p->receiver, p->name, p->value); } BIND(&if_accessor); HandleStoreAccessor(p, holder, handler_word); BIND(&if_native_data_property); HandleStoreICNativeDataProperty(p, holder, handler_word); BIND(&if_api_setter); { Comment("api_setter"); CSA_ASSERT(this, TaggedIsNotSmi(handler)); Node* call_handler_info = holder; // Context is stored either in data2 or data3 field depending on whether // the access check is enabled for this handler or not. TNode<MaybeObject> maybe_context = Select<MaybeObject>( IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_word), [=] { return LoadHandlerDataField(handler, 3); }, [=] { return LoadHandlerDataField(handler, 2); }); CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_context)); TNode<Object> context = Select<Object>(IsClearedWeakHeapObject(maybe_context), [=] { return SmiConstant(0); }, [=] { return ToWeakHeapObject(maybe_context); }); Node* foreign = LoadObjectField(call_handler_info, CallHandlerInfo::kJsCallbackOffset); Node* callback = LoadObjectField(foreign, Foreign::kForeignAddressOffset, MachineType::Pointer()); Node* data = LoadObjectField(call_handler_info, CallHandlerInfo::kDataOffset); VARIABLE(api_holder, MachineRepresentation::kTagged, p->receiver); Label store(this); GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kApiSetter)), &store); CSA_ASSERT( this, WordEqual(handler_kind, IntPtrConstant(StoreHandler::kApiSetterHolderIsPrototype))); api_holder.Bind(LoadMapPrototype(LoadMap(p->receiver))); Goto(&store); BIND(&store); Callable callable = CodeFactory::CallApiCallback(isolate(), 1); Return(CallStub(callable, nullptr, context, data, api_holder.value(), callback, p->receiver, p->value)); } BIND(&if_store_global_proxy); { ExitPoint direct_exit(this); StoreGlobalIC_PropertyCellCase(holder, p->value, &direct_exit, miss); } } } Node* AccessorAssembler::GetLanguageMode(Node* vector, Node* slot) { VARIABLE(var_language_mode, MachineRepresentation::kTaggedSigned, SmiConstant(LanguageMode::kStrict)); Label language_mode_determined(this); BranchIfStrictMode(vector, slot, &language_mode_determined); var_language_mode.Bind(SmiConstant(LanguageMode::kSloppy)); Goto(&language_mode_determined); BIND(&language_mode_determined); return var_language_mode.value(); } void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p, Node* proxy, Label* miss, ElementSupport support_elements) { VARIABLE(var_index, MachineType::PointerRepresentation()); VARIABLE(var_unique, MachineRepresentation::kTagged); Label if_index(this), if_unique_name(this), to_name_failed(this, Label::kDeferred); Node* language_mode = GetLanguageMode(p->vector, p->slot); if (support_elements == kSupportElements) { TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &to_name_failed); BIND(&if_unique_name); CallBuiltin(Builtins::kProxySetProperty, p->context, proxy, var_unique.value(), p->value, p->receiver, language_mode); Return(p->value); // The index case is handled earlier by the runtime. BIND(&if_index); // TODO(mslekova): introduce TryToName that doesn't try to compute // the intptr index value Goto(&to_name_failed); BIND(&to_name_failed); TailCallRuntime(Runtime::kSetPropertyWithReceiver, p->context, proxy, p->name, p->value, p->receiver, language_mode); } else { Node* name = ToName(p->context, p->name); TailCallBuiltin(Builtins::kProxySetProperty, p->context, proxy, name, p->value, p->receiver, language_mode); } } void AccessorAssembler::HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder, Node* value, Label* miss) { Comment("field store"); #ifdef DEBUG Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word); if (FLAG_track_constant_fields) { CSA_ASSERT( this, Word32Or(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kField)), WordEqual(handler_kind, IntPtrConstant(StoreHandler::kConstField)))); } else { CSA_ASSERT(this, WordEqual(handler_kind, IntPtrConstant(StoreHandler::kField))); } #endif Node* field_representation = DecodeWord<StoreHandler::FieldRepresentationBits>(handler_word); Label if_smi_field(this), if_double_field(this), if_heap_object_field(this), if_tagged_field(this); GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kTagged)), &if_tagged_field); GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kHeapObject)), &if_heap_object_field); GotoIf(WordEqual(field_representation, IntPtrConstant(StoreHandler::kDouble)), &if_double_field); CSA_ASSERT(this, WordEqual(field_representation, IntPtrConstant(StoreHandler::kSmi))); Goto(&if_smi_field); BIND(&if_tagged_field); { Comment("store tagged field"); HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(), value, miss); } BIND(&if_double_field); { Comment("store double field"); HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(), value, miss); } BIND(&if_heap_object_field); { Comment("store heap object field"); HandleStoreFieldAndReturn(handler_word, holder, Representation::HeapObject(), value, miss); } BIND(&if_smi_field); { Comment("store smi field"); HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(), value, miss); } } void AccessorAssembler::HandleStoreFieldAndReturn(Node* handler_word, Node* holder, Representation representation, Node* value, Label* miss) { Node* prepared_value = PrepareValueForStore(handler_word, holder, representation, value, miss); Label if_inobject(this), if_out_of_object(this); Branch(IsSetWord<StoreHandler::IsInobjectBits>(handler_word), &if_inobject, &if_out_of_object); BIND(&if_inobject); { StoreNamedField(handler_word, holder, true, representation, prepared_value, miss); Return(value); } BIND(&if_out_of_object); { StoreNamedField(handler_word, holder, false, representation, prepared_value, miss); Return(value); } } Node* AccessorAssembler::PrepareValueForStore(Node* handler_word, Node* holder, Representation representation, Node* value, Label* bailout) { if (representation.IsDouble()) { value = TryTaggedToFloat64(value, bailout); } else if (representation.IsHeapObject()) { GotoIf(TaggedIsSmi(value), bailout); Label done(this); if (FLAG_track_constant_fields) { // Skip field type check in favor of constant value check when storing // to constant field. GotoIf(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word), IntPtrConstant(StoreHandler::kConstField)), &done); } Node* descriptor = DecodeWord<StoreHandler::DescriptorBits>(handler_word); TNode<MaybeObject> maybe_field_type = LoadDescriptorValueOrFieldType(LoadMap(holder), descriptor); GotoIf(TaggedIsSmi(maybe_field_type), &done); // Check that value type matches the field type. { Node* field_type = ToWeakHeapObject(maybe_field_type, bailout); Branch(WordEqual(LoadMap(value), field_type), &done, bailout); } BIND(&done); } else if (representation.IsSmi()) { GotoIfNot(TaggedIsSmi(value), bailout); } else { DCHECK(representation.IsTagged()); } return value; } Node* AccessorAssembler::ExtendPropertiesBackingStore(Node* object, Node* index) { Comment("[ Extend storage"); ParameterMode mode = OptimalParameterMode(); // TODO(gsathya): Clean up the type conversions by creating smarter // helpers that do the correct op based on the mode. VARIABLE(var_properties, MachineRepresentation::kTaggedPointer); VARIABLE(var_encoded_hash, MachineRepresentation::kWord32); VARIABLE(var_length, ParameterRepresentation(mode)); Node* properties = LoadObjectField(object, JSObject::kPropertiesOrHashOffset); var_properties.Bind(properties); Label if_smi_hash(this), if_property_array(this), extend_store(this); Branch(TaggedIsSmi(properties), &if_smi_hash, &if_property_array); BIND(&if_smi_hash); { Node* hash = SmiToInt32(properties); Node* encoded_hash = Word32Shl(hash, Int32Constant(PropertyArray::HashField::kShift)); var_encoded_hash.Bind(encoded_hash); var_length.Bind(IntPtrOrSmiConstant(0, mode)); var_properties.Bind(EmptyFixedArrayConstant()); Goto(&extend_store); } BIND(&if_property_array); { Node* length_and_hash_int32 = LoadAndUntagToWord32ObjectField( var_properties.value(), PropertyArray::kLengthAndHashOffset); var_encoded_hash.Bind(Word32And( length_and_hash_int32, Int32Constant(PropertyArray::HashField::kMask))); Node* length_intptr = ChangeInt32ToIntPtr( Word32And(length_and_hash_int32, Int32Constant(PropertyArray::LengthField::kMask))); Node* length = IntPtrToParameter(length_intptr, mode); var_length.Bind(length); Goto(&extend_store); } BIND(&extend_store); { VARIABLE(var_new_properties, MachineRepresentation::kTaggedPointer, var_properties.value()); Label done(this); // Previous property deletion could have left behind unused backing store // capacity even for a map that think it doesn't have any unused fields. // Perform a bounds check to see if we actually have to grow the array. GotoIf(UintPtrLessThan(index, ParameterToIntPtr(var_length.value(), mode)), &done); Node* delta = IntPtrOrSmiConstant(JSObject::kFieldsAdded, mode); Node* new_capacity = IntPtrOrSmiAdd(var_length.value(), delta, mode); // Grow properties array. DCHECK(kMaxNumberOfDescriptors + JSObject::kFieldsAdded < FixedArrayBase::GetMaxLengthForNewSpaceAllocation(PACKED_ELEMENTS)); // The size of a new properties backing store is guaranteed to be small // enough that the new backing store will be allocated in new space. CSA_ASSERT(this, UintPtrOrSmiLessThan( new_capacity, IntPtrOrSmiConstant( kMaxNumberOfDescriptors + JSObject::kFieldsAdded, mode), mode)); Node* new_properties = AllocatePropertyArray(new_capacity, mode); var_new_properties.Bind(new_properties); FillPropertyArrayWithUndefined(new_properties, var_length.value(), new_capacity, mode); // |new_properties| is guaranteed to be in new space, so we can skip // the write barrier. CopyPropertyArrayValues(var_properties.value(), new_properties, var_length.value(), SKIP_WRITE_BARRIER, mode); // TODO(gsathya): Clean up the type conversions by creating smarter // helpers that do the correct op based on the mode. Node* new_capacity_int32 = TruncateIntPtrToInt32(ParameterToIntPtr(new_capacity, mode)); Node* new_length_and_hash_int32 = Word32Or(var_encoded_hash.value(), new_capacity_int32); StoreObjectField(new_properties, PropertyArray::kLengthAndHashOffset, SmiFromInt32(new_length_and_hash_int32)); StoreObjectField(object, JSObject::kPropertiesOrHashOffset, new_properties); Comment("] Extend storage"); Goto(&done); BIND(&done); return var_new_properties.value(); } } void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object, bool is_inobject, Representation representation, Node* value, Label* bailout) { bool store_value_as_double = representation.IsDouble(); Node* property_storage = object; if (!is_inobject) { property_storage = LoadFastProperties(object); } Node* index = DecodeWord<StoreHandler::FieldIndexBits>(handler_word); Node* offset = IntPtrMul(index, IntPtrConstant(kPointerSize)); if (representation.IsDouble()) { if (!FLAG_unbox_double_fields || !is_inobject) { // Load the mutable heap number. property_storage = LoadObjectField(property_storage, offset); // Store the double value into it. offset = IntPtrConstant(HeapNumber::kValueOffset); } } // Do constant value check if necessary. if (FLAG_track_constant_fields) { Label done(this); GotoIfNot(WordEqual(DecodeWord<StoreHandler::KindBits>(handler_word), IntPtrConstant(StoreHandler::kConstField)), &done); { if (store_value_as_double) { Node* current_value = LoadObjectField(property_storage, offset, MachineType::Float64()); GotoIfNot(Float64Equal(current_value, value), bailout); } else { Node* current_value = LoadObjectField(property_storage, offset); GotoIfNot(WordEqual(current_value, value), bailout); } Goto(&done); } BIND(&done); } // Do the store. if (store_value_as_double) { StoreObjectFieldNoWriteBarrier(property_storage, offset, value, MachineRepresentation::kFloat64); } else if (representation.IsSmi()) { StoreObjectFieldNoWriteBarrier(property_storage, offset, value); } else { StoreObjectField(property_storage, offset, value); } } void AccessorAssembler::EmitFastElementsBoundsCheck(Node* object, Node* elements, Node* intptr_index, Node* is_jsarray_condition, Label* miss) { VARIABLE(var_length, MachineType::PointerRepresentation()); Comment("Fast elements bounds check"); Label if_array(this), length_loaded(this, &var_length); GotoIf(is_jsarray_condition, &if_array); { var_length.Bind(SmiUntag(LoadFixedArrayBaseLength(elements))); Goto(&length_loaded); } BIND(&if_array); { var_length.Bind(SmiUntag(LoadFastJSArrayLength(object))); Goto(&length_loaded); } BIND(&length_loaded); GotoIfNot(UintPtrLessThan(intptr_index, var_length.value()), miss); } void AccessorAssembler::EmitElementLoad( Node* object, Node* elements, Node* elements_kind, SloppyTNode<IntPtrT> intptr_index, Node* is_jsarray_condition, Label* if_hole, Label* rebox_double, Variable* var_double_value, Label* unimplemented_elements_kind, Label* out_of_bounds, Label* miss, ExitPoint* exit_point) { Label if_typed_array(this), if_fast_packed(this), if_fast_holey(this), if_fast_double(this), if_fast_holey_double(this), if_nonfast(this), if_dictionary(this); GotoIf( Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)), &if_nonfast); EmitFastElementsBoundsCheck(object, elements, intptr_index, is_jsarray_condition, out_of_bounds); int32_t kinds[] = {// Handled by if_fast_packed. PACKED_SMI_ELEMENTS, PACKED_ELEMENTS, // Handled by if_fast_holey. HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS, // Handled by if_fast_double. PACKED_DOUBLE_ELEMENTS, // Handled by if_fast_holey_double. HOLEY_DOUBLE_ELEMENTS}; Label* labels[] = {// FAST_{SMI,}_ELEMENTS &if_fast_packed, &if_fast_packed, // FAST_HOLEY_{SMI,}_ELEMENTS &if_fast_holey, &if_fast_holey, // PACKED_DOUBLE_ELEMENTS &if_fast_double, // HOLEY_DOUBLE_ELEMENTS &if_fast_holey_double}; Switch(elements_kind, unimplemented_elements_kind, kinds, labels, arraysize(kinds)); BIND(&if_fast_packed); { Comment("fast packed elements"); exit_point->Return(LoadFixedArrayElement(CAST(elements), intptr_index)); } BIND(&if_fast_holey); { Comment("fast holey elements"); Node* element = LoadFixedArrayElement(CAST(elements), intptr_index); GotoIf(WordEqual(element, TheHoleConstant()), if_hole); exit_point->Return(element); } BIND(&if_fast_double); { Comment("packed double elements"); var_double_value->Bind(LoadFixedDoubleArrayElement(elements, intptr_index, MachineType::Float64())); Goto(rebox_double); } BIND(&if_fast_holey_double); { Comment("holey double elements"); Node* value = LoadFixedDoubleArrayElement(elements, intptr_index, MachineType::Float64(), 0, INTPTR_PARAMETERS, if_hole); var_double_value->Bind(value); Goto(rebox_double); } BIND(&if_nonfast); { STATIC_ASSERT(LAST_ELEMENTS_KIND == LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND); GotoIf(Int32GreaterThanOrEqual( elements_kind, Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)), &if_typed_array); GotoIf(Word32Equal(elements_kind, Int32Constant(DICTIONARY_ELEMENTS)), &if_dictionary); Goto(unimplemented_elements_kind); } BIND(&if_dictionary); { Comment("dictionary elements"); GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), out_of_bounds); TNode<Object> value = BasicLoadNumberDictionaryElement( CAST(elements), intptr_index, miss, if_hole); exit_point->Return(value); } BIND(&if_typed_array); { Comment("typed elements"); // Check if buffer has been neutered. Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset); GotoIf(IsDetachedBuffer(buffer), miss); // Bounds check. Node* length = SmiUntag(LoadTypedArrayLength(CAST(object))); GotoIfNot(UintPtrLessThan(intptr_index, length), out_of_bounds); Node* backing_store = LoadFixedTypedArrayBackingStore(CAST(elements)); Label uint8_elements(this), int8_elements(this), uint16_elements(this), int16_elements(this), uint32_elements(this), int32_elements(this), float32_elements(this), float64_elements(this), bigint64_elements(this), biguint64_elements(this); Label* elements_kind_labels[] = { &uint8_elements, &uint8_elements, &int8_elements, &uint16_elements, &int16_elements, &uint32_elements, &int32_elements, &float32_elements, &float64_elements, &bigint64_elements, &biguint64_elements}; int32_t elements_kinds[] = { UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS, UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS, INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_ELEMENTS, BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS}; const size_t kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1; DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds)); DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels)); Switch(elements_kind, miss, elements_kinds, elements_kind_labels, kTypedElementsKindCount); BIND(&uint8_elements); { Comment("UINT8_ELEMENTS"); // Handles UINT8_CLAMPED_ELEMENTS too. Node* element = Load(MachineType::Uint8(), backing_store, intptr_index); exit_point->Return(SmiFromInt32(element)); } BIND(&int8_elements); { Comment("INT8_ELEMENTS"); Node* element = Load(MachineType::Int8(), backing_store, intptr_index); exit_point->Return(SmiFromInt32(element)); } BIND(&uint16_elements); { Comment("UINT16_ELEMENTS"); Node* index = WordShl(intptr_index, IntPtrConstant(1)); Node* element = Load(MachineType::Uint16(), backing_store, index); exit_point->Return(SmiFromInt32(element)); } BIND(&int16_elements); { Comment("INT16_ELEMENTS"); Node* index = WordShl(intptr_index, IntPtrConstant(1)); Node* element = Load(MachineType::Int16(), backing_store, index); exit_point->Return(SmiFromInt32(element)); } BIND(&uint32_elements); { Comment("UINT32_ELEMENTS"); Node* index = WordShl(intptr_index, IntPtrConstant(2)); Node* element = Load(MachineType::Uint32(), backing_store, index); exit_point->Return(ChangeUint32ToTagged(element)); } BIND(&int32_elements); { Comment("INT32_ELEMENTS"); Node* index = WordShl(intptr_index, IntPtrConstant(2)); Node* element = Load(MachineType::Int32(), backing_store, index); exit_point->Return(ChangeInt32ToTagged(element)); } BIND(&float32_elements); { Comment("FLOAT32_ELEMENTS"); Node* index = WordShl(intptr_index, IntPtrConstant(2)); Node* element = Load(MachineType::Float32(), backing_store, index); var_double_value->Bind(ChangeFloat32ToFloat64(element)); Goto(rebox_double); } BIND(&float64_elements); { Comment("FLOAT64_ELEMENTS"); Node* index = WordShl(intptr_index, IntPtrConstant(3)); Node* element = Load(MachineType::Float64(), backing_store, index); var_double_value->Bind(element); Goto(rebox_double); } BIND(&bigint64_elements); { Comment("BIGINT64_ELEMENTS"); exit_point->Return(LoadFixedTypedArrayElementAsTagged( backing_store, intptr_index, BIGINT64_ELEMENTS, INTPTR_PARAMETERS)); } BIND(&biguint64_elements); { Comment("BIGUINT64_ELEMENTS"); exit_point->Return(LoadFixedTypedArrayElementAsTagged( backing_store, intptr_index, BIGUINT64_ELEMENTS, INTPTR_PARAMETERS)); } } } void AccessorAssembler::NameDictionaryNegativeLookup(Node* object, SloppyTNode<Name> name, Label* miss) { CSA_ASSERT(this, IsDictionaryMap(LoadMap(object))); TNode<NameDictionary> properties = CAST(LoadSlowProperties(object)); // Ensure the property does not exist in a dictionary-mode object. TVARIABLE(IntPtrT, var_name_index); Label done(this); NameDictionaryLookup<NameDictionary>(properties, name, miss, &var_name_index, &done); BIND(&done); } void AccessorAssembler::BranchIfStrictMode(Node* vector, Node* slot, Label* if_strict) { Node* sfi = LoadObjectField(vector, FeedbackVector::kSharedFunctionInfoOffset); TNode<FeedbackMetadata> metadata = CAST(LoadObjectField( sfi, SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset)); Node* slot_int = SmiToInt32(slot); // See VectorICComputer::index(). const int kItemsPerWord = FeedbackMetadata::VectorICComputer::kItemsPerWord; Node* word_index = Int32Div(slot_int, Int32Constant(kItemsPerWord)); Node* word_offset = Int32Mod(slot_int, Int32Constant(kItemsPerWord)); int32_t first_item = FeedbackMetadata::kHeaderSize - kHeapObjectTag; Node* offset = ElementOffsetFromIndex(ChangeInt32ToIntPtr(word_index), UINT32_ELEMENTS, INTPTR_PARAMETERS, first_item); Node* data = Load(MachineType::Int32(), metadata, offset); // See VectorICComputer::decode(). const int kBitsPerItem = FeedbackMetadata::kFeedbackSlotKindBits; Node* shift = Int32Mul(word_offset, Int32Constant(kBitsPerItem)); const int kMask = FeedbackMetadata::VectorICComputer::kMask; Node* kind = Word32And(Word32Shr(data, shift), Int32Constant(kMask)); STATIC_ASSERT(FeedbackSlotKind::kStoreGlobalSloppy <= FeedbackSlotKind::kLastSloppyKind); STATIC_ASSERT(FeedbackSlotKind::kStoreKeyedSloppy <= FeedbackSlotKind::kLastSloppyKind); STATIC_ASSERT(FeedbackSlotKind::kStoreNamedSloppy <= FeedbackSlotKind::kLastSloppyKind); GotoIfNot(Int32LessThanOrEqual(kind, Int32Constant(static_cast<int>( FeedbackSlotKind::kLastSloppyKind))), if_strict); } void AccessorAssembler::InvalidateValidityCellIfPrototype(Node* map, Node* bitfield2) { Label is_prototype(this), cont(this); if (bitfield2 == nullptr) { bitfield2 = LoadMapBitField2(map); } Branch(IsSetWord32(bitfield2, Map::IsPrototypeMapBit::kMask), &is_prototype, &cont); BIND(&is_prototype); { Node* maybe_prototype_info = LoadObjectField(map, Map::kTransitionsOrPrototypeInfoOffset); // If there's no prototype info then there's nothing to invalidate. GotoIf(TaggedIsSmi(maybe_prototype_info), &cont); Node* function = ExternalConstant( ExternalReference::invalidate_prototype_chains_function()); CallCFunction1(MachineType::AnyTagged(), MachineType::AnyTagged(), function, map); Goto(&cont); } BIND(&cont); } void AccessorAssembler::GenericElementLoad(Node* receiver, Node* receiver_map, SloppyTNode<Int32T> instance_type, Node* index, Label* slow) { Comment("integer index"); ExitPoint direct_exit(this); Label if_custom(this), if_element_hole(this), if_oob(this); // Receivers requiring non-standard element accesses (interceptors, access // checks, strings and string wrappers, proxies) are handled in the runtime. GotoIf(IsCustomElementsReceiverInstanceType(instance_type), &if_custom); Node* elements = LoadElements(receiver); Node* elements_kind = LoadMapElementsKind(receiver_map); Node* is_jsarray_condition = InstanceTypeEqual(instance_type, JS_ARRAY_TYPE); VARIABLE(var_double_value, MachineRepresentation::kFloat64); Label rebox_double(this, &var_double_value); // Unimplemented elements kinds fall back to a runtime call. Label* unimplemented_elements_kind = slow; IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1); EmitElementLoad(receiver, elements, elements_kind, index, is_jsarray_condition, &if_element_hole, &rebox_double, &var_double_value, unimplemented_elements_kind, &if_oob, slow, &direct_exit); BIND(&rebox_double); Return(AllocateHeapNumberWithValue(var_double_value.value())); BIND(&if_oob); { Comment("out of bounds"); // Positive OOB indices are effectively the same as hole loads. GotoIf(IntPtrGreaterThanOrEqual(index, IntPtrConstant(0)), &if_element_hole); // Negative keys can't take the fast OOB path, except for typed arrays. GotoIfNot(InstanceTypeEqual(instance_type, JS_TYPED_ARRAY_TYPE), slow); Return(UndefinedConstant()); } BIND(&if_element_hole); { Comment("found the hole"); Label return_undefined(this); BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, slow); BIND(&return_undefined); Return(UndefinedConstant()); } BIND(&if_custom); { Comment("check if string"); GotoIfNot(IsStringInstanceType(instance_type), slow); Comment("load string character"); Node* length = LoadAndUntagObjectField(receiver, String::kLengthOffset); GotoIfNot(UintPtrLessThan(index, length), slow); IncrementCounter(isolate()->counters()->ic_keyed_load_generic_smi(), 1); TailCallBuiltin(Builtins::kStringCharAt, NoContextConstant(), receiver, index); } } void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map, SloppyTNode<Int32T> instance_type, const LoadICParameters* p, Label* slow, UseStubCache use_stub_cache) { ExitPoint direct_exit(this); Comment("key is unique name"); Label if_found_on_receiver(this), if_property_dictionary(this), lookup_prototype_chain(this), special_receiver(this); VARIABLE(var_details, MachineRepresentation::kWord32); VARIABLE(var_value, MachineRepresentation::kTagged); // Receivers requiring non-standard accesses (interceptors, access // checks, strings and string wrappers) are handled in the runtime. GotoIf(IsSpecialReceiverInstanceType(instance_type), &special_receiver); // Check if the receiver has fast or slow properties. Node* bitfield3 = LoadMapBitField3(receiver_map); GotoIf(IsSetWord32<Map::IsDictionaryMapBit>(bitfield3), &if_property_dictionary); // Try looking up the property on the receiver; if unsuccessful, look // for a handler in the stub cache. TNode<DescriptorArray> descriptors = LoadMapDescriptors(receiver_map); Label if_descriptor_found(this), stub_cache(this); TVARIABLE(IntPtrT, var_name_index); Label* notfound = use_stub_cache == kUseStubCache ? &stub_cache : &lookup_prototype_chain; DescriptorLookup(p->name, descriptors, bitfield3, &if_descriptor_found, &var_name_index, notfound); BIND(&if_descriptor_found); { LoadPropertyFromFastObject(receiver, receiver_map, descriptors, var_name_index.value(), &var_details, &var_value); Goto(&if_found_on_receiver); } if (use_stub_cache == kUseStubCache) { BIND(&stub_cache); Comment("stub cache probe for fast property load"); TVARIABLE(MaybeObject, var_handler); Label found_handler(this, &var_handler), stub_cache_miss(this); TryProbeStubCache(isolate()->load_stub_cache(), receiver, p->name, &found_handler, &var_handler, &stub_cache_miss); BIND(&found_handler); { HandleLoadICHandlerCase(p, CAST(var_handler.value()), &stub_cache_miss, &direct_exit); } BIND(&stub_cache_miss); { // TODO(jkummerow): Check if the property exists on the prototype // chain. If it doesn't, then there's no point in missing. Comment("KeyedLoadGeneric_miss"); TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver, p->name, p->slot, p->vector); } } BIND(&if_property_dictionary); { Comment("dictionary property load"); // We checked for LAST_CUSTOM_ELEMENTS_RECEIVER before, which rules out // seeing global objects here (which would need special handling). TVARIABLE(IntPtrT, var_name_index); Label dictionary_found(this, &var_name_index); TNode<NameDictionary> properties = CAST(LoadSlowProperties(receiver)); NameDictionaryLookup<NameDictionary>(properties, CAST(p->name), &dictionary_found, &var_name_index, &lookup_prototype_chain); BIND(&dictionary_found); { LoadPropertyFromNameDictionary(properties, var_name_index.value(), &var_details, &var_value); Goto(&if_found_on_receiver); } } BIND(&if_found_on_receiver); { Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(), p->context, receiver, slow); IncrementCounter(isolate()->counters()->ic_keyed_load_generic_symbol(), 1); Return(value); } BIND(&lookup_prototype_chain); { VARIABLE(var_holder_map, MachineRepresentation::kTagged); VARIABLE(var_holder_instance_type, MachineRepresentation::kWord32); Label return_undefined(this); Variable* merged_variables[] = {&var_holder_map, &var_holder_instance_type}; Label loop(this, arraysize(merged_variables), merged_variables); var_holder_map.Bind(receiver_map); var_holder_instance_type.Bind(instance_type); // Private symbols must not be looked up on the prototype chain. GotoIf(IsPrivateSymbol(p->name), &return_undefined); Goto(&loop); BIND(&loop); { // Bailout if it can be an integer indexed exotic case. GotoIf(InstanceTypeEqual(var_holder_instance_type.value(), JS_TYPED_ARRAY_TYPE), slow); Node* proto = LoadMapPrototype(var_holder_map.value()); GotoIf(WordEqual(proto, NullConstant()), &return_undefined); Node* proto_map = LoadMap(proto); Node* proto_instance_type = LoadMapInstanceType(proto_map); var_holder_map.Bind(proto_map); var_holder_instance_type.Bind(proto_instance_type); Label next_proto(this), return_value(this, &var_value), goto_slow(this); TryGetOwnProperty(p->context, receiver, proto, proto_map, proto_instance_type, p->name, &return_value, &var_value, &next_proto, &goto_slow); // This trampoline and the next are required to appease Turbofan's // variable merging. BIND(&next_proto); Goto(&loop); BIND(&goto_slow); Goto(slow); BIND(&return_value); Return(var_value.value()); } BIND(&return_undefined); Return(UndefinedConstant()); } BIND(&special_receiver); { // TODO(jkummerow): Consider supporting JSModuleNamespace. GotoIfNot(InstanceTypeEqual(instance_type, JS_PROXY_TYPE), slow); // Private field/symbol lookup is not supported. GotoIf(IsPrivateSymbol(p->name), slow); direct_exit.ReturnCallStub( Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty), p->context, receiver /*holder is the same as receiver*/, p->name, receiver, SmiConstant(OnNonExistent::kReturnUndefined)); } } //////////////////// Stub cache access helpers. enum AccessorAssembler::StubCacheTable : int { kPrimary = static_cast<int>(StubCache::kPrimary), kSecondary = static_cast<int>(StubCache::kSecondary) }; Node* AccessorAssembler::StubCachePrimaryOffset(Node* name, Node* map) { // See v8::internal::StubCache::PrimaryOffset(). STATIC_ASSERT(StubCache::kCacheIndexShift == Name::kHashShift); // Compute the hash of the name (use entire hash field). Node* hash_field = LoadNameHashField(name); CSA_ASSERT(this, Word32Equal(Word32And(hash_field, Int32Constant(Name::kHashNotComputedMask)), Int32Constant(0))); // Using only the low bits in 64-bit mode is unlikely to increase the // risk of collision even if the heap is spread over an area larger than // 4Gb (and not at all if it isn't). Node* map32 = TruncateIntPtrToInt32(BitcastTaggedToWord(map)); // Base the offset on a simple combination of name and map. Node* hash = Int32Add(hash_field, map32); uint32_t mask = (StubCache::kPrimaryTableSize - 1) << StubCache::kCacheIndexShift; return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask))); } Node* AccessorAssembler::StubCacheSecondaryOffset(Node* name, Node* seed) { // See v8::internal::StubCache::SecondaryOffset(). // Use the seed from the primary cache in the secondary cache. Node* name32 = TruncateIntPtrToInt32(BitcastTaggedToWord(name)); Node* hash = Int32Sub(TruncateIntPtrToInt32(seed), name32); hash = Int32Add(hash, Int32Constant(StubCache::kSecondaryMagic)); int32_t mask = (StubCache::kSecondaryTableSize - 1) << StubCache::kCacheIndexShift; return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask))); } void AccessorAssembler::TryProbeStubCacheTable( StubCache* stub_cache, StubCacheTable table_id, Node* entry_offset, Node* name, Node* map, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss) { StubCache::Table table = static_cast<StubCache::Table>(table_id); #ifdef DEBUG if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) { Goto(if_miss); return; } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary) { Goto(if_miss); return; } #endif // The {table_offset} holds the entry offset times four (due to masking // and shifting optimizations). const int kMultiplier = sizeof(StubCache::Entry) >> Name::kHashShift; entry_offset = IntPtrMul(entry_offset, IntPtrConstant(kMultiplier)); // Check that the key in the entry matches the name. Node* key_base = ExternalConstant( ExternalReference::Create(stub_cache->key_reference(table))); Node* entry_key = Load(MachineType::Pointer(), key_base, entry_offset); GotoIf(WordNotEqual(name, entry_key), if_miss); // Get the map entry from the cache. DCHECK_EQ(kPointerSize * 2, stub_cache->map_reference(table).address() - stub_cache->key_reference(table).address()); Node* entry_map = Load(MachineType::Pointer(), key_base, IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize * 2))); GotoIf(WordNotEqual(map, entry_map), if_miss); DCHECK_EQ(kPointerSize, stub_cache->value_reference(table).address() - stub_cache->key_reference(table).address()); TNode<MaybeObject> handler = ReinterpretCast<MaybeObject>( Load(MachineType::TaggedPointer(), key_base, IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)))); // We found the handler. *var_handler = handler; Goto(if_handler); } void AccessorAssembler::TryProbeStubCache(StubCache* stub_cache, Node* receiver, Node* name, Label* if_handler, TVariable<MaybeObject>* var_handler, Label* if_miss) { Label try_secondary(this), miss(this); Counters* counters = isolate()->counters(); IncrementCounter(counters->megamorphic_stub_cache_probes(), 1); // Check that the {receiver} isn't a smi. GotoIf(TaggedIsSmi(receiver), &miss); Node* receiver_map = LoadMap(receiver); // Probe the primary table. Node* primary_offset = StubCachePrimaryOffset(name, receiver_map); TryProbeStubCacheTable(stub_cache, kPrimary, primary_offset, name, receiver_map, if_handler, var_handler, &try_secondary); BIND(&try_secondary); { // Probe the secondary table. Node* secondary_offset = StubCacheSecondaryOffset(name, primary_offset); TryProbeStubCacheTable(stub_cache, kSecondary, secondary_offset, name, receiver_map, if_handler, var_handler, &miss); } BIND(&miss); { IncrementCounter(counters->megamorphic_stub_cache_misses(), 1); Goto(if_miss); } } //////////////////// Entry points into private implementation (one per stub). void AccessorAssembler::LoadIC_BytecodeHandler(const LoadICParameters* p, ExitPoint* exit_point) { // Must be kept in sync with LoadIC. // This function is hand-tuned to omit frame construction for common cases, // e.g.: monomorphic field and constant loads through smi handlers. // Polymorphic ICs with a hit in the first two entries also omit frames. // TODO(jgruber): Frame omission is fragile and can be affected by minor // changes in control flow and logic. We currently have no way of ensuring // that no frame is constructed, so it's easy to break this optimization by // accident. Label stub_call(this, Label::kDeferred), miss(this, Label::kDeferred); // Inlined fast path. { Comment("LoadIC_BytecodeHandler_fast"); Node* recv_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(recv_map), &miss); TVARIABLE(MaybeObject, var_handler); Label try_polymorphic(this), if_handler(this, &var_handler); TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, recv_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, exit_point); BIND(&try_polymorphic); { TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &stub_call); HandlePolymorphicCase(recv_map, CAST(strong_feedback), &if_handler, &var_handler, &miss, 2); } } BIND(&stub_call); { Comment("LoadIC_BytecodeHandler_noninlined"); // Call into the stub that implements the non-inlined parts of LoadIC. Callable ic = Builtins::CallableFor(isolate(), Builtins::kLoadIC_Noninlined); Node* code_target = HeapConstant(ic.code()); exit_point->ReturnCallStub(ic.descriptor(), code_target, p->context, p->receiver, p->name, p->slot, p->vector); } BIND(&miss); { Comment("LoadIC_BytecodeHandler_miss"); exit_point->ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name, p->slot, p->vector); } } void AccessorAssembler::LoadIC(const LoadICParameters* p) { // Must be kept in sync with LoadIC_BytecodeHandler. ExitPoint direct_exit(this); TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), non_inlined(this, Label::kDeferred), try_polymorphic(this), miss(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(receiver_map), &miss); // Check monomorphic case. TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit); BIND(&try_polymorphic); TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss); { // Check polymorphic case. Comment("LoadIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &non_inlined); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, &var_handler, &miss, 2); } BIND(&non_inlined); { LoadIC_Noninlined(p, receiver_map, strong_feedback, &var_handler, &if_handler, &miss, &direct_exit); } BIND(&miss); direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name, p->slot, p->vector); } void AccessorAssembler::LoadIC_Noninlined(const LoadICParameters* p, Node* receiver_map, TNode<HeapObject> feedback, TVariable<MaybeObject>* var_handler, Label* if_handler, Label* miss, ExitPoint* exit_point) { Label try_uninitialized(this, Label::kDeferred); // Neither deprecated map nor monomorphic. These cases are handled in the // bytecode handler. CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(receiver_map))); CSA_ASSERT(this, WordNotEqual(receiver_map, feedback)); CSA_ASSERT(this, Word32BinaryNot(IsWeakFixedArrayMap(LoadMap(feedback)))); DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep()); { // Check megamorphic case. GotoIfNot(WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)), &try_uninitialized); TryProbeStubCache(isolate()->load_stub_cache(), p->receiver, p->name, if_handler, var_handler, miss); } BIND(&try_uninitialized); { // Check uninitialized case. GotoIfNot( WordEqual(feedback, LoadRoot(Heap::kuninitialized_symbolRootIndex)), miss); exit_point->ReturnCallStub( Builtins::CallableFor(isolate(), Builtins::kLoadIC_Uninitialized), p->context, p->receiver, p->name, p->slot, p->vector); } } void AccessorAssembler::LoadIC_Uninitialized(const LoadICParameters* p) { Label miss(this, Label::kDeferred); Node* receiver = p->receiver; GotoIf(TaggedIsSmi(receiver), &miss); Node* receiver_map = LoadMap(receiver); Node* instance_type = LoadMapInstanceType(receiver_map); // Optimistically write the state transition to the vector. StoreFeedbackVectorSlot(p->vector, p->slot, LoadRoot(Heap::kpremonomorphic_symbolRootIndex), SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS); StoreWeakReferenceInFeedbackVector(p->vector, p->slot, receiver_map, kPointerSize, SMI_PARAMETERS); { // Special case for Function.prototype load, because it's very common // for ICs that are only executed once (MyFunc.prototype.foo = ...). Label not_function_prototype(this, Label::kDeferred); GotoIfNot(InstanceTypeEqual(instance_type, JS_FUNCTION_TYPE), ¬_function_prototype); GotoIfNot(IsPrototypeString(p->name), ¬_function_prototype); GotoIfPrototypeRequiresRuntimeLookup(CAST(receiver), CAST(receiver_map), ¬_function_prototype); Return(LoadJSFunctionPrototype(receiver, &miss)); BIND(¬_function_prototype); } GenericPropertyLoad(receiver, receiver_map, instance_type, p, &miss, kDontUseStubCache); BIND(&miss); { // Undo the optimistic state transition. StoreFeedbackVectorSlot(p->vector, p->slot, LoadRoot(Heap::kuninitialized_symbolRootIndex), SKIP_WRITE_BARRIER, 0, SMI_PARAMETERS); TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name, p->slot, p->vector); } } void AccessorAssembler::LoadGlobalIC(TNode<FeedbackVector> vector, Node* slot, const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point, ParameterMode slot_mode) { Label try_handler(this, Label::kDeferred), miss(this, Label::kDeferred); LoadGlobalIC_TryPropertyCellCase(vector, slot, lazy_context, exit_point, &try_handler, &miss, slot_mode); BIND(&try_handler); LoadGlobalIC_TryHandlerCase(vector, slot, lazy_context, lazy_name, typeof_mode, exit_point, &miss, slot_mode); BIND(&miss); { Comment("LoadGlobalIC_MissCase"); TNode<Context> context = lazy_context(); TNode<Name> name = lazy_name(); exit_point->ReturnCallRuntime(Runtime::kLoadGlobalIC_Miss, context, name, ParameterToTagged(slot, slot_mode), vector); } } void AccessorAssembler::LoadGlobalIC_TryPropertyCellCase( TNode<FeedbackVector> vector, Node* slot, const LazyNode<Context>& lazy_context, ExitPoint* exit_point, Label* try_handler, Label* miss, ParameterMode slot_mode) { Comment("LoadGlobalIC_TryPropertyCellCase"); Label if_lexical_var(this), if_property_cell(this); TNode<MaybeObject> maybe_weak_ref = LoadFeedbackVectorSlot(vector, slot, 0, slot_mode); Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_property_cell); BIND(&if_property_cell); { // Load value or try handler case if the weak reference is cleared. CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_weak_ref)); TNode<PropertyCell> property_cell = CAST(ToWeakHeapObject(maybe_weak_ref, try_handler)); TNode<Object> value = LoadObjectField(property_cell, PropertyCell::kValueOffset); GotoIf(WordEqual(value, TheHoleConstant()), miss); exit_point->Return(value); } BIND(&if_lexical_var); { Comment("Load lexical variable"); TNode<IntPtrT> lexical_handler = SmiUntag(CAST(maybe_weak_ref)); TNode<IntPtrT> context_index = Signed(DecodeWord<FeedbackNexus::ContextIndexBits>(lexical_handler)); TNode<IntPtrT> slot_index = Signed(DecodeWord<FeedbackNexus::SlotIndexBits>(lexical_handler)); TNode<Context> context = lazy_context(); TNode<Context> script_context = LoadScriptContext(context, context_index); TNode<Object> result = LoadContextElement(script_context, slot_index); exit_point->Return(result); } } void AccessorAssembler::LoadGlobalIC_TryHandlerCase( TNode<FeedbackVector> vector, Node* slot, const LazyNode<Context>& lazy_context, const LazyNode<Name>& lazy_name, TypeofMode typeof_mode, ExitPoint* exit_point, Label* miss, ParameterMode slot_mode) { Comment("LoadGlobalIC_TryHandlerCase"); Label call_handler(this), non_smi(this); TNode<MaybeObject> feedback_element = LoadFeedbackVectorSlot(vector, slot, kPointerSize, slot_mode); TNode<Object> handler = CAST(feedback_element); GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)), miss); OnNonExistent on_nonexistent = typeof_mode == NOT_INSIDE_TYPEOF ? OnNonExistent::kThrowReferenceError : OnNonExistent::kReturnUndefined; TNode<Context> context = lazy_context(); TNode<Context> native_context = LoadNativeContext(context); TNode<JSGlobalProxy> receiver = CAST(LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX)); Node* holder = LoadContextElement(native_context, Context::EXTENSION_INDEX); LoadICParameters p(context, receiver, lazy_name(), ParameterToTagged(slot, slot_mode), vector, holder); HandleLoadICHandlerCase(&p, handler, miss, exit_point, ICMode::kGlobalIC, on_nonexistent); } void AccessorAssembler::KeyedLoadIC(const LoadICParameters* p) { ExitPoint direct_exit(this); TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred), try_polymorphic_name(this, Label::kDeferred), miss(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(receiver_map), &miss); // Check monomorphic case. TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); { HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit, ICMode::kNonGlobalIC, OnNonExistent::kReturnUndefined, kSupportElements); } BIND(&try_polymorphic); TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss); { // Check polymorphic case. Comment("KeyedLoadIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, &var_handler, &miss, 2); } BIND(&try_megamorphic); { // Check megamorphic case. Comment("KeyedLoadIC_try_megamorphic"); GotoIfNot(WordEqual(strong_feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)), &try_polymorphic_name); // TODO(jkummerow): Inline this? Or some of it? TailCallBuiltin(Builtins::kKeyedLoadIC_Megamorphic, p->context, p->receiver, p->name, p->slot, p->vector); } BIND(&try_polymorphic_name); { // We might have a name in feedback, and a weak fixed array in the next // slot. Node* name = p->name; Comment("KeyedLoadIC_try_polymorphic_name"); VARIABLE(var_name, MachineRepresentation::kTagged, name); VARIABLE(var_index, MachineType::PointerRepresentation()); Label if_polymorphic_name(this, &var_name), if_internalized(this), if_notinternalized(this, Label::kDeferred); // Fast-case: The recorded {feedback} matches the {name}. GotoIf(WordEqual(strong_feedback, name), &if_polymorphic_name); // Try to internalize the {name} if it isn't already. TryToName(name, &miss, &var_index, &if_internalized, &var_name, &miss, &if_notinternalized); BIND(&if_internalized); { // The {var_name} now contains a unique name. Branch(WordEqual(strong_feedback, var_name.value()), &if_polymorphic_name, &miss); } BIND(&if_notinternalized); { // Try to internalize the {name}. Node* function = ExternalConstant( ExternalReference::try_internalize_string_function()); Node* const isolate_ptr = ExternalConstant(ExternalReference::isolate_address(isolate())); var_name.Bind(CallCFunction2( MachineType::AnyTagged(), MachineType::Pointer(), MachineType::AnyTagged(), function, isolate_ptr, name)); Goto(&if_internalized); } BIND(&if_polymorphic_name); { // If the name comparison succeeded, we know we have a weak fixed array // with at least one map/handler pair. Node* name = var_name.value(); TailCallBuiltin(Builtins::kKeyedLoadIC_PolymorphicName, p->context, p->receiver, name, p->slot, p->vector); } } BIND(&miss); { Comment("KeyedLoadIC_miss"); TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver, p->name, p->slot, p->vector); } } void AccessorAssembler::KeyedLoadICGeneric(const LoadICParameters* p) { VARIABLE(var_index, MachineType::PointerRepresentation()); VARIABLE(var_unique, MachineRepresentation::kTagged); var_unique.Bind(p->name); // Dummy initialization. Label if_index(this), if_unique_name(this), if_notunique(this), slow(this); Node* receiver = p->receiver; GotoIf(TaggedIsSmi(receiver), &slow); Node* receiver_map = LoadMap(receiver); Node* instance_type = LoadMapInstanceType(receiver_map); TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique, &slow, &if_notunique); BIND(&if_index); { GenericElementLoad(receiver, receiver_map, instance_type, var_index.value(), &slow); } BIND(&if_unique_name); { LoadICParameters pp = *p; pp.name = var_unique.value(); GenericPropertyLoad(receiver, receiver_map, instance_type, &pp, &slow); } BIND(&if_notunique); { if (FLAG_internalize_on_the_fly) { // Ideally we could return undefined directly here if the name is not // found in the string table, i.e. it was never internalized, but that // invariant doesn't hold with named property interceptors (at this // point), so we take the {slow} path instead. Label if_in_string_table(this); TryInternalizeString(p->name, &if_index, &var_index, &if_in_string_table, &var_unique, &slow, &slow); BIND(&if_in_string_table); { // TODO(bmeurer): We currently use a version of GenericPropertyLoad // here, where we don't try to probe the megamorphic stub cache after // successfully internalizing the incoming string. Past experiments // with this have shown that it causes too much traffic on the stub // cache. We may want to re-evaluate that in the future. LoadICParameters pp = *p; pp.name = var_unique.value(); GenericPropertyLoad(receiver, receiver_map, instance_type, &pp, &slow, kDontUseStubCache); } } else { Goto(&slow); } } BIND(&slow); { Comment("KeyedLoadGeneric_slow"); IncrementCounter(isolate()->counters()->ic_keyed_load_generic_slow(), 1); // TODO(jkummerow): Should we use the GetProperty TF stub instead? TailCallRuntime(Runtime::kKeyedGetProperty, p->context, p->receiver, p->name); } } void AccessorAssembler::KeyedLoadICPolymorphicName(const LoadICParameters* p) { TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), miss(this, Label::kDeferred); Node* receiver = p->receiver; Node* receiver_map = LoadReceiverMap(receiver); Node* name = p->name; Node* vector = p->vector; Node* slot = p->slot; Node* context = p->context; // When we get here, we know that the {name} matches the recorded // feedback name in the {vector} and can safely be used for the // LoadIC handler logic below. CSA_ASSERT(this, IsName(name)); CSA_ASSERT(this, Word32BinaryNot(IsDeprecatedMap(receiver_map))); CSA_ASSERT(this, WordEqual(name, CAST(LoadFeedbackVectorSlot( vector, slot, 0, SMI_PARAMETERS)))); // Check if we have a matching handler for the {receiver_map}. TNode<MaybeObject> feedback_element = LoadFeedbackVectorSlot(vector, slot, kPointerSize, SMI_PARAMETERS); TNode<WeakFixedArray> array = CAST(feedback_element); HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss, 1); BIND(&if_handler); { ExitPoint direct_exit(this); HandleLoadICHandlerCase(p, CAST(var_handler.value()), &miss, &direct_exit, ICMode::kNonGlobalIC, OnNonExistent::kReturnUndefined, kOnlyProperties); } BIND(&miss); { Comment("KeyedLoadIC_miss"); TailCallRuntime(Runtime::kKeyedLoadIC_Miss, context, receiver, name, slot, vector); } } void AccessorAssembler::StoreIC(const StoreICParameters* p) { TVARIABLE(MaybeObject, var_handler, ReinterpretCast<MaybeObject>(SmiConstant(0))); Label if_handler(this, &var_handler), if_handler_from_stub_cache(this, &var_handler, Label::kDeferred), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred), try_uninitialized(this, Label::kDeferred), miss(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(receiver_map), &miss); // Check monomorphic case. TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); { Comment("StoreIC_if_handler"); HandleStoreICHandlerCase(p, var_handler.value(), &miss, ICMode::kNonGlobalIC); } BIND(&try_polymorphic); TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss); { // Check polymorphic case. Comment("StoreIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, &var_handler, &miss, 2); } BIND(&try_megamorphic); { // Check megamorphic case. GotoIfNot(WordEqual(strong_feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)), &try_uninitialized); TryProbeStubCache(isolate()->store_stub_cache(), p->receiver, p->name, &if_handler, &var_handler, &miss); } BIND(&try_uninitialized); { // Check uninitialized case. GotoIfNot(WordEqual(strong_feedback, LoadRoot(Heap::kuninitialized_symbolRootIndex)), &miss); TailCallBuiltin(Builtins::kStoreIC_Uninitialized, p->context, p->receiver, p->name, p->value, p->slot, p->vector); } BIND(&miss); { TailCallRuntime(Runtime::kStoreIC_Miss, p->context, p->value, p->slot, p->vector, p->receiver, p->name); } } void AccessorAssembler::StoreGlobalIC(const StoreICParameters* pp) { Label if_lexical_var(this), if_property_cell(this); TNode<MaybeObject> maybe_weak_ref = LoadFeedbackVectorSlot(pp->vector, pp->slot, 0, SMI_PARAMETERS); Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_property_cell); BIND(&if_property_cell); { Label try_handler(this), miss(this, Label::kDeferred); CSA_ASSERT(this, IsWeakOrClearedHeapObject(maybe_weak_ref)); TNode<PropertyCell> property_cell = CAST(ToWeakHeapObject(maybe_weak_ref, &try_handler)); ExitPoint direct_exit(this); StoreGlobalIC_PropertyCellCase(property_cell, pp->value, &direct_exit, &miss); BIND(&try_handler); { Comment("StoreGlobalIC_try_handler"); TNode<MaybeObject> handler = LoadFeedbackVectorSlot( pp->vector, pp->slot, kPointerSize, SMI_PARAMETERS); GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)), &miss); StoreICParameters p = *pp; DCHECK_NULL(p.receiver); Node* native_context = LoadNativeContext(p.context); p.receiver = LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX); HandleStoreICHandlerCase(&p, handler, &miss, ICMode::kGlobalIC); } BIND(&miss); { TailCallRuntime(Runtime::kStoreGlobalIC_Miss, pp->context, pp->value, pp->slot, pp->vector, pp->name); } } BIND(&if_lexical_var); { Comment("Store lexical variable"); TNode<IntPtrT> lexical_handler = SmiUntag(CAST(maybe_weak_ref)); TNode<IntPtrT> context_index = Signed(DecodeWord<FeedbackNexus::ContextIndexBits>(lexical_handler)); TNode<IntPtrT> slot_index = Signed(DecodeWord<FeedbackNexus::SlotIndexBits>(lexical_handler)); TNode<Context> script_context = LoadScriptContext(CAST(pp->context), context_index); StoreContextElement(script_context, slot_index, pp->value); Return(pp->value); } } void AccessorAssembler::StoreGlobalIC_PropertyCellCase(Node* property_cell, Node* value, ExitPoint* exit_point, Label* miss) { Comment("StoreGlobalIC_TryPropertyCellCase"); CSA_ASSERT(this, IsPropertyCell(property_cell)); // Load the payload of the global parameter cell. A hole indicates that // the cell has been invalidated and that the store must be handled by the // runtime. Node* cell_contents = LoadObjectField(property_cell, PropertyCell::kValueOffset); Node* details = LoadAndUntagToWord32ObjectField(property_cell, PropertyCell::kDetailsOffset); GotoIf(IsSetWord32(details, PropertyDetails::kAttributesReadOnlyMask), miss); CSA_ASSERT(this, Word32Equal(DecodeWord32<PropertyDetails::KindField>(details), Int32Constant(kData))); Node* type = DecodeWord32<PropertyDetails::PropertyCellTypeField>(details); Label constant(this), store(this), not_smi(this); GotoIf(Word32Equal(type, Int32Constant( static_cast<int>(PropertyCellType::kConstant))), &constant); GotoIf(IsTheHole(cell_contents), miss); GotoIf(Word32Equal( type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))), &store); CSA_ASSERT(this, Word32Or(Word32Equal(type, Int32Constant(static_cast<int>( PropertyCellType::kConstantType))), Word32Equal(type, Int32Constant(static_cast<int>( PropertyCellType::kUndefined))))); GotoIfNot(TaggedIsSmi(cell_contents), ¬_smi); GotoIfNot(TaggedIsSmi(value), miss); Goto(&store); BIND(¬_smi); { GotoIf(TaggedIsSmi(value), miss); Node* expected_map = LoadMap(cell_contents); Node* map = LoadMap(value); GotoIfNot(WordEqual(expected_map, map), miss); Goto(&store); } BIND(&store); { StoreObjectField(property_cell, PropertyCell::kValueOffset, value); exit_point->Return(value); } BIND(&constant); { GotoIfNot(WordEqual(cell_contents, value), miss); exit_point->Return(value); } } void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) { Label miss(this, Label::kDeferred); { TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred), try_polymorphic_name(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(receiver_map), &miss); // Check monomorphic case. TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); { Comment("KeyedStoreIC_if_handler"); HandleStoreICHandlerCase(p, var_handler.value(), &miss, ICMode::kNonGlobalIC, kSupportElements); } BIND(&try_polymorphic); TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss); { // CheckPolymorphic case. Comment("KeyedStoreIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler, &var_handler, &miss, 2); } BIND(&try_megamorphic); { // Check megamorphic case. Comment("KeyedStoreIC_try_megamorphic"); GotoIfNot(WordEqual(strong_feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)), &try_polymorphic_name); TailCallBuiltin(Builtins::kKeyedStoreIC_Megamorphic, p->context, p->receiver, p->name, p->value, p->slot, p->vector); } BIND(&try_polymorphic_name); { // We might have a name in feedback, and a fixed array in the next slot. Comment("KeyedStoreIC_try_polymorphic_name"); GotoIfNot(WordEqual(strong_feedback, p->name), &miss); // If the name comparison succeeded, we know we have a feedback vector // with at least one map/handler pair. TNode<MaybeObject> feedback_element = LoadFeedbackVectorSlot( p->vector, p->slot, kPointerSize, SMI_PARAMETERS); TNode<WeakFixedArray> array = CAST(feedback_element); HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss, 1); } } BIND(&miss); { Comment("KeyedStoreIC_miss"); TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot, p->vector, p->receiver, p->name); } } void AccessorAssembler::StoreInArrayLiteralIC(const StoreICParameters* p) { Label miss(this, Label::kDeferred); { TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred); Node* array_map = LoadReceiverMap(p->receiver); GotoIf(IsDeprecatedMap(array_map), &miss); TNode<MaybeObject> feedback = TryMonomorphicCase(p->slot, p->vector, array_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); { Comment("StoreInArrayLiteralIC_if_handler"); // This is a stripped-down version of HandleStoreICHandlerCase. TNode<HeapObject> handler = CAST(var_handler.value()); Label if_transitioning_element_store(this); GotoIfNot(IsCode(handler), &if_transitioning_element_store); TailCallStub(StoreWithVectorDescriptor{}, CAST(handler), CAST(p->context), p->receiver, p->name, p->value, p->slot, p->vector); BIND(&if_transitioning_element_store); { TNode<MaybeObject> maybe_transition_map = LoadHandlerDataField(CAST(handler), 1); TNode<Map> transition_map = CAST(ToWeakHeapObject(maybe_transition_map, &miss)); GotoIf(IsDeprecatedMap(transition_map), &miss); Node* code = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset); CSA_ASSERT(this, IsCode(code)); TailCallStub(StoreTransitionDescriptor{}, code, p->context, p->receiver, p->name, transition_map, p->value, p->slot, p->vector); } } BIND(&try_polymorphic); TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss); { Comment("StoreInArrayLiteralIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(array_map, CAST(strong_feedback), &if_handler, &var_handler, &miss, 2); } BIND(&try_megamorphic); { Comment("StoreInArrayLiteralIC_try_megamorphic"); CSA_ASSERT( this, Word32Or(WordEqual(strong_feedback, LoadRoot(Heap::kuninitialized_symbolRootIndex)), WordEqual(strong_feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)))); GotoIfNot(WordEqual(strong_feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)), &miss); TailCallRuntime(Runtime::kStoreInArrayLiteralIC_Slow, p->context, p->value, p->receiver, p->name); } } BIND(&miss); { Comment("StoreInArrayLiteralIC_miss"); // TODO(neis): Introduce Runtime::kStoreInArrayLiteralIC_Miss. TailCallRuntime(Runtime::kKeyedStoreIC_Miss, p->context, p->value, p->slot, p->vector, p->receiver, p->name); } } //////////////////// Public methods. void AccessorAssembler::GenerateLoadIC() { typedef LoadWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); LoadICParameters p(context, receiver, name, slot, vector); LoadIC(&p); } void AccessorAssembler::GenerateLoadIC_Noninlined() { typedef LoadWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); ExitPoint direct_exit(this); TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler), miss(this, Label::kDeferred); Node* receiver_map = LoadReceiverMap(receiver); TNode<MaybeObject> feedback_element = LoadFeedbackVectorSlot(vector, slot, 0, SMI_PARAMETERS); TNode<HeapObject> feedback = CAST(feedback_element); LoadICParameters p(context, receiver, name, slot, vector); LoadIC_Noninlined(&p, receiver_map, feedback, &var_handler, &if_handler, &miss, &direct_exit); BIND(&if_handler); HandleLoadICHandlerCase(&p, CAST(var_handler.value()), &miss, &direct_exit); BIND(&miss); direct_exit.ReturnCallRuntime(Runtime::kLoadIC_Miss, context, receiver, name, slot, vector); } void AccessorAssembler::GenerateLoadIC_Uninitialized() { typedef LoadWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); LoadICParameters p(context, receiver, name, slot, vector); LoadIC_Uninitialized(&p); } void AccessorAssembler::GenerateLoadICTrampoline() { typedef LoadDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* context = Parameter(Descriptor::kContext); Node* vector = LoadFeedbackVectorForStub(); TailCallBuiltin(Builtins::kLoadIC, context, receiver, name, slot, vector); } void AccessorAssembler::GenerateLoadGlobalIC(TypeofMode typeof_mode) { typedef LoadGlobalWithVectorDescriptor Descriptor; Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); ExitPoint direct_exit(this); LoadGlobalIC(CAST(vector), slot, // lazy_context [=] { return CAST(context); }, // lazy_name [=] { return CAST(name); }, typeof_mode, &direct_exit); } void AccessorAssembler::GenerateLoadGlobalICTrampoline(TypeofMode typeof_mode) { typedef LoadGlobalDescriptor Descriptor; Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* context = Parameter(Descriptor::kContext); Node* vector = LoadFeedbackVectorForStub(); Callable callable = CodeFactory::LoadGlobalICInOptimizedCode(isolate(), typeof_mode); TailCallStub(callable, context, name, slot, vector); } void AccessorAssembler::GenerateKeyedLoadIC() { typedef LoadWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); LoadICParameters p(context, receiver, name, slot, vector); KeyedLoadIC(&p); } void AccessorAssembler::GenerateKeyedLoadICTrampoline() { typedef LoadDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* context = Parameter(Descriptor::kContext); Node* vector = LoadFeedbackVectorForStub(); TailCallBuiltin(Builtins::kKeyedLoadIC, context, receiver, name, slot, vector); } void AccessorAssembler::GenerateKeyedLoadIC_Megamorphic() { typedef LoadWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); LoadICParameters p(context, receiver, name, slot, vector); KeyedLoadICGeneric(&p); } void AccessorAssembler::GenerateKeyedLoadIC_PolymorphicName() { typedef LoadWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); LoadICParameters p(context, receiver, name, slot, vector); KeyedLoadICPolymorphicName(&p); } void AccessorAssembler::GenerateStoreGlobalIC() { typedef StoreGlobalWithVectorDescriptor Descriptor; Node* name = Parameter(Descriptor::kName); Node* value = Parameter(Descriptor::kValue); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); StoreICParameters p(context, nullptr, name, value, slot, vector); StoreGlobalIC(&p); } void AccessorAssembler::GenerateStoreGlobalICTrampoline() { typedef StoreGlobalDescriptor Descriptor; Node* name = Parameter(Descriptor::kName); Node* value = Parameter(Descriptor::kValue); Node* slot = Parameter(Descriptor::kSlot); Node* context = Parameter(Descriptor::kContext); Node* vector = LoadFeedbackVectorForStub(); TailCallBuiltin(Builtins::kStoreGlobalIC, context, name, value, slot, vector); } void AccessorAssembler::GenerateStoreIC() { typedef StoreWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* value = Parameter(Descriptor::kValue); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); StoreICParameters p(context, receiver, name, value, slot, vector); StoreIC(&p); } void AccessorAssembler::GenerateStoreICTrampoline() { typedef StoreDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* value = Parameter(Descriptor::kValue); Node* slot = Parameter(Descriptor::kSlot); Node* context = Parameter(Descriptor::kContext); Node* vector = LoadFeedbackVectorForStub(); TailCallBuiltin(Builtins::kStoreIC, context, receiver, name, value, slot, vector); } void AccessorAssembler::GenerateKeyedStoreIC() { typedef StoreWithVectorDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* value = Parameter(Descriptor::kValue); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); StoreICParameters p(context, receiver, name, value, slot, vector); KeyedStoreIC(&p); } void AccessorAssembler::GenerateKeyedStoreICTrampoline() { typedef StoreDescriptor Descriptor; Node* receiver = Parameter(Descriptor::kReceiver); Node* name = Parameter(Descriptor::kName); Node* value = Parameter(Descriptor::kValue); Node* slot = Parameter(Descriptor::kSlot); Node* context = Parameter(Descriptor::kContext); Node* vector = LoadFeedbackVectorForStub(); TailCallBuiltin(Builtins::kKeyedStoreIC, context, receiver, name, value, slot, vector); } void AccessorAssembler::GenerateStoreInArrayLiteralIC() { typedef StoreWithVectorDescriptor Descriptor; Node* array = Parameter(Descriptor::kReceiver); Node* index = Parameter(Descriptor::kName); Node* value = Parameter(Descriptor::kValue); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); StoreICParameters p(context, array, index, value, slot, vector); StoreInArrayLiteralIC(&p); } void AccessorAssembler::GenerateCloneObjectIC() { typedef CloneObjectWithVectorDescriptor Descriptor; Node* source = Parameter(Descriptor::kSource); Node* flags = Parameter(Descriptor::kFlags); Node* slot = Parameter(Descriptor::kSlot); Node* vector = Parameter(Descriptor::kVector); Node* context = Parameter(Descriptor::kContext); TVARIABLE(MaybeObject, var_handler); Label if_handler(this, &var_handler); Label miss(this, Label::kDeferred), try_polymorphic(this, Label::kDeferred), try_megamorphic(this, Label::kDeferred); CSA_SLOW_ASSERT(this, TaggedIsNotSmi(source)); Node* source_map = LoadMap(UncheckedCast<HeapObject>(source)); GotoIf(IsDeprecatedMap(source_map), &miss); TNode<MaybeObject> feedback = TryMonomorphicCase( slot, vector, source_map, &if_handler, &var_handler, &try_polymorphic); BIND(&if_handler); { Comment("CloneObjectIC_if_handler"); // Handlers for the CloneObjectIC stub are weak references to the Map of // a result object. TNode<Map> result_map = CAST(var_handler.value()); TVARIABLE(Object, var_properties, EmptyFixedArrayConstant()); TVARIABLE(FixedArrayBase, var_elements, EmptyFixedArrayConstant()); Label allocate_object(this); GotoIf(IsNullOrUndefined(source), &allocate_object); CSA_SLOW_ASSERT(this, IsJSObjectMap(result_map)); // The IC fast case should only be taken if the result map a compatible // elements kind with the source object. TNode<FixedArrayBase> source_elements = LoadElements(source); auto flags = ExtractFixedArrayFlag::kAllFixedArraysDontCopyCOW; var_elements = CAST(CloneFixedArray(source_elements, flags)); // Copy the PropertyArray backing store. The source PropertyArray must be // either an Smi, or a PropertyArray. // FIXME: Make a CSA macro for this TNode<Object> source_properties = LoadObjectField(source, JSObject::kPropertiesOrHashOffset); { GotoIf(TaggedIsSmi(source_properties), &allocate_object); GotoIf(IsEmptyFixedArray(source_properties), &allocate_object); // This IC requires that the source object has fast properties CSA_SLOW_ASSERT(this, IsPropertyArray(CAST(source_properties))); TNode<IntPtrT> length = LoadPropertyArrayLength( UncheckedCast<PropertyArray>(source_properties)); GotoIf(IntPtrEqual(length, IntPtrConstant(0)), &allocate_object); auto mode = INTPTR_PARAMETERS; var_properties = CAST(AllocatePropertyArray(length, mode)); CopyPropertyArrayValues(source_properties, var_properties.value(), length, SKIP_WRITE_BARRIER, mode); } Goto(&allocate_object); BIND(&allocate_object); TNode<JSObject> object = UncheckedCast<JSObject>(AllocateJSObjectFromMap( result_map, var_properties.value(), var_elements.value())); ReturnIf(IsNullOrUndefined(source), object); // Lastly, clone any in-object properties. // Determine the inobject property capacity of both objects, and copy the // smaller number into the resulting object. Node* source_start = LoadMapInobjectPropertiesStartInWords(source_map); Node* source_size = LoadMapInstanceSizeInWords(source_map); Node* result_start = LoadMapInobjectPropertiesStartInWords(result_map); Node* field_offset_difference = TimesPointerSize(IntPtrSub(result_start, source_start)); BuildFastLoop(source_start, source_size, [=](Node* field_index) { Node* field_offset = TimesPointerSize(field_index); Node* field = LoadObjectField(source, field_offset); Node* result_offset = IntPtrAdd(field_offset, field_offset_difference); StoreObjectFieldNoWriteBarrier(object, result_offset, field); }, 1, INTPTR_PARAMETERS, IndexAdvanceMode::kPost); Return(object); } BIND(&try_polymorphic); TNode<HeapObject> strong_feedback = ToStrongHeapObject(feedback, &miss); { Comment("CloneObjectIC_try_polymorphic"); GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)), &try_megamorphic); HandlePolymorphicCase(source_map, CAST(strong_feedback), &if_handler, &var_handler, &miss, 2); } BIND(&try_megamorphic); { Comment("CloneObjectIC_try_megamorphic"); CSA_ASSERT( this, Word32Or(WordEqual(strong_feedback, LoadRoot(Heap::kuninitialized_symbolRootIndex)), WordEqual(strong_feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)))); GotoIfNot(WordEqual(strong_feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)), &miss); TailCallRuntime(Runtime::kCloneObjectIC_Slow, context, source, flags); } BIND(&miss); { Comment("CloneObjectIC_miss"); Node* map_or_result = CallRuntime(Runtime::kCloneObjectIC_Miss, context, source, flags, slot, vector); var_handler = UncheckedCast<MaybeObject>(map_or_result); GotoIf(IsMap(map_or_result), &if_handler); CSA_ASSERT(this, IsJSObject(map_or_result)); Return(map_or_result); } } } // namespace internal } // namespace v8