Commit 248a3e25 authored by jkummerow's avatar jkummerow Committed by Commit bot

[refactoring] Pull AccessorAssembler out of CodeStubAssembler

The new AccessorAssembler encapsulates all the functionality that's
specific to building LoadIC/StoreIC stubs.
There are two header files (accessor-assembler.h and
accessor-assembler-impl.h) so that clients of the assembler can include
the one, and subclassing assemblers can include the other.

Review-Url: https://codereview.chromium.org/2507733002
Cr-Commit-Position: refs/heads/master@{#41037}
parent f21a6b25
......@@ -1414,6 +1414,9 @@ v8_source_set("v8_base") {
"src/ic/access-compiler-data.h",
"src/ic/access-compiler.cc",
"src/ic/access-compiler.h",
"src/ic/accessor-assembler-impl.h",
"src/ic/accessor-assembler.cc",
"src/ic/accessor-assembler.h",
"src/ic/call-optimization.cc",
"src/ic/call-optimization.h",
"src/ic/handler-compiler.cc",
......
......@@ -4,6 +4,7 @@
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/ic/accessor-assembler.h"
#include "src/ic/handler-compiler.h"
#include "src/ic/ic.h"
#include "src/ic/keyed-store-generic.h"
......@@ -13,18 +14,7 @@ namespace internal {
void Builtins::Generate_KeyedLoadIC_Megamorphic_TF(
compiler::CodeAssemblerState* state) {
typedef compiler::Node Node;
typedef LoadWithVectorDescriptor Descriptor;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* context = assembler.Parameter(Descriptor::kContext);
CodeStubAssembler::LoadICParameters p(context, receiver, name, slot, vector);
assembler.KeyedLoadICGeneric(&p);
AccessorAssembler::GenerateKeyedLoadICMegamorphic(state);
}
void Builtins::Generate_KeyedLoadIC_Miss(MacroAssembler* masm) {
......
......@@ -5,8 +5,6 @@
#include "src/code-factory.h"
#include "src/frames-inl.h"
#include "src/frames.h"
#include "src/ic/handler-configuration.h"
#include "src/ic/stub-cache.h"
namespace v8 {
namespace internal {
......@@ -4024,6 +4022,8 @@ Node* CodeStubAssembler::EntryToIndex(Node* entry, int field_index) {
template Node* CodeStubAssembler::EntryToIndex<NameDictionary>(Node*, int);
template Node* CodeStubAssembler::EntryToIndex<GlobalDictionary>(Node*, int);
template Node* CodeStubAssembler::EntryToIndex<SeededNumberDictionary>(Node*,
int);
Node* CodeStubAssembler::HashTableComputeCapacity(Node* at_least_space_for) {
Node* capacity = IntPtrRoundUpToPowerOfTwo32(
......@@ -5006,242 +5006,6 @@ compiler::Node* CodeStubAssembler::LoadReceiverMap(compiler::Node* receiver) {
return var_receiver_map.value();
}
compiler::Node* CodeStubAssembler::TryMonomorphicCase(
compiler::Node* slot, compiler::Node* vector, compiler::Node* receiver_map,
Label* if_handler, Variable* 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.
int32_t header_size = FixedArray::kHeaderSize - 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, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS);
Node* feedback = 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 cell in feedback. We do know it's safe to look
// at WeakCell::kValueOffset.
GotoIf(WordNotEqual(receiver_map, LoadWeakCellValueUnchecked(feedback)),
if_miss);
Node* handler =
Load(MachineType::AnyTagged(), vector,
IntPtrAdd(offset, IntPtrConstant(header_size + kPointerSize)));
var_handler->Bind(handler);
Goto(if_handler);
return feedback;
}
void CodeStubAssembler::HandlePolymorphicCase(
compiler::Node* receiver_map, compiler::Node* feedback, Label* if_handler,
Variable* var_handler, Label* if_miss, int unroll_count) {
Comment("HandlePolymorphicCase");
DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
// Iterate {feedback} array.
const int kEntrySize = 2;
for (int i = 0; i < unroll_count; i++) {
Label next_entry(this);
Node* cached_map = LoadWeakCellValue(LoadFixedArrayElement(
feedback, IntPtrConstant(i * kEntrySize), 0, INTPTR_PARAMETERS));
GotoIf(WordNotEqual(receiver_map, cached_map), &next_entry);
// Found, now call handler.
Node* handler = LoadFixedArrayElement(
feedback, IntPtrConstant(i * kEntrySize + 1), 0, INTPTR_PARAMETERS);
var_handler->Bind(handler);
Goto(if_handler);
Bind(&next_entry);
}
// Loop from {unroll_count}*kEntrySize to {length}.
Node* init = IntPtrConstant(unroll_count * kEntrySize);
Node* length = LoadAndUntagFixedArrayBaseLength(feedback);
BuildFastLoop(
MachineType::PointerRepresentation(), init, length,
[receiver_map, feedback, if_handler, var_handler](CodeStubAssembler* csa,
Node* index) {
Node* cached_map = csa->LoadWeakCellValue(
csa->LoadFixedArrayElement(feedback, index, 0, INTPTR_PARAMETERS));
Label next_entry(csa);
csa->GotoIf(csa->WordNotEqual(receiver_map, cached_map), &next_entry);
// Found, now call handler.
Node* handler = csa->LoadFixedArrayElement(
feedback, index, kPointerSize, INTPTR_PARAMETERS);
var_handler->Bind(handler);
csa->Goto(if_handler);
csa->Bind(&next_entry);
},
kEntrySize, IndexAdvanceMode::kPost);
// The loop falls through if no handler was found.
Goto(if_miss);
}
void CodeStubAssembler::HandleKeyedStorePolymorphicCase(
compiler::Node* receiver_map, compiler::Node* feedback, Label* if_handler,
Variable* var_handler, Label* if_transition_handler,
Variable* var_transition_map_cell, Label* if_miss) {
DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_transition_map_cell->rep());
const int kEntrySize = 3;
Node* init = IntPtrConstant(0);
Node* length = LoadAndUntagFixedArrayBaseLength(feedback);
BuildFastLoop(
MachineType::PointerRepresentation(), init, length,
[receiver_map, feedback, if_handler, var_handler, if_transition_handler,
var_transition_map_cell](CodeStubAssembler* csa, Node* index) {
Node* cached_map = csa->LoadWeakCellValue(
csa->LoadFixedArrayElement(feedback, index, 0, INTPTR_PARAMETERS));
Label next_entry(csa);
csa->GotoIf(csa->WordNotEqual(receiver_map, cached_map), &next_entry);
Node* maybe_transition_map_cell = csa->LoadFixedArrayElement(
feedback, index, kPointerSize, INTPTR_PARAMETERS);
var_handler->Bind(csa->LoadFixedArrayElement(
feedback, index, 2 * kPointerSize, INTPTR_PARAMETERS));
csa->GotoIf(
csa->WordEqual(maybe_transition_map_cell,
csa->LoadRoot(Heap::kUndefinedValueRootIndex)),
if_handler);
var_transition_map_cell->Bind(maybe_transition_map_cell);
csa->Goto(if_transition_handler);
csa->Bind(&next_entry);
},
kEntrySize, IndexAdvanceMode::kPost);
// The loop falls through if no handler was found.
Goto(if_miss);
}
compiler::Node* CodeStubAssembler::StubCachePrimaryOffset(compiler::Node* name,
compiler::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* hash = Int32Add(hash_field, map);
// Base the offset on a simple combination of name and map.
hash = Word32Xor(hash, Int32Constant(StubCache::kPrimaryMagic));
uint32_t mask = (StubCache::kPrimaryTableSize - 1)
<< StubCache::kCacheIndexShift;
return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
}
compiler::Node* CodeStubAssembler::StubCacheSecondaryOffset(
compiler::Node* name, compiler::Node* seed) {
// See v8::internal::StubCache::SecondaryOffset().
// Use the seed from the primary cache in the secondary cache.
Node* hash = Int32Sub(seed, name);
hash = Int32Add(hash, Int32Constant(StubCache::kSecondaryMagic));
int32_t mask = (StubCache::kSecondaryTableSize - 1)
<< StubCache::kCacheIndexShift;
return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
}
enum CodeStubAssembler::StubCacheTable : int {
kPrimary = static_cast<int>(StubCache::kPrimary),
kSecondary = static_cast<int>(StubCache::kSecondary)
};
void CodeStubAssembler::TryProbeStubCacheTable(
StubCache* stub_cache, StubCacheTable table_id,
compiler::Node* entry_offset, compiler::Node* name, compiler::Node* map,
Label* if_handler, Variable* 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(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());
Node* handler = Load(MachineType::TaggedPointer(), key_base,
IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)));
// We found the handler.
var_handler->Bind(handler);
Goto(if_handler);
}
void CodeStubAssembler::TryProbeStubCache(
StubCache* stub_cache, compiler::Node* receiver, compiler::Node* name,
Label* if_handler, Variable* 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);
}
}
Node* CodeStubAssembler::TryToIntptr(Node* key, Label* miss) {
Variable var_intptr_key(this, MachineType::PointerRepresentation());
Label done(this, &var_intptr_key), key_is_smi(this);
......@@ -5266,1280 +5030,6 @@ Node* CodeStubAssembler::TryToIntptr(Node* key, Label* miss) {
return var_intptr_key.value();
}
void CodeStubAssembler::EmitFastElementsBoundsCheck(Node* object,
Node* elements,
Node* intptr_index,
Node* is_jsarray_condition,
Label* miss) {
Variable var_length(this, 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(LoadJSArrayLength(object)));
Goto(&length_loaded);
}
Bind(&length_loaded);
GotoUnless(UintPtrLessThan(intptr_index, var_length.value()), miss);
}
void CodeStubAssembler::EmitElementLoad(Node* object, Node* elements,
Node* elements_kind, Node* 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) {
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(
IntPtrGreaterThan(elements_kind, IntPtrConstant(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.
FAST_SMI_ELEMENTS, FAST_ELEMENTS,
// Handled by if_fast_holey.
FAST_HOLEY_SMI_ELEMENTS, FAST_HOLEY_ELEMENTS,
// Handled by if_fast_double.
FAST_DOUBLE_ELEMENTS,
// Handled by if_fast_holey_double.
FAST_HOLEY_DOUBLE_ELEMENTS};
Label* labels[] = {// FAST_{SMI,}_ELEMENTS
&if_fast_packed, &if_fast_packed,
// FAST_HOLEY_{SMI,}_ELEMENTS
&if_fast_holey, &if_fast_holey,
// FAST_DOUBLE_ELEMENTS
&if_fast_double,
// FAST_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");
Return(LoadFixedArrayElement(elements, intptr_index, 0, INTPTR_PARAMETERS));
}
Bind(&if_fast_holey);
{
Comment("fast holey elements");
Node* element =
LoadFixedArrayElement(elements, intptr_index, 0, INTPTR_PARAMETERS);
GotoIf(WordEqual(element, TheHoleConstant()), if_hole);
Return(element);
}
Bind(&if_fast_double);
{
Comment("packed double elements");
var_double_value->Bind(LoadFixedDoubleArrayElement(
elements, intptr_index, MachineType::Float64(), 0, INTPTR_PARAMETERS));
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(IntPtrGreaterThanOrEqual(
elements_kind,
IntPtrConstant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
&if_typed_array);
GotoIf(IntPtrEqual(elements_kind, IntPtrConstant(DICTIONARY_ELEMENTS)),
&if_dictionary);
Goto(unimplemented_elements_kind);
}
Bind(&if_dictionary);
{
Comment("dictionary elements");
GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), out_of_bounds);
Variable var_entry(this, MachineType::PointerRepresentation());
Label if_found(this);
NumberDictionaryLookup<SeededNumberDictionary>(
elements, intptr_index, &if_found, &var_entry, if_hole);
Bind(&if_found);
// Check that the value is a data property.
Node* details_index = EntryToIndex<SeededNumberDictionary>(
var_entry.value(), SeededNumberDictionary::kEntryDetailsIndex);
Node* details = SmiToWord32(
LoadFixedArrayElement(elements, details_index, 0, INTPTR_PARAMETERS));
Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
// TODO(jkummerow): Support accessors without missing?
GotoUnless(Word32Equal(kind, Int32Constant(kData)), miss);
// Finally, load the value.
Node* value_index = EntryToIndex<SeededNumberDictionary>(
var_entry.value(), SeededNumberDictionary::kEntryValueIndex);
Return(LoadFixedArrayElement(elements, value_index, 0, INTPTR_PARAMETERS));
}
Bind(&if_typed_array);
{
Comment("typed elements");
// Check if buffer has been neutered.
Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset);
Node* bitfield = LoadObjectField(buffer, JSArrayBuffer::kBitFieldOffset,
MachineType::Uint32());
Node* neutered_bit =
Word32And(bitfield, Int32Constant(JSArrayBuffer::WasNeutered::kMask));
GotoUnless(Word32Equal(neutered_bit, Int32Constant(0)), miss);
// Bounds check.
Node* length =
SmiUntag(LoadObjectField(object, JSTypedArray::kLengthOffset));
GotoUnless(UintPtrLessThan(intptr_index, length), out_of_bounds);
// Backing store = external_pointer + base_pointer.
Node* external_pointer =
LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
MachineType::Pointer());
Node* base_pointer =
LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
Node* backing_store = IntPtrAdd(external_pointer, base_pointer);
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);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
&int32_elements, &float32_elements, &float64_elements};
int32_t elements_kinds[] = {
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_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.
Return(SmiTag(Load(MachineType::Uint8(), backing_store, intptr_index)));
}
Bind(&int8_elements);
{
Comment("INT8_ELEMENTS");
Return(SmiTag(Load(MachineType::Int8(), backing_store, intptr_index)));
}
Bind(&uint16_elements);
{
Comment("UINT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Return(SmiTag(Load(MachineType::Uint16(), backing_store, index)));
}
Bind(&int16_elements);
{
Comment("INT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Return(SmiTag(Load(MachineType::Int16(), backing_store, index)));
}
Bind(&uint32_elements);
{
Comment("UINT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Uint32(), backing_store, index);
Return(ChangeUint32ToTagged(element));
}
Bind(&int32_elements);
{
Comment("INT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Int32(), backing_store, index);
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);
}
}
}
void CodeStubAssembler::HandleLoadICHandlerCase(
const LoadICParameters* p, Node* handler, Label* miss,
ElementSupport support_elements) {
Comment("have_handler");
Variable var_holder(this, MachineRepresentation::kTagged);
var_holder.Bind(p->receiver);
Variable var_smi_handler(this, MachineRepresentation::kTagged);
var_smi_handler.Bind(handler);
Variable* vars[] = {&var_holder, &var_smi_handler};
Label if_smi_handler(this, 2, vars);
Label try_proto_handler(this), call_handler(this);
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(),
miss, support_elements);
}
Bind(&try_proto_handler);
{
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler,
&if_smi_handler, miss);
}
Bind(&call_handler);
{
typedef LoadWithVectorDescriptor Descriptor;
TailCallStub(Descriptor(isolate()), handler, p->context,
Arg(Descriptor::kReceiver, p->receiver),
Arg(Descriptor::kName, p->name),
Arg(Descriptor::kSlot, p->slot),
Arg(Descriptor::kVector, p->vector));
}
}
void CodeStubAssembler::HandleLoadICSmiHandlerCase(
const LoadICParameters* p, Node* holder, Node* smi_handler, Label* miss,
ElementSupport support_elements) {
Variable var_double_value(this, MachineRepresentation::kFloat64);
Label rebox_double(this, &var_double_value);
Node* handler_word = SmiUntag(smi_handler);
Node* handler_kind = DecodeWord<LoadHandler::KindBits>(handler_word);
if (support_elements == kSupportElements) {
Label property(this);
GotoUnless(
WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForElements)),
&property);
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 =
DecodeWord<LoadHandler::ElementsKindBits>(handler_word);
Label if_hole(this), unimplemented_elements_kind(this);
Label* out_of_bounds = miss;
EmitElementLoad(holder, elements, elements_kind, intptr_index,
is_jsarray_condition, &if_hole, &rebox_double,
&var_double_value, &unimplemented_elements_kind,
out_of_bounds, miss);
Bind(&unimplemented_elements_kind);
{
// Smi handlers should only be installed for supported elements kinds.
// Crash if we get here.
DebugBreak();
Goto(miss);
}
Bind(&if_hole);
{
Comment("convert hole");
GotoUnless(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
Node* protector_cell = LoadRoot(Heap::kArrayProtectorRootIndex);
DCHECK(isolate()->heap()->array_protector()->IsPropertyCell());
GotoUnless(
WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
SmiConstant(Smi::FromInt(Isolate::kProtectorValid))),
miss);
Return(UndefinedConstant());
}
Bind(&property);
Comment("property_load");
}
Label constant(this), field(this);
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForFields)),
&field, &constant);
Bind(&field);
{
Comment("field_load");
Node* offset = DecodeWord<LoadHandler::FieldOffsetBits>(handler_word);
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);
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 = LoadProperties(holder);
Node* value = LoadObjectField(properties, offset);
GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
Return(value);
Bind(&is_double);
var_double_value.Bind(LoadHeapNumberValue(value));
Goto(&rebox_double);
}
Bind(&rebox_double);
Return(AllocateHeapNumberWithValue(var_double_value.value()));
}
Bind(&constant);
{
Comment("constant_load");
Node* descriptors = LoadMapDescriptors(LoadMap(holder));
Node* descriptor =
DecodeWord<LoadHandler::DescriptorValueIndexBits>(handler_word);
CSA_ASSERT(this,
UintPtrLessThan(descriptor,
LoadAndUntagFixedArrayBaseLength(descriptors)));
Node* value =
LoadFixedArrayElement(descriptors, descriptor, 0, INTPTR_PARAMETERS);
Label if_accessor_info(this);
GotoIf(IsSetWord<LoadHandler::IsAccessorInfoBits>(handler_word),
&if_accessor_info);
Return(value);
Bind(&if_accessor_info);
Callable callable = CodeFactory::ApiGetter(isolate());
TailCallStub(callable, p->context, p->receiver, holder, value);
}
}
void CodeStubAssembler::HandleLoadICProtoHandler(
const LoadICParameters* p, Node* handler, Variable* var_holder,
Variable* var_smi_handler, Label* if_smi_handler, Label* miss) {
DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep());
// IC dispatchers rely on these assumptions to be held.
STATIC_ASSERT(FixedArray::kLengthOffset == LoadHandler::kHolderCellOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kSmiHandlerIndex),
LoadHandler::kSmiHandlerOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kValidityCellIndex),
LoadHandler::kValidityCellOffset);
// Both FixedArray and Tuple3 handlers have validity cell at the same offset.
Label validity_cell_check_done(this);
Node* validity_cell =
LoadObjectField(handler, LoadHandler::kValidityCellOffset);
GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
&validity_cell_check_done);
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
GotoIf(WordNotEqual(cell_value,
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
miss);
Goto(&validity_cell_check_done);
Bind(&validity_cell_check_done);
Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
CSA_ASSERT(this, TaggedIsSmi(smi_handler));
Node* handler_flags = SmiUntag(smi_handler);
Label check_prototypes(this);
GotoUnless(
IsSetWord<LoadHandler::DoNegativeLookupOnReceiverBits>(handler_flags),
&check_prototypes);
{
// We have a dictionary receiver, do a negative lookup check.
NameDictionaryNegativeLookup(p->receiver, p->name, miss);
Goto(&check_prototypes);
}
Bind(&check_prototypes);
Node* maybe_holder_cell =
LoadObjectField(handler, LoadHandler::kHolderCellOffset);
Label array_handler(this), tuple_handler(this);
Branch(TaggedIsSmi(maybe_holder_cell), &array_handler, &tuple_handler);
Bind(&tuple_handler);
{
Label load_existent(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
// This is a handler for a load of a non-existent value.
Return(UndefinedConstant());
Bind(&load_existent);
Node* holder = LoadWeakCellValue(maybe_holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed
// both the receiver map check and the validity cell check.
CSA_ASSERT(this, WordNotEqual(holder, IntPtrConstant(0)));
var_holder->Bind(holder);
var_smi_handler->Bind(smi_handler);
Goto(if_smi_handler);
}
Bind(&array_handler);
{
typedef LoadICProtoArrayDescriptor Descriptor;
LoadICProtoArrayStub stub(isolate());
Node* target = HeapConstant(stub.GetCode());
TailCallStub(Descriptor(isolate()), target, p->context,
Arg(Descriptor::kReceiver, p->receiver),
Arg(Descriptor::kName, p->name),
Arg(Descriptor::kSlot, p->slot),
Arg(Descriptor::kVector, p->vector),
Arg(Descriptor::kHandler, handler));
}
}
void CodeStubAssembler::LoadICProtoArray(const LoadICParameters* p,
Node* handler) {
Label miss(this);
CSA_ASSERT(this, Word32BinaryNot(TaggedIsSmi(handler)));
CSA_ASSERT(this, IsFixedArrayMap(LoadMap(handler)));
Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
Node* handler_flags = SmiUntag(smi_handler);
Node* handler_length = LoadAndUntagFixedArrayBaseLength(handler);
Node* holder = EmitLoadICProtoArrayCheck(p, handler, handler_length,
handler_flags, &miss);
HandleLoadICSmiHandlerCase(p, holder, smi_handler, &miss, kOnlyProperties);
Bind(&miss);
{
TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
p->slot, p->vector);
}
}
Node* CodeStubAssembler::EmitLoadICProtoArrayCheck(const LoadICParameters* p,
Node* handler,
Node* handler_length,
Node* handler_flags,
Label* miss) {
Variable start_index(this, MachineType::PointerRepresentation());
start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex));
Label can_access(this);
GotoUnless(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags),
&can_access);
{
// Skip this entry of a handler.
start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1));
int offset =
FixedArray::OffsetOfElementAt(LoadHandler::kFirstPrototypeIndex);
Node* expected_native_context =
LoadWeakCellValue(LoadObjectField(handler, offset), miss);
CSA_ASSERT(this, IsNativeContext(expected_native_context));
Node* native_context = LoadNativeContext(p->context);
GotoIf(WordEqual(expected_native_context, native_context), &can_access);
// If the receiver is not a JSGlobalProxy then we miss.
GotoUnless(IsJSGlobalProxy(p->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);
}
Bind(&can_access);
BuildFastLoop(
MachineType::PointerRepresentation(), start_index.value(), handler_length,
[this, p, handler, miss](CodeStubAssembler*, Node* current) {
Node* prototype_cell =
LoadFixedArrayElement(handler, current, 0, INTPTR_PARAMETERS);
CheckPrototype(prototype_cell, p->name, miss);
},
1, IndexAdvanceMode::kPost);
Node* maybe_holder_cell = LoadFixedArrayElement(
handler, IntPtrConstant(LoadHandler::kHolderCellIndex), 0,
INTPTR_PARAMETERS);
Label load_existent(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
// This is a handler for a load of a non-existent value.
Return(UndefinedConstant());
Bind(&load_existent);
Node* holder = LoadWeakCellValue(maybe_holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed
// the receiver map check, the validity cell check and the prototype chain
// check.
CSA_ASSERT(this, WordNotEqual(holder, IntPtrConstant(0)));
return holder;
}
void CodeStubAssembler::CheckPrototype(Node* prototype_cell, Node* name,
Label* miss) {
Node* maybe_prototype = LoadWeakCellValue(prototype_cell, miss);
Label done(this);
Label if_property_cell(this), if_dictionary_object(this);
// |maybe_prototype| is either a PropertyCell or a slow-mode prototype.
Branch(WordEqual(LoadMap(maybe_prototype),
LoadRoot(Heap::kGlobalPropertyCellMapRootIndex)),
&if_property_cell, &if_dictionary_object);
Bind(&if_dictionary_object);
{
CSA_ASSERT(this, IsDictionaryMap(LoadMap(maybe_prototype)));
NameDictionaryNegativeLookup(maybe_prototype, name, miss);
Goto(&done);
}
Bind(&if_property_cell);
{
// Ensure the property cell still contains the hole.
Node* value = LoadObjectField(maybe_prototype, PropertyCell::kValueOffset);
GotoIf(WordNotEqual(value, LoadRoot(Heap::kTheHoleValueRootIndex)), miss);
Goto(&done);
}
Bind(&done);
}
void CodeStubAssembler::NameDictionaryNegativeLookup(Node* object, Node* name,
Label* miss) {
CSA_ASSERT(this, IsDictionaryMap(LoadMap(object)));
Node* properties = LoadProperties(object);
// Ensure the property does not exist in a dictionary-mode object.
Variable var_name_index(this, MachineType::PointerRepresentation());
Label done(this);
NameDictionaryLookup<NameDictionary>(properties, name, miss, &var_name_index,
&done);
Bind(&done);
}
void CodeStubAssembler::LoadIC(const LoadICParameters* p) {
Variable var_handler(this, MachineRepresentation::kTagged);
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* feedback =
TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
&var_handler, &try_polymorphic);
Bind(&if_handler);
{
HandleLoadICHandlerCase(p, var_handler.value(), &miss);
}
Bind(&try_polymorphic);
{
// Check polymorphic case.
Comment("LoadIC_try_polymorphic");
GotoUnless(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
&try_megamorphic);
HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
&miss, 2);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&miss);
TryProbeStubCache(isolate()->load_stub_cache(), p->receiver, p->name,
&if_handler, &var_handler, &miss);
}
Bind(&miss);
{
TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
p->slot, p->vector);
}
}
void CodeStubAssembler::KeyedLoadIC(const LoadICParameters* p) {
Variable var_handler(this, MachineRepresentation::kTagged);
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
try_polymorphic_name(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* feedback =
TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
&var_handler, &try_polymorphic);
Bind(&if_handler);
{
HandleLoadICHandlerCase(p, var_handler.value(), &miss, kSupportElements);
}
Bind(&try_polymorphic);
{
// Check polymorphic case.
Comment("KeyedLoadIC_try_polymorphic");
GotoUnless(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
&try_megamorphic);
HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
&miss, 2);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
Comment("KeyedLoadIC_try_megamorphic");
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&try_polymorphic_name);
// TODO(jkummerow): Inline this? Or some of it?
TailCallStub(CodeFactory::KeyedLoadIC_Megamorphic(isolate()), p->context,
p->receiver, p->name, p->slot, p->vector);
}
Bind(&try_polymorphic_name);
{
// We might have a name in feedback, and a fixed array in the next slot.
Comment("KeyedLoadIC_try_polymorphic_name");
GotoUnless(WordEqual(feedback, p->name), &miss);
// If the name comparison succeeded, we know we have a fixed array with
// at least one map/handler pair.
Node* offset = ElementOffsetFromIndex(
p->slot, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS,
FixedArray::kHeaderSize + kPointerSize - kHeapObjectTag);
Node* array = Load(MachineType::AnyTagged(), p->vector, offset);
HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss,
1);
}
Bind(&miss);
{
Comment("KeyedLoadIC_miss");
TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
p->name, p->slot, p->vector);
}
}
void CodeStubAssembler::KeyedLoadICGeneric(const LoadICParameters* p) {
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_details(this, MachineRepresentation::kWord32);
Variable var_value(this, MachineRepresentation::kTagged);
Label if_index(this), if_unique_name(this), if_element_hole(this),
if_oob(this), slow(this), stub_cache_miss(this),
if_property_dictionary(this), if_found_on_receiver(this);
Node* receiver = p->receiver;
GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
// Receivers requiring non-standard element accesses (interceptors, access
// checks, strings and string wrappers, proxies) are handled in the runtime.
GotoIf(Int32LessThanOrEqual(instance_type,
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
&slow);
Node* key = p->name;
TryToName(key, &if_index, &var_index, &if_unique_name, &slow);
Bind(&if_index);
{
Comment("integer index");
Node* index = var_index.value();
Node* elements = LoadElements(receiver);
Node* elements_kind = LoadMapElementsKind(receiver_map);
Node* is_jsarray_condition =
Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE));
Variable var_double_value(this, 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);
Bind(&rebox_double);
Return(AllocateHeapNumberWithValue(var_double_value.value()));
}
Bind(&if_oob);
{
Comment("out of bounds");
Node* index = var_index.value();
// Negative keys can't take the fast OOB path.
GotoIf(IntPtrLessThan(index, IntPtrConstant(0)), &slow);
// Positive OOB indices are effectively the same as hole loads.
Goto(&if_element_hole);
}
Bind(&if_element_hole);
{
Comment("found the hole");
Label return_undefined(this);
BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, &slow);
Bind(&return_undefined);
Return(UndefinedConstant());
}
Node* properties = nullptr;
Bind(&if_unique_name);
{
Comment("key is unique name");
// Check if the receiver has fast or slow properties.
properties = LoadProperties(receiver);
Node* properties_map = LoadMap(properties);
GotoIf(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
&if_property_dictionary);
// Try looking up the property on the receiver; if unsuccessful, look
// for a handler in the stub cache.
Comment("DescriptorArray lookup");
// Skip linear search if there are too many descriptors.
// TODO(jkummerow): Consider implementing binary search.
// See also TryLookupProperty() which has the same limitation.
const int32_t kMaxLinear = 210;
Label stub_cache(this);
Node* bitfield3 = LoadMapBitField3(receiver_map);
Node* nof =
DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
GotoIf(UintPtrGreaterThan(nof, IntPtrConstant(kMaxLinear)), &stub_cache);
Node* descriptors = LoadMapDescriptors(receiver_map);
Variable var_name_index(this, MachineType::PointerRepresentation());
Label if_descriptor_found(this);
DescriptorLookupLinear(key, descriptors, nof, &if_descriptor_found,
&var_name_index, &stub_cache);
Bind(&if_descriptor_found);
{
LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
var_name_index.value(), &var_details,
&var_value);
Goto(&if_found_on_receiver);
}
Bind(&stub_cache);
{
Comment("stub cache probe for fast property load");
Variable var_handler(this, MachineRepresentation::kTagged);
Label found_handler(this, &var_handler), stub_cache_miss(this);
TryProbeStubCache(isolate()->load_stub_cache(), receiver, key,
&found_handler, &var_handler, &stub_cache_miss);
Bind(&found_handler);
{ HandleLoadICHandlerCase(p, var_handler.value(), &slow); }
Bind(&stub_cache_miss);
{
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).
Variable var_name_index(this, MachineType::PointerRepresentation());
Label dictionary_found(this, &var_name_index);
NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found,
&var_name_index, &slow);
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(&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 CodeStubAssembler::HandleStoreFieldAndReturn(Node* handler_word,
Node* holder,
Representation representation,
Node* value, Node* transition,
Label* miss) {
bool transition_to_field = transition != nullptr;
Node* prepared_value = PrepareValueForWrite(value, representation, miss);
if (transition_to_field) {
Label storage_extended(this);
GotoUnless(IsSetWord<StoreHandler::ExtendStorageBits>(handler_word),
&storage_extended);
Comment("[ Extend storage");
ExtendPropertiesBackingStore(holder);
Comment("] Extend storage");
Goto(&storage_extended);
Bind(&storage_extended);
}
Node* offset = DecodeWord<StoreHandler::FieldOffsetBits>(handler_word);
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(holder, offset, true, representation, prepared_value,
transition_to_field);
if (transition_to_field) {
StoreObjectField(holder, JSObject::kMapOffset, transition);
}
Return(value);
}
Bind(&if_out_of_object);
{
StoreNamedField(holder, offset, false, representation, prepared_value,
transition_to_field);
if (transition_to_field) {
StoreObjectField(holder, JSObject::kMapOffset, transition);
}
Return(value);
}
}
void CodeStubAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
Node* holder, Node* value,
Node* transition,
Label* miss) {
Comment(transition ? "transitioning field store" : "field store");
#ifdef DEBUG
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
if (transition) {
CSA_ASSERT(
this,
WordOr(WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kTransitionToField)),
WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kTransitionToConstant))));
} else {
CSA_ASSERT(this, WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kStoreField)));
}
#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, transition, miss);
}
Bind(&if_double_field);
{
Comment("store double field");
HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(),
value, transition, miss);
}
Bind(&if_heap_object_field);
{
Comment("store heap object field");
// Generate full field type check here and then store value as Tagged.
Node* prepared_value =
PrepareValueForWrite(value, Representation::HeapObject(), miss);
Node* value_index_in_descriptor =
DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
Node* descriptors =
LoadMapDescriptors(transition ? transition : LoadMap(holder));
Node* maybe_field_type = LoadFixedArrayElement(
descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS);
Label do_store(this);
GotoIf(TaggedIsSmi(maybe_field_type), &do_store);
// Check that value type matches the field type.
{
Node* field_type = LoadWeakCellValue(maybe_field_type, miss);
Branch(WordEqual(LoadMap(prepared_value), field_type), &do_store, miss);
}
Bind(&do_store);
HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(),
prepared_value, transition, miss);
}
Bind(&if_smi_field);
{
Comment("store smi field");
HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(),
value, transition, miss);
}
}
void CodeStubAssembler::HandleStoreICHandlerCase(const StoreICParameters* p,
Node* handler, Label* miss) {
Label if_smi_handler(this);
Label try_proto_handler(this), call_handler(this);
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);
{
Node* holder = p->receiver;
Node* handler_word = SmiUntag(handler);
// Handle non-transitioning field stores.
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
}
Bind(&try_proto_handler);
{
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
HandleStoreICProtoHandler(p, handler, miss);
}
// |handler| is a heap object. Must be code, call it.
Bind(&call_handler);
{
StoreWithVectorDescriptor descriptor(isolate());
TailCallStub(descriptor, handler, p->context, p->receiver, p->name,
p->value, p->slot, p->vector);
}
}
void CodeStubAssembler::HandleStoreICProtoHandler(const StoreICParameters* p,
Node* handler, Label* miss) {
// IC dispatchers rely on these assumptions to be held.
STATIC_ASSERT(FixedArray::kLengthOffset ==
StoreHandler::kTransitionCellOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kSmiHandlerIndex),
StoreHandler::kSmiHandlerOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kValidityCellIndex),
StoreHandler::kValidityCellOffset);
// Both FixedArray and Tuple3 handlers have validity cell at the same offset.
Label validity_cell_check_done(this);
Node* validity_cell =
LoadObjectField(handler, StoreHandler::kValidityCellOffset);
GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
&validity_cell_check_done);
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
GotoIf(WordNotEqual(cell_value,
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
miss);
Goto(&validity_cell_check_done);
Bind(&validity_cell_check_done);
Node* smi_handler = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset);
CSA_ASSERT(this, TaggedIsSmi(smi_handler));
Node* maybe_transition_cell =
LoadObjectField(handler, StoreHandler::kTransitionCellOffset);
Label array_handler(this), tuple_handler(this);
Branch(TaggedIsSmi(maybe_transition_cell), &array_handler, &tuple_handler);
Variable var_transition(this, MachineRepresentation::kTagged);
Label if_transition(this), if_transition_to_constant(this);
Bind(&tuple_handler);
{
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
var_transition.Bind(transition);
Goto(&if_transition);
}
Bind(&array_handler);
{
Node* length = SmiUntag(maybe_transition_cell);
BuildFastLoop(MachineType::PointerRepresentation(),
IntPtrConstant(StoreHandler::kFirstPrototypeIndex), length,
[this, p, handler, miss](CodeStubAssembler*, Node* current) {
Node* prototype_cell = LoadFixedArrayElement(
handler, current, 0, INTPTR_PARAMETERS);
CheckPrototype(prototype_cell, p->name, miss);
},
1, IndexAdvanceMode::kPost);
Node* maybe_transition_cell = LoadFixedArrayElement(
handler, IntPtrConstant(StoreHandler::kTransitionCellIndex), 0,
INTPTR_PARAMETERS);
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
var_transition.Bind(transition);
Goto(&if_transition);
}
Bind(&if_transition);
{
Node* holder = p->receiver;
Node* transition = var_transition.value();
Node* handler_word = SmiUntag(smi_handler);
GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(transition)), miss);
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
GotoIf(WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kTransitionToConstant)),
&if_transition_to_constant);
// Handle transitioning field stores.
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition,
miss);
Bind(&if_transition_to_constant);
{
// Check that constant matches value.
Node* value_index_in_descriptor =
DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
Node* descriptors = LoadMapDescriptors(transition);
Node* constant = LoadFixedArrayElement(
descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS);
GotoIf(WordNotEqual(p->value, constant), miss);
StoreObjectField(p->receiver, JSObject::kMapOffset, transition);
Return(p->value);
}
}
}
void CodeStubAssembler::StoreIC(const StoreICParameters* p) {
Variable var_handler(this, MachineRepresentation::kTagged);
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* 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);
}
Bind(&try_polymorphic);
{
// Check polymorphic case.
Comment("StoreIC_try_polymorphic");
GotoUnless(
WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
&try_megamorphic);
HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
&miss, 2);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&miss);
TryProbeStubCache(isolate()->store_stub_cache(), p->receiver, p->name,
&if_handler, &var_handler, &miss);
}
Bind(&miss);
{
TailCallRuntime(Runtime::kStoreIC_Miss, p->context, p->value, p->slot,
p->vector, p->receiver, p->name);
}
}
void CodeStubAssembler::KeyedStoreIC(const StoreICParameters* p,
LanguageMode language_mode) {
Variable var_handler(this, MachineRepresentation::kTagged);
// This is to make |miss| label see the var_handler bound on all paths.
var_handler.Bind(IntPtrConstant(0));
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
try_polymorphic_name(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* 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);
}
Bind(&try_polymorphic);
{
// CheckPolymorphic case.
Comment("KeyedStoreIC_try_polymorphic");
GotoUnless(
WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
&try_megamorphic);
Label if_transition_handler(this);
Variable var_transition_map_cell(this, MachineRepresentation::kTagged);
HandleKeyedStorePolymorphicCase(receiver_map, feedback, &if_handler,
&var_handler, &if_transition_handler,
&var_transition_map_cell, &miss);
Bind(&if_transition_handler);
Comment("KeyedStoreIC_polymorphic_transition");
Node* transition_map =
LoadWeakCellValue(var_transition_map_cell.value(), &miss);
StoreTransitionDescriptor descriptor(isolate());
TailCallStub(descriptor, var_handler.value(), p->context, p->receiver,
p->name, transition_map, p->value, p->slot, p->vector);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
Comment("KeyedStoreIC_try_megamorphic");
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&try_polymorphic_name);
TailCallStub(
CodeFactory::KeyedStoreIC_Megamorphic(isolate(), language_mode),
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");
GotoUnless(WordEqual(feedback, p->name), &miss);
// If the name comparison succeeded, we know we have a FixedArray with
// at least one map/handler pair.
Node* offset = ElementOffsetFromIndex(
p->slot, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS,
FixedArray::kHeaderSize + kPointerSize - kHeapObjectTag);
Node* array = Load(MachineType::AnyTagged(), p->vector, offset);
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 CodeStubAssembler::LoadGlobalIC(const LoadICParameters* p) {
Label try_handler(this), miss(this);
Node* weak_cell =
LoadFixedArrayElement(p->vector, p->slot, 0, SMI_PARAMETERS);
CSA_ASSERT(this, HasInstanceType(weak_cell, WEAK_CELL_TYPE));
// Load value or try handler case if the {weak_cell} is cleared.
Node* property_cell = LoadWeakCellValue(weak_cell, &try_handler);
CSA_ASSERT(this, HasInstanceType(property_cell, PROPERTY_CELL_TYPE));
Node* value = LoadObjectField(property_cell, PropertyCell::kValueOffset);
GotoIf(WordEqual(value, TheHoleConstant()), &miss);
Return(value);
Bind(&try_handler);
{
Node* handler =
LoadFixedArrayElement(p->vector, p->slot, kPointerSize, SMI_PARAMETERS);
GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
&miss);
// In this case {handler} must be a Code object.
CSA_ASSERT(this, HasInstanceType(handler, CODE_TYPE));
LoadWithVectorDescriptor descriptor(isolate());
Node* native_context = LoadNativeContext(p->context);
Node* receiver =
LoadContextElement(native_context, Context::EXTENSION_INDEX);
Node* fake_name = IntPtrConstant(0);
TailCallStub(descriptor, handler, p->context, receiver, fake_name, p->slot,
p->vector);
}
Bind(&miss);
{
TailCallRuntime(Runtime::kLoadGlobalIC_Miss, p->context, p->slot,
p->vector);
}
}
void CodeStubAssembler::ExtendPropertiesBackingStore(compiler::Node* object) {
Node* properties = LoadProperties(object);
Node* length = LoadFixedArrayBaseLength(properties);
......
......@@ -861,33 +861,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
compiler::Node* callable,
compiler::Node* object);
// Load/StoreIC helpers.
struct LoadICParameters {
LoadICParameters(compiler::Node* context, compiler::Node* receiver,
compiler::Node* name, compiler::Node* slot,
compiler::Node* vector)
: context(context),
receiver(receiver),
name(name),
slot(slot),
vector(vector) {}
compiler::Node* context;
compiler::Node* receiver;
compiler::Node* name;
compiler::Node* slot;
compiler::Node* vector;
};
struct StoreICParameters : public LoadICParameters {
StoreICParameters(compiler::Node* context, compiler::Node* receiver,
compiler::Node* name, compiler::Node* value,
compiler::Node* slot, compiler::Node* vector)
: LoadICParameters(context, receiver, name, slot, vector),
value(value) {}
compiler::Node* value;
};
// Load type feedback vector from the stub caller's frame.
compiler::Node* LoadTypeFeedbackVectorForStub();
......@@ -898,43 +871,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
compiler::Node* LoadReceiverMap(compiler::Node* receiver);
// Checks monomorphic case. Returns {feedback} entry of the vector.
compiler::Node* TryMonomorphicCase(compiler::Node* slot,
compiler::Node* vector,
compiler::Node* receiver_map,
Label* if_handler, Variable* var_handler,
Label* if_miss);
void HandlePolymorphicCase(compiler::Node* receiver_map,
compiler::Node* feedback, Label* if_handler,
Variable* var_handler, Label* if_miss,
int unroll_count);
void HandleKeyedStorePolymorphicCase(compiler::Node* receiver_map,
compiler::Node* feedback,
Label* if_handler, Variable* var_handler,
Label* if_transition_handler,
Variable* var_transition_map_cell,
Label* if_miss);
compiler::Node* StubCachePrimaryOffset(compiler::Node* name,
compiler::Node* map);
compiler::Node* StubCacheSecondaryOffset(compiler::Node* name,
compiler::Node* seed);
// This enum is used here as a replacement for StubCache::Table to avoid
// including stub cache header.
enum StubCacheTable : int;
void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id,
compiler::Node* entry_offset,
compiler::Node* name, compiler::Node* map,
Label* if_handler, Variable* var_handler,
Label* if_miss);
void TryProbeStubCache(StubCache* stub_cache, compiler::Node* receiver,
compiler::Node* name, Label* if_handler,
Variable* var_handler, Label* if_miss);
// Extends properties backing store by JSObject::kFieldsAdded elements.
void ExtendPropertiesBackingStore(compiler::Node* object);
......@@ -991,14 +927,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
ElementsKind kind, compiler::Node* length,
ParameterMode mode, Label* bailout);
void LoadIC(const LoadICParameters* p);
void LoadICProtoArray(const LoadICParameters* p, compiler::Node* handler);
void LoadGlobalIC(const LoadICParameters* p);
void KeyedLoadIC(const LoadICParameters* p);
void KeyedLoadICGeneric(const LoadICParameters* p);
void StoreIC(const StoreICParameters* p);
void KeyedStoreIC(const StoreICParameters* p, LanguageMode language_mode);
void TransitionElementsKind(compiler::Node* object, compiler::Node* map,
ElementsKind from_kind, ElementsKind to_kind,
bool is_jsarray, Label* bailout);
......@@ -1124,84 +1052,26 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
int base_size = 0);
protected:
void HandleStoreICHandlerCase(const StoreICParameters* p,
compiler::Node* handler, Label* miss);
private:
friend class CodeStubArguments;
enum ElementSupport { kOnlyProperties, kSupportElements };
void DescriptorLookupLinear(compiler::Node* unique_name,
compiler::Node* descriptors, compiler::Node* nof,
Label* if_found, Variable* var_name_index,
Label* if_not_found);
compiler::Node* CallGetterIfAccessor(compiler::Node* value,
compiler::Node* details,
compiler::Node* context,
compiler::Node* receiver,
Label* if_bailout);
void HandleLoadICHandlerCase(
const LoadICParameters* p, compiler::Node* handler, Label* miss,
ElementSupport support_elements = kOnlyProperties);
void HandleLoadICSmiHandlerCase(const LoadICParameters* p,
compiler::Node* holder,
compiler::Node* smi_handler, Label* miss,
ElementSupport support_elements);
void HandleLoadICProtoHandler(const LoadICParameters* p,
compiler::Node* handler, Variable* var_holder,
Variable* var_smi_handler,
Label* if_smi_handler, Label* miss);
compiler::Node* EmitLoadICProtoArrayCheck(const LoadICParameters* p,
compiler::Node* handler,
compiler::Node* handler_length,
compiler::Node* handler_flags,
Label* miss);
void CheckPrototype(compiler::Node* prototype_cell, compiler::Node* name,
Label* miss);
void NameDictionaryNegativeLookup(compiler::Node* object,
compiler::Node* name, Label* miss);
// If |transition| is nullptr then the normal field store is generated or
// transitioning store otherwise.
void HandleStoreFieldAndReturn(compiler::Node* handler_word,
compiler::Node* holder,
Representation representation,
compiler::Node* value,
compiler::Node* transition, Label* miss);
// If |transition| is nullptr then the normal field store is generated or
// transitioning store otherwise.
void HandleStoreICSmiHandlerCase(compiler::Node* handler_word,
compiler::Node* holder,
compiler::Node* value,
compiler::Node* transition, Label* miss);
void HandleStoreICProtoHandler(const StoreICParameters* p,
compiler::Node* handler, Label* miss);
compiler::Node* TryToIntptr(compiler::Node* key, Label* miss);
void EmitFastElementsBoundsCheck(compiler::Node* object,
compiler::Node* elements,
compiler::Node* intptr_index,
compiler::Node* is_jsarray_condition,
Label* miss);
void EmitElementLoad(compiler::Node* object, compiler::Node* elements,
compiler::Node* elements_kind, compiler::Node* key,
compiler::Node* is_jsarray_condition, Label* if_hole,
Label* rebox_double, Variable* var_double_value,
Label* unimplemented_elements_kind, Label* out_of_bounds,
Label* miss);
void BranchIfPrototypesHaveNoElements(compiler::Node* receiver_map,
Label* definitely_no_elements,
Label* possibly_elements);
private:
friend class CodeStubArguments;
compiler::Node* AllocateRawAligned(compiler::Node* size_in_bytes,
AllocationFlags flags,
compiler::Node* top_address,
......
......@@ -12,6 +12,7 @@
#include "src/code-stub-assembler.h"
#include "src/factory.h"
#include "src/gdb-jit.h"
#include "src/ic/accessor-assembler.h"
#include "src/ic/handler-compiler.h"
#include "src/ic/ic.h"
#include "src/macro-assembler.h"
......@@ -19,6 +20,7 @@
namespace v8 {
namespace internal {
using compiler::CodeAssemblerState;
RUNTIME_FUNCTION(UnexpectedStubMiss) {
FATAL("Unexpected deopt of a stub");
......@@ -430,174 +432,33 @@ Handle<Code> TurboFanCodeStub::GenerateCode() {
return compiler::CodeAssembler::GenerateCode(&state);
}
void LoadICTrampolineStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* context = assembler.Parameter(Descriptor::kContext);
Node* vector = assembler.LoadTypeFeedbackVectorForStub();
CodeStubAssembler::LoadICParameters p(context, receiver, name, slot, vector);
assembler.LoadIC(&p);
}
void LoadICStub::GenerateAssembly(compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* context = assembler.Parameter(Descriptor::kContext);
CodeStubAssembler::LoadICParameters p(context, receiver, name, slot, vector);
assembler.LoadIC(&p);
}
void LoadICProtoArrayStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* handler = assembler.Parameter(Descriptor::kHandler);
Node* context = assembler.Parameter(Descriptor::kContext);
CodeStubAssembler::LoadICParameters p(context, receiver, name, slot, vector);
assembler.LoadICProtoArray(&p, handler);
}
void LoadGlobalICTrampolineStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* context = assembler.Parameter(Descriptor::kContext);
Node* vector = assembler.LoadTypeFeedbackVectorForStub();
CodeStubAssembler::LoadICParameters p(context, nullptr, nullptr, slot,
vector);
assembler.LoadGlobalIC(&p);
}
void LoadGlobalICStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* context = assembler.Parameter(Descriptor::kContext);
CodeStubAssembler::LoadICParameters p(context, nullptr, nullptr, slot,
vector);
assembler.LoadGlobalIC(&p);
}
void KeyedLoadICTrampolineTFStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* context = assembler.Parameter(Descriptor::kContext);
Node* vector = assembler.LoadTypeFeedbackVectorForStub();
CodeStubAssembler::LoadICParameters p(context, receiver, name, slot, vector);
assembler.KeyedLoadIC(&p);
}
void KeyedLoadICTFStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* context = assembler.Parameter(Descriptor::kContext);
CodeStubAssembler::LoadICParameters p(context, receiver, name, slot, vector);
assembler.KeyedLoadIC(&p);
}
void StoreICTrampolineStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* value = assembler.Parameter(Descriptor::kValue);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* context = assembler.Parameter(Descriptor::kContext);
Node* vector = assembler.LoadTypeFeedbackVectorForStub();
CodeStubAssembler::StoreICParameters p(context, receiver, name, value, slot,
vector);
assembler.StoreIC(&p);
}
void StoreICStub::GenerateAssembly(compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
#define ACCESSOR_ASSEMBLER(Name) \
void Name##Stub::GenerateAssembly(CodeAssemblerState* state) const { \
AccessorAssembler::Generate##Name(state); \
}
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* value = assembler.Parameter(Descriptor::kValue);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* context = assembler.Parameter(Descriptor::kContext);
ACCESSOR_ASSEMBLER(LoadIC)
ACCESSOR_ASSEMBLER(LoadICTrampoline)
ACCESSOR_ASSEMBLER(LoadICProtoArray)
ACCESSOR_ASSEMBLER(LoadGlobalIC)
ACCESSOR_ASSEMBLER(LoadGlobalICTrampoline)
ACCESSOR_ASSEMBLER(KeyedLoadICTF)
ACCESSOR_ASSEMBLER(KeyedLoadICTrampolineTF)
ACCESSOR_ASSEMBLER(StoreIC)
ACCESSOR_ASSEMBLER(StoreICTrampoline)
CodeStubAssembler::StoreICParameters p(context, receiver, name, value, slot,
vector);
assembler.StoreIC(&p);
}
#undef ACCESSOR_ASSEMBLER
void KeyedStoreICTrampolineTFStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* value = assembler.Parameter(Descriptor::kValue);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* context = assembler.Parameter(Descriptor::kContext);
Node* vector = assembler.LoadTypeFeedbackVectorForStub();
CodeStubAssembler::StoreICParameters p(context, receiver, name, value, slot,
vector);
assembler.KeyedStoreIC(&p, StoreICState::GetLanguageMode(GetExtraICState()));
CodeAssemblerState* state) const {
LanguageMode language_mode = StoreICState::GetLanguageMode(GetExtraICState());
AccessorAssembler::GenerateKeyedStoreICTrampolineTF(state, language_mode);
}
void KeyedStoreICTFStub::GenerateAssembly(
compiler::CodeAssemblerState* state) const {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* value = assembler.Parameter(Descriptor::kValue);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* context = assembler.Parameter(Descriptor::kContext);
CodeStubAssembler::StoreICParameters p(context, receiver, name, value, slot,
vector);
assembler.KeyedStoreIC(&p, StoreICState::GetLanguageMode(GetExtraICState()));
LanguageMode language_mode = StoreICState::GetLanguageMode(GetExtraICState());
AccessorAssembler::GenerateKeyedStoreICTF(state, language_mode);
}
void StoreMapStub::GenerateAssembly(compiler::CodeAssemblerState* state) const {
......
// 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.
#ifndef V8_SRC_IC_ACCESSOR_ASSEMBLER_IMPL_H_
#define V8_SRC_IC_ACCESSOR_ASSEMBLER_IMPL_H_
#include "src/code-stub-assembler.h"
namespace v8 {
namespace internal {
namespace compiler {
class CodeAssemblerState;
}
using compiler::Node;
#define ACCESSOR_ASSEMBLER_PUBLIC_INTERFACE(V) \
V(LoadIC) \
V(LoadICTrampoline) \
V(LoadICProtoArray) \
V(LoadGlobalIC) \
V(LoadGlobalICTrampoline) \
V(KeyedLoadICTF) \
V(KeyedLoadICTrampolineTF) \
V(KeyedLoadICMegamorphic) \
V(StoreIC) \
V(StoreICTrampoline)
// KeyedStoreIC and KeyedStoreICTrampoline need custom handling because of
// their "language_mode" parameter.
class AccessorAssemblerImpl : public CodeStubAssembler {
public:
explicit AccessorAssemblerImpl(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
#define DECLARE_PUBLIC_METHOD(Name) void Generate##Name();
ACCESSOR_ASSEMBLER_PUBLIC_INTERFACE(DECLARE_PUBLIC_METHOD)
#undef DECLARE_PUBLIC_METHOD
void GenerateKeyedStoreICTF(LanguageMode language_mode);
void GenerateKeyedStoreICTrampolineTF(LanguageMode language_mode);
void TryProbeStubCache(StubCache* stub_cache, Node* receiver, Node* name,
Label* if_handler, Variable* var_handler,
Label* if_miss);
Node* StubCachePrimaryOffsetForTesting(Node* name, Node* map) {
return StubCachePrimaryOffset(name, map);
}
Node* StubCacheSecondaryOffsetForTesting(Node* name, Node* map) {
return StubCacheSecondaryOffset(name, map);
}
protected:
struct LoadICParameters {
LoadICParameters(Node* context, Node* receiver, Node* name, Node* slot,
Node* vector)
: context(context),
receiver(receiver),
name(name),
slot(slot),
vector(vector) {}
Node* context;
Node* receiver;
Node* name;
Node* slot;
Node* vector;
};
struct StoreICParameters : public LoadICParameters {
StoreICParameters(Node* context, Node* receiver, Node* name, Node* value,
Node* slot, Node* vector)
: LoadICParameters(context, receiver, name, slot, vector),
value(value) {}
Node* value;
};
void HandleStoreICHandlerCase(const StoreICParameters* p, Node* handler,
Label* miss);
private:
enum ElementSupport { kOnlyProperties, kSupportElements };
// Stub generation entry points.
void LoadIC(const LoadICParameters* p);
void LoadICProtoArray(const LoadICParameters* p, Node* handler);
void LoadGlobalIC(const LoadICParameters* p);
void KeyedLoadIC(const LoadICParameters* p);
void KeyedLoadICGeneric(const LoadICParameters* p);
void StoreIC(const StoreICParameters* p);
void KeyedStoreIC(const StoreICParameters* p, LanguageMode language_mode);
// IC dispatcher behavior.
// Checks monomorphic case. Returns {feedback} entry of the vector.
Node* TryMonomorphicCase(Node* slot, Node* vector, Node* receiver_map,
Label* if_handler, Variable* var_handler,
Label* if_miss);
void HandlePolymorphicCase(Node* receiver_map, Node* feedback,
Label* if_handler, Variable* var_handler,
Label* if_miss, int unroll_count);
void HandleKeyedStorePolymorphicCase(Node* receiver_map, Node* feedback,
Label* if_handler, Variable* var_handler,
Label* if_transition_handler,
Variable* var_transition_map_cell,
Label* if_miss);
// LoadIC implementation.
void HandleLoadICHandlerCase(
const LoadICParameters* p, Node* handler, Label* miss,
ElementSupport support_elements = kOnlyProperties);
void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder,
Node* smi_handler, Label* miss,
ElementSupport support_elements);
void HandleLoadICProtoHandler(const LoadICParameters* p, Node* handler,
Variable* var_holder, Variable* var_smi_handler,
Label* if_smi_handler, Label* miss);
Node* EmitLoadICProtoArrayCheck(const LoadICParameters* p, Node* handler,
Node* handler_length, Node* handler_flags,
Label* miss);
// StoreIC implementation.
void HandleStoreICProtoHandler(const StoreICParameters* p, Node* handler,
Label* miss);
// If |transition| is nullptr then the normal field store is generated or
// transitioning store otherwise.
void HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder,
Node* value, Node* transition, Label* miss);
// If |transition| is nullptr then the normal field store is generated or
// transitioning store otherwise.
void HandleStoreFieldAndReturn(Node* handler_word, Node* holder,
Representation representation, Node* value,
Node* transition, Label* miss);
// Low-level helpers.
void EmitFastElementsBoundsCheck(Node* object, Node* elements,
Node* intptr_index,
Node* is_jsarray_condition, Label* miss);
void EmitElementLoad(Node* object, Node* elements, Node* elements_kind,
Node* key, Node* is_jsarray_condition, Label* if_hole,
Label* rebox_double, Variable* var_double_value,
Label* unimplemented_elements_kind, Label* out_of_bounds,
Label* miss);
void CheckPrototype(Node* prototype_cell, Node* name, Label* miss);
void NameDictionaryNegativeLookup(Node* object, Node* name, Label* miss);
// Stub cache access helpers.
// This enum is used here as a replacement for StubCache::Table to avoid
// including stub cache header.
enum StubCacheTable : int;
Node* StubCachePrimaryOffset(Node* name, Node* map);
Node* StubCacheSecondaryOffset(Node* name, Node* seed);
void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id,
Node* entry_offset, Node* name, Node* map,
Label* if_handler, Variable* var_handler,
Label* if_miss);
};
} // namespace internal
} // namespace v8
#endif // V8_SRC_IC_ACCESSOR_ASSEMBLER_IMPL_H_
// 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/ic/accessor-assembler-impl.h"
#include "src/code-factory.h"
#include "src/code-stubs.h"
#include "src/ic/handler-configuration.h"
#include "src/ic/stub-cache.h"
namespace v8 {
namespace internal {
using compiler::CodeAssemblerState;
//////////////////// Private helpers.
Node* AccessorAssemblerImpl::TryMonomorphicCase(Node* slot, Node* vector,
Node* receiver_map,
Label* if_handler,
Variable* 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.
int32_t header_size = FixedArray::kHeaderSize - 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, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS);
Node* feedback = 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 cell in feedback. We do know it's safe to look
// at WeakCell::kValueOffset.
GotoIf(WordNotEqual(receiver_map, LoadWeakCellValueUnchecked(feedback)),
if_miss);
Node* handler =
Load(MachineType::AnyTagged(), vector,
IntPtrAdd(offset, IntPtrConstant(header_size + kPointerSize)));
var_handler->Bind(handler);
Goto(if_handler);
return feedback;
}
void AccessorAssemblerImpl::HandlePolymorphicCase(
Node* receiver_map, Node* feedback, Label* if_handler,
Variable* var_handler, Label* if_miss, int unroll_count) {
Comment("HandlePolymorphicCase");
DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
// Iterate {feedback} array.
const int kEntrySize = 2;
for (int i = 0; i < unroll_count; i++) {
Label next_entry(this);
Node* cached_map = LoadWeakCellValue(LoadFixedArrayElement(
feedback, IntPtrConstant(i * kEntrySize), 0, INTPTR_PARAMETERS));
GotoIf(WordNotEqual(receiver_map, cached_map), &next_entry);
// Found, now call handler.
Node* handler = LoadFixedArrayElement(
feedback, IntPtrConstant(i * kEntrySize + 1), 0, INTPTR_PARAMETERS);
var_handler->Bind(handler);
Goto(if_handler);
Bind(&next_entry);
}
// Loop from {unroll_count}*kEntrySize to {length}.
Node* init = IntPtrConstant(unroll_count * kEntrySize);
Node* length = LoadAndUntagFixedArrayBaseLength(feedback);
BuildFastLoop(
MachineType::PointerRepresentation(), init, length,
[receiver_map, feedback, if_handler, var_handler](CodeStubAssembler* csa,
Node* index) {
Node* cached_map = csa->LoadWeakCellValue(
csa->LoadFixedArrayElement(feedback, index, 0, INTPTR_PARAMETERS));
Label next_entry(csa);
csa->GotoIf(csa->WordNotEqual(receiver_map, cached_map), &next_entry);
// Found, now call handler.
Node* handler = csa->LoadFixedArrayElement(
feedback, index, kPointerSize, INTPTR_PARAMETERS);
var_handler->Bind(handler);
csa->Goto(if_handler);
csa->Bind(&next_entry);
},
kEntrySize, IndexAdvanceMode::kPost);
// The loop falls through if no handler was found.
Goto(if_miss);
}
void AccessorAssemblerImpl::HandleKeyedStorePolymorphicCase(
Node* receiver_map, Node* feedback, Label* if_handler,
Variable* var_handler, Label* if_transition_handler,
Variable* var_transition_map_cell, Label* if_miss) {
DCHECK_EQ(MachineRepresentation::kTagged, var_handler->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_transition_map_cell->rep());
const int kEntrySize = 3;
Node* init = IntPtrConstant(0);
Node* length = LoadAndUntagFixedArrayBaseLength(feedback);
BuildFastLoop(
MachineType::PointerRepresentation(), init, length,
[receiver_map, feedback, if_handler, var_handler, if_transition_handler,
var_transition_map_cell](CodeStubAssembler* csa, Node* index) {
Node* cached_map = csa->LoadWeakCellValue(
csa->LoadFixedArrayElement(feedback, index, 0, INTPTR_PARAMETERS));
Label next_entry(csa);
csa->GotoIf(csa->WordNotEqual(receiver_map, cached_map), &next_entry);
Node* maybe_transition_map_cell = csa->LoadFixedArrayElement(
feedback, index, kPointerSize, INTPTR_PARAMETERS);
var_handler->Bind(csa->LoadFixedArrayElement(
feedback, index, 2 * kPointerSize, INTPTR_PARAMETERS));
csa->GotoIf(
csa->WordEqual(maybe_transition_map_cell,
csa->LoadRoot(Heap::kUndefinedValueRootIndex)),
if_handler);
var_transition_map_cell->Bind(maybe_transition_map_cell);
csa->Goto(if_transition_handler);
csa->Bind(&next_entry);
},
kEntrySize, IndexAdvanceMode::kPost);
// The loop falls through if no handler was found.
Goto(if_miss);
}
void AccessorAssemblerImpl::HandleLoadICHandlerCase(
const LoadICParameters* p, Node* handler, Label* miss,
ElementSupport support_elements) {
Comment("have_handler");
Variable var_holder(this, MachineRepresentation::kTagged);
var_holder.Bind(p->receiver);
Variable var_smi_handler(this, MachineRepresentation::kTagged);
var_smi_handler.Bind(handler);
Variable* vars[] = {&var_holder, &var_smi_handler};
Label if_smi_handler(this, 2, vars);
Label try_proto_handler(this), call_handler(this);
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(),
miss, support_elements);
}
Bind(&try_proto_handler);
{
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
HandleLoadICProtoHandler(p, handler, &var_holder, &var_smi_handler,
&if_smi_handler, miss);
}
Bind(&call_handler);
{
typedef LoadWithVectorDescriptor Descriptor;
TailCallStub(Descriptor(isolate()), handler, p->context,
Arg(Descriptor::kReceiver, p->receiver),
Arg(Descriptor::kName, p->name),
Arg(Descriptor::kSlot, p->slot),
Arg(Descriptor::kVector, p->vector));
}
}
void AccessorAssemblerImpl::HandleLoadICSmiHandlerCase(
const LoadICParameters* p, Node* holder, Node* smi_handler, Label* miss,
ElementSupport support_elements) {
Variable var_double_value(this, MachineRepresentation::kFloat64);
Label rebox_double(this, &var_double_value);
Node* handler_word = SmiUntag(smi_handler);
Node* handler_kind = DecodeWord<LoadHandler::KindBits>(handler_word);
if (support_elements == kSupportElements) {
Label property(this);
GotoUnless(
WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForElements)),
&property);
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 =
DecodeWord<LoadHandler::ElementsKindBits>(handler_word);
Label if_hole(this), unimplemented_elements_kind(this);
Label* out_of_bounds = miss;
EmitElementLoad(holder, elements, elements_kind, intptr_index,
is_jsarray_condition, &if_hole, &rebox_double,
&var_double_value, &unimplemented_elements_kind,
out_of_bounds, miss);
Bind(&unimplemented_elements_kind);
{
// Smi handlers should only be installed for supported elements kinds.
// Crash if we get here.
DebugBreak();
Goto(miss);
}
Bind(&if_hole);
{
Comment("convert hole");
GotoUnless(IsSetWord<LoadHandler::ConvertHoleBits>(handler_word), miss);
Node* protector_cell = LoadRoot(Heap::kArrayProtectorRootIndex);
DCHECK(isolate()->heap()->array_protector()->IsPropertyCell());
GotoUnless(
WordEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
SmiConstant(Smi::FromInt(Isolate::kProtectorValid))),
miss);
Return(UndefinedConstant());
}
Bind(&property);
Comment("property_load");
}
Label constant(this), field(this);
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForFields)),
&field, &constant);
Bind(&field);
{
Comment("field_load");
Node* offset = DecodeWord<LoadHandler::FieldOffsetBits>(handler_word);
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);
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 = LoadProperties(holder);
Node* value = LoadObjectField(properties, offset);
GotoIf(IsSetWord<LoadHandler::IsDoubleBits>(handler_word), &is_double);
Return(value);
Bind(&is_double);
var_double_value.Bind(LoadHeapNumberValue(value));
Goto(&rebox_double);
}
Bind(&rebox_double);
Return(AllocateHeapNumberWithValue(var_double_value.value()));
}
Bind(&constant);
{
Comment("constant_load");
Node* descriptors = LoadMapDescriptors(LoadMap(holder));
Node* descriptor =
DecodeWord<LoadHandler::DescriptorValueIndexBits>(handler_word);
CSA_ASSERT(this,
UintPtrLessThan(descriptor,
LoadAndUntagFixedArrayBaseLength(descriptors)));
Node* value =
LoadFixedArrayElement(descriptors, descriptor, 0, INTPTR_PARAMETERS);
Label if_accessor_info(this);
GotoIf(IsSetWord<LoadHandler::IsAccessorInfoBits>(handler_word),
&if_accessor_info);
Return(value);
Bind(&if_accessor_info);
Callable callable = CodeFactory::ApiGetter(isolate());
TailCallStub(callable, p->context, p->receiver, holder, value);
}
}
void AccessorAssemblerImpl::HandleLoadICProtoHandler(
const LoadICParameters* p, Node* handler, Variable* var_holder,
Variable* var_smi_handler, Label* if_smi_handler, Label* miss) {
DCHECK_EQ(MachineRepresentation::kTagged, var_holder->rep());
DCHECK_EQ(MachineRepresentation::kTagged, var_smi_handler->rep());
// IC dispatchers rely on these assumptions to be held.
STATIC_ASSERT(FixedArray::kLengthOffset == LoadHandler::kHolderCellOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kSmiHandlerIndex),
LoadHandler::kSmiHandlerOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(LoadHandler::kValidityCellIndex),
LoadHandler::kValidityCellOffset);
// Both FixedArray and Tuple3 handlers have validity cell at the same offset.
Label validity_cell_check_done(this);
Node* validity_cell =
LoadObjectField(handler, LoadHandler::kValidityCellOffset);
GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
&validity_cell_check_done);
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
GotoIf(WordNotEqual(cell_value,
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
miss);
Goto(&validity_cell_check_done);
Bind(&validity_cell_check_done);
Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
CSA_ASSERT(this, TaggedIsSmi(smi_handler));
Node* handler_flags = SmiUntag(smi_handler);
Label check_prototypes(this);
GotoUnless(
IsSetWord<LoadHandler::DoNegativeLookupOnReceiverBits>(handler_flags),
&check_prototypes);
{
// We have a dictionary receiver, do a negative lookup check.
NameDictionaryNegativeLookup(p->receiver, p->name, miss);
Goto(&check_prototypes);
}
Bind(&check_prototypes);
Node* maybe_holder_cell =
LoadObjectField(handler, LoadHandler::kHolderCellOffset);
Label array_handler(this), tuple_handler(this);
Branch(TaggedIsSmi(maybe_holder_cell), &array_handler, &tuple_handler);
Bind(&tuple_handler);
{
Label load_existent(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
// This is a handler for a load of a non-existent value.
Return(UndefinedConstant());
Bind(&load_existent);
Node* holder = LoadWeakCellValue(maybe_holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed
// both the receiver map check and the validity cell check.
CSA_ASSERT(this, WordNotEqual(holder, IntPtrConstant(0)));
var_holder->Bind(holder);
var_smi_handler->Bind(smi_handler);
Goto(if_smi_handler);
}
Bind(&array_handler);
{
typedef LoadICProtoArrayDescriptor Descriptor;
LoadICProtoArrayStub stub(isolate());
Node* target = HeapConstant(stub.GetCode());
TailCallStub(Descriptor(isolate()), target, p->context,
Arg(Descriptor::kReceiver, p->receiver),
Arg(Descriptor::kName, p->name),
Arg(Descriptor::kSlot, p->slot),
Arg(Descriptor::kVector, p->vector),
Arg(Descriptor::kHandler, handler));
}
}
Node* AccessorAssemblerImpl::EmitLoadICProtoArrayCheck(
const LoadICParameters* p, Node* handler, Node* handler_length,
Node* handler_flags, Label* miss) {
Variable start_index(this, MachineType::PointerRepresentation());
start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex));
Label can_access(this);
GotoUnless(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags),
&can_access);
{
// Skip this entry of a handler.
start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1));
int offset =
FixedArray::OffsetOfElementAt(LoadHandler::kFirstPrototypeIndex);
Node* expected_native_context =
LoadWeakCellValue(LoadObjectField(handler, offset), miss);
CSA_ASSERT(this, IsNativeContext(expected_native_context));
Node* native_context = LoadNativeContext(p->context);
GotoIf(WordEqual(expected_native_context, native_context), &can_access);
// If the receiver is not a JSGlobalProxy then we miss.
GotoUnless(IsJSGlobalProxy(p->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);
}
Bind(&can_access);
BuildFastLoop(
MachineType::PointerRepresentation(), start_index.value(), handler_length,
[this, p, handler, miss](CodeStubAssembler*, Node* current) {
Node* prototype_cell =
LoadFixedArrayElement(handler, current, 0, INTPTR_PARAMETERS);
CheckPrototype(prototype_cell, p->name, miss);
},
1, IndexAdvanceMode::kPost);
Node* maybe_holder_cell = LoadFixedArrayElement(
handler, IntPtrConstant(LoadHandler::kHolderCellIndex), 0,
INTPTR_PARAMETERS);
Label load_existent(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
// This is a handler for a load of a non-existent value.
Return(UndefinedConstant());
Bind(&load_existent);
Node* holder = LoadWeakCellValue(maybe_holder_cell);
// The |holder| is guaranteed to be alive at this point since we passed
// the receiver map check, the validity cell check and the prototype chain
// check.
CSA_ASSERT(this, WordNotEqual(holder, IntPtrConstant(0)));
return holder;
}
void AccessorAssemblerImpl::HandleStoreICHandlerCase(const StoreICParameters* p,
Node* handler,
Label* miss) {
Label if_smi_handler(this);
Label try_proto_handler(this), call_handler(this);
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);
{
Node* holder = p->receiver;
Node* handler_word = SmiUntag(handler);
// Handle non-transitioning field stores.
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
}
Bind(&try_proto_handler);
{
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
HandleStoreICProtoHandler(p, handler, miss);
}
// |handler| is a heap object. Must be code, call it.
Bind(&call_handler);
{
StoreWithVectorDescriptor descriptor(isolate());
TailCallStub(descriptor, handler, p->context, p->receiver, p->name,
p->value, p->slot, p->vector);
}
}
void AccessorAssemblerImpl::HandleStoreICProtoHandler(
const StoreICParameters* p, Node* handler, Label* miss) {
// IC dispatchers rely on these assumptions to be held.
STATIC_ASSERT(FixedArray::kLengthOffset ==
StoreHandler::kTransitionCellOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kSmiHandlerIndex),
StoreHandler::kSmiHandlerOffset);
DCHECK_EQ(FixedArray::OffsetOfElementAt(StoreHandler::kValidityCellIndex),
StoreHandler::kValidityCellOffset);
// Both FixedArray and Tuple3 handlers have validity cell at the same offset.
Label validity_cell_check_done(this);
Node* validity_cell =
LoadObjectField(handler, StoreHandler::kValidityCellOffset);
GotoIf(WordEqual(validity_cell, IntPtrConstant(0)),
&validity_cell_check_done);
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
GotoIf(WordNotEqual(cell_value,
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
miss);
Goto(&validity_cell_check_done);
Bind(&validity_cell_check_done);
Node* smi_handler = LoadObjectField(handler, StoreHandler::kSmiHandlerOffset);
CSA_ASSERT(this, TaggedIsSmi(smi_handler));
Node* maybe_transition_cell =
LoadObjectField(handler, StoreHandler::kTransitionCellOffset);
Label array_handler(this), tuple_handler(this);
Branch(TaggedIsSmi(maybe_transition_cell), &array_handler, &tuple_handler);
Variable var_transition(this, MachineRepresentation::kTagged);
Label if_transition(this), if_transition_to_constant(this);
Bind(&tuple_handler);
{
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
var_transition.Bind(transition);
Goto(&if_transition);
}
Bind(&array_handler);
{
Node* length = SmiUntag(maybe_transition_cell);
BuildFastLoop(MachineType::PointerRepresentation(),
IntPtrConstant(StoreHandler::kFirstPrototypeIndex), length,
[this, p, handler, miss](CodeStubAssembler*, Node* current) {
Node* prototype_cell = LoadFixedArrayElement(
handler, current, 0, INTPTR_PARAMETERS);
CheckPrototype(prototype_cell, p->name, miss);
},
1, IndexAdvanceMode::kPost);
Node* maybe_transition_cell = LoadFixedArrayElement(
handler, IntPtrConstant(StoreHandler::kTransitionCellIndex), 0,
INTPTR_PARAMETERS);
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
var_transition.Bind(transition);
Goto(&if_transition);
}
Bind(&if_transition);
{
Node* holder = p->receiver;
Node* transition = var_transition.value();
Node* handler_word = SmiUntag(smi_handler);
GotoIf(IsSetWord32<Map::Deprecated>(LoadMapBitField3(transition)), miss);
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
GotoIf(WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kTransitionToConstant)),
&if_transition_to_constant);
// Handle transitioning field stores.
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition,
miss);
Bind(&if_transition_to_constant);
{
// Check that constant matches value.
Node* value_index_in_descriptor =
DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
Node* descriptors = LoadMapDescriptors(transition);
Node* constant = LoadFixedArrayElement(
descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS);
GotoIf(WordNotEqual(p->value, constant), miss);
StoreObjectField(p->receiver, JSObject::kMapOffset, transition);
Return(p->value);
}
}
}
void AccessorAssemblerImpl::HandleStoreICSmiHandlerCase(Node* handler_word,
Node* holder,
Node* value,
Node* transition,
Label* miss) {
Comment(transition ? "transitioning field store" : "field store");
#ifdef DEBUG
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
if (transition) {
CSA_ASSERT(
this,
WordOr(WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kTransitionToField)),
WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kTransitionToConstant))));
} else {
CSA_ASSERT(this, WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kStoreField)));
}
#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, transition, miss);
}
Bind(&if_double_field);
{
Comment("store double field");
HandleStoreFieldAndReturn(handler_word, holder, Representation::Double(),
value, transition, miss);
}
Bind(&if_heap_object_field);
{
Comment("store heap object field");
// Generate full field type check here and then store value as Tagged.
Node* prepared_value =
PrepareValueForWrite(value, Representation::HeapObject(), miss);
Node* value_index_in_descriptor =
DecodeWord<StoreHandler::DescriptorValueIndexBits>(handler_word);
Node* descriptors =
LoadMapDescriptors(transition ? transition : LoadMap(holder));
Node* maybe_field_type = LoadFixedArrayElement(
descriptors, value_index_in_descriptor, 0, INTPTR_PARAMETERS);
Label do_store(this);
GotoIf(TaggedIsSmi(maybe_field_type), &do_store);
// Check that value type matches the field type.
{
Node* field_type = LoadWeakCellValue(maybe_field_type, miss);
Branch(WordEqual(LoadMap(prepared_value), field_type), &do_store, miss);
}
Bind(&do_store);
HandleStoreFieldAndReturn(handler_word, holder, Representation::Tagged(),
prepared_value, transition, miss);
}
Bind(&if_smi_field);
{
Comment("store smi field");
HandleStoreFieldAndReturn(handler_word, holder, Representation::Smi(),
value, transition, miss);
}
}
void AccessorAssemblerImpl::HandleStoreFieldAndReturn(
Node* handler_word, Node* holder, Representation representation,
Node* value, Node* transition, Label* miss) {
bool transition_to_field = transition != nullptr;
Node* prepared_value = PrepareValueForWrite(value, representation, miss);
if (transition_to_field) {
Label storage_extended(this);
GotoUnless(IsSetWord<StoreHandler::ExtendStorageBits>(handler_word),
&storage_extended);
Comment("[ Extend storage");
ExtendPropertiesBackingStore(holder);
Comment("] Extend storage");
Goto(&storage_extended);
Bind(&storage_extended);
}
Node* offset = DecodeWord<StoreHandler::FieldOffsetBits>(handler_word);
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(holder, offset, true, representation, prepared_value,
transition_to_field);
if (transition_to_field) {
StoreObjectField(holder, JSObject::kMapOffset, transition);
}
Return(value);
}
Bind(&if_out_of_object);
{
StoreNamedField(holder, offset, false, representation, prepared_value,
transition_to_field);
if (transition_to_field) {
StoreObjectField(holder, JSObject::kMapOffset, transition);
}
Return(value);
}
}
void AccessorAssemblerImpl::EmitFastElementsBoundsCheck(
Node* object, Node* elements, Node* intptr_index,
Node* is_jsarray_condition, Label* miss) {
Variable var_length(this, 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(LoadJSArrayLength(object)));
Goto(&length_loaded);
}
Bind(&length_loaded);
GotoUnless(UintPtrLessThan(intptr_index, var_length.value()), miss);
}
void AccessorAssemblerImpl::EmitElementLoad(
Node* object, Node* elements, Node* elements_kind, Node* 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) {
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(
IntPtrGreaterThan(elements_kind, IntPtrConstant(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.
FAST_SMI_ELEMENTS, FAST_ELEMENTS,
// Handled by if_fast_holey.
FAST_HOLEY_SMI_ELEMENTS, FAST_HOLEY_ELEMENTS,
// Handled by if_fast_double.
FAST_DOUBLE_ELEMENTS,
// Handled by if_fast_holey_double.
FAST_HOLEY_DOUBLE_ELEMENTS};
Label* labels[] = {// FAST_{SMI,}_ELEMENTS
&if_fast_packed, &if_fast_packed,
// FAST_HOLEY_{SMI,}_ELEMENTS
&if_fast_holey, &if_fast_holey,
// FAST_DOUBLE_ELEMENTS
&if_fast_double,
// FAST_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");
Return(LoadFixedArrayElement(elements, intptr_index, 0, INTPTR_PARAMETERS));
}
Bind(&if_fast_holey);
{
Comment("fast holey elements");
Node* element =
LoadFixedArrayElement(elements, intptr_index, 0, INTPTR_PARAMETERS);
GotoIf(WordEqual(element, TheHoleConstant()), if_hole);
Return(element);
}
Bind(&if_fast_double);
{
Comment("packed double elements");
var_double_value->Bind(LoadFixedDoubleArrayElement(
elements, intptr_index, MachineType::Float64(), 0, INTPTR_PARAMETERS));
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(IntPtrGreaterThanOrEqual(
elements_kind,
IntPtrConstant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)),
&if_typed_array);
GotoIf(IntPtrEqual(elements_kind, IntPtrConstant(DICTIONARY_ELEMENTS)),
&if_dictionary);
Goto(unimplemented_elements_kind);
}
Bind(&if_dictionary);
{
Comment("dictionary elements");
GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), out_of_bounds);
Variable var_entry(this, MachineType::PointerRepresentation());
Label if_found(this);
NumberDictionaryLookup<SeededNumberDictionary>(
elements, intptr_index, &if_found, &var_entry, if_hole);
Bind(&if_found);
// Check that the value is a data property.
Node* details_index = EntryToIndex<SeededNumberDictionary>(
var_entry.value(), SeededNumberDictionary::kEntryDetailsIndex);
Node* details = SmiToWord32(
LoadFixedArrayElement(elements, details_index, 0, INTPTR_PARAMETERS));
Node* kind = DecodeWord32<PropertyDetails::KindField>(details);
// TODO(jkummerow): Support accessors without missing?
GotoUnless(Word32Equal(kind, Int32Constant(kData)), miss);
// Finally, load the value.
Node* value_index = EntryToIndex<SeededNumberDictionary>(
var_entry.value(), SeededNumberDictionary::kEntryValueIndex);
Return(LoadFixedArrayElement(elements, value_index, 0, INTPTR_PARAMETERS));
}
Bind(&if_typed_array);
{
Comment("typed elements");
// Check if buffer has been neutered.
Node* buffer = LoadObjectField(object, JSArrayBufferView::kBufferOffset);
Node* bitfield = LoadObjectField(buffer, JSArrayBuffer::kBitFieldOffset,
MachineType::Uint32());
Node* neutered_bit =
Word32And(bitfield, Int32Constant(JSArrayBuffer::WasNeutered::kMask));
GotoUnless(Word32Equal(neutered_bit, Int32Constant(0)), miss);
// Bounds check.
Node* length =
SmiUntag(LoadObjectField(object, JSTypedArray::kLengthOffset));
GotoUnless(UintPtrLessThan(intptr_index, length), out_of_bounds);
// Backing store = external_pointer + base_pointer.
Node* external_pointer =
LoadObjectField(elements, FixedTypedArrayBase::kExternalPointerOffset,
MachineType::Pointer());
Node* base_pointer =
LoadObjectField(elements, FixedTypedArrayBase::kBasePointerOffset);
Node* backing_store = IntPtrAdd(external_pointer, base_pointer);
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);
Label* elements_kind_labels[] = {
&uint8_elements, &uint8_elements, &int8_elements,
&uint16_elements, &int16_elements, &uint32_elements,
&int32_elements, &float32_elements, &float64_elements};
int32_t elements_kinds[] = {
UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS, INT8_ELEMENTS,
UINT16_ELEMENTS, INT16_ELEMENTS, UINT32_ELEMENTS,
INT32_ELEMENTS, FLOAT32_ELEMENTS, FLOAT64_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.
Return(SmiTag(Load(MachineType::Uint8(), backing_store, intptr_index)));
}
Bind(&int8_elements);
{
Comment("INT8_ELEMENTS");
Return(SmiTag(Load(MachineType::Int8(), backing_store, intptr_index)));
}
Bind(&uint16_elements);
{
Comment("UINT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Return(SmiTag(Load(MachineType::Uint16(), backing_store, index)));
}
Bind(&int16_elements);
{
Comment("INT16_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(1));
Return(SmiTag(Load(MachineType::Int16(), backing_store, index)));
}
Bind(&uint32_elements);
{
Comment("UINT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Uint32(), backing_store, index);
Return(ChangeUint32ToTagged(element));
}
Bind(&int32_elements);
{
Comment("INT32_ELEMENTS");
Node* index = WordShl(intptr_index, IntPtrConstant(2));
Node* element = Load(MachineType::Int32(), backing_store, index);
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);
}
}
}
void AccessorAssemblerImpl::CheckPrototype(Node* prototype_cell, Node* name,
Label* miss) {
Node* maybe_prototype = LoadWeakCellValue(prototype_cell, miss);
Label done(this);
Label if_property_cell(this), if_dictionary_object(this);
// |maybe_prototype| is either a PropertyCell or a slow-mode prototype.
Branch(WordEqual(LoadMap(maybe_prototype),
LoadRoot(Heap::kGlobalPropertyCellMapRootIndex)),
&if_property_cell, &if_dictionary_object);
Bind(&if_dictionary_object);
{
CSA_ASSERT(this, IsDictionaryMap(LoadMap(maybe_prototype)));
NameDictionaryNegativeLookup(maybe_prototype, name, miss);
Goto(&done);
}
Bind(&if_property_cell);
{
// Ensure the property cell still contains the hole.
Node* value = LoadObjectField(maybe_prototype, PropertyCell::kValueOffset);
GotoIf(WordNotEqual(value, LoadRoot(Heap::kTheHoleValueRootIndex)), miss);
Goto(&done);
}
Bind(&done);
}
void AccessorAssemblerImpl::NameDictionaryNegativeLookup(Node* object,
Node* name,
Label* miss) {
CSA_ASSERT(this, IsDictionaryMap(LoadMap(object)));
Node* properties = LoadProperties(object);
// Ensure the property does not exist in a dictionary-mode object.
Variable var_name_index(this, MachineType::PointerRepresentation());
Label done(this);
NameDictionaryLookup<NameDictionary>(properties, name, miss, &var_name_index,
&done);
Bind(&done);
}
//////////////////// Stub cache access helpers.
enum AccessorAssemblerImpl::StubCacheTable : int {
kPrimary = static_cast<int>(StubCache::kPrimary),
kSecondary = static_cast<int>(StubCache::kSecondary)
};
Node* AccessorAssemblerImpl::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* hash = Int32Add(hash_field, map);
// Base the offset on a simple combination of name and map.
hash = Word32Xor(hash, Int32Constant(StubCache::kPrimaryMagic));
uint32_t mask = (StubCache::kPrimaryTableSize - 1)
<< StubCache::kCacheIndexShift;
return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
}
Node* AccessorAssemblerImpl::StubCacheSecondaryOffset(Node* name, Node* seed) {
// See v8::internal::StubCache::SecondaryOffset().
// Use the seed from the primary cache in the secondary cache.
Node* hash = Int32Sub(seed, name);
hash = Int32Add(hash, Int32Constant(StubCache::kSecondaryMagic));
int32_t mask = (StubCache::kSecondaryTableSize - 1)
<< StubCache::kCacheIndexShift;
return ChangeUint32ToWord(Word32And(hash, Int32Constant(mask)));
}
void AccessorAssemblerImpl::TryProbeStubCacheTable(
StubCache* stub_cache, StubCacheTable table_id, Node* entry_offset,
Node* name, Node* map, Label* if_handler, Variable* 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(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());
Node* handler = Load(MachineType::TaggedPointer(), key_base,
IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)));
// We found the handler.
var_handler->Bind(handler);
Goto(if_handler);
}
void AccessorAssemblerImpl::TryProbeStubCache(StubCache* stub_cache,
Node* receiver, Node* name,
Label* if_handler,
Variable* 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 AccessorAssemblerImpl::LoadIC(const LoadICParameters* p) {
Variable var_handler(this, MachineRepresentation::kTagged);
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* feedback =
TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
&var_handler, &try_polymorphic);
Bind(&if_handler);
{ HandleLoadICHandlerCase(p, var_handler.value(), &miss); }
Bind(&try_polymorphic);
{
// Check polymorphic case.
Comment("LoadIC_try_polymorphic");
GotoUnless(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
&try_megamorphic);
HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
&miss, 2);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&miss);
TryProbeStubCache(isolate()->load_stub_cache(), p->receiver, p->name,
&if_handler, &var_handler, &miss);
}
Bind(&miss);
{
TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
p->slot, p->vector);
}
}
void AccessorAssemblerImpl::LoadICProtoArray(const LoadICParameters* p,
Node* handler) {
Label miss(this);
CSA_ASSERT(this, Word32BinaryNot(TaggedIsSmi(handler)));
CSA_ASSERT(this, IsFixedArrayMap(LoadMap(handler)));
Node* smi_handler = LoadObjectField(handler, LoadHandler::kSmiHandlerOffset);
Node* handler_flags = SmiUntag(smi_handler);
Node* handler_length = LoadAndUntagFixedArrayBaseLength(handler);
Node* holder = EmitLoadICProtoArrayCheck(p, handler, handler_length,
handler_flags, &miss);
HandleLoadICSmiHandlerCase(p, holder, smi_handler, &miss, kOnlyProperties);
Bind(&miss);
{
TailCallRuntime(Runtime::kLoadIC_Miss, p->context, p->receiver, p->name,
p->slot, p->vector);
}
}
void AccessorAssemblerImpl::LoadGlobalIC(const LoadICParameters* p) {
Label try_handler(this), miss(this);
Node* weak_cell =
LoadFixedArrayElement(p->vector, p->slot, 0, SMI_PARAMETERS);
CSA_ASSERT(this, HasInstanceType(weak_cell, WEAK_CELL_TYPE));
// Load value or try handler case if the {weak_cell} is cleared.
Node* property_cell = LoadWeakCellValue(weak_cell, &try_handler);
CSA_ASSERT(this, HasInstanceType(property_cell, PROPERTY_CELL_TYPE));
Node* value = LoadObjectField(property_cell, PropertyCell::kValueOffset);
GotoIf(WordEqual(value, TheHoleConstant()), &miss);
Return(value);
Bind(&try_handler);
{
Node* handler =
LoadFixedArrayElement(p->vector, p->slot, kPointerSize, SMI_PARAMETERS);
GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
&miss);
// In this case {handler} must be a Code object.
CSA_ASSERT(this, HasInstanceType(handler, CODE_TYPE));
LoadWithVectorDescriptor descriptor(isolate());
Node* native_context = LoadNativeContext(p->context);
Node* receiver =
LoadContextElement(native_context, Context::EXTENSION_INDEX);
Node* fake_name = IntPtrConstant(0);
TailCallStub(descriptor, handler, p->context, receiver, fake_name, p->slot,
p->vector);
}
Bind(&miss);
{
TailCallRuntime(Runtime::kLoadGlobalIC_Miss, p->context, p->slot,
p->vector);
}
}
void AccessorAssemblerImpl::KeyedLoadIC(const LoadICParameters* p) {
Variable var_handler(this, MachineRepresentation::kTagged);
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
try_polymorphic_name(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* feedback =
TryMonomorphicCase(p->slot, p->vector, receiver_map, &if_handler,
&var_handler, &try_polymorphic);
Bind(&if_handler);
{ HandleLoadICHandlerCase(p, var_handler.value(), &miss, kSupportElements); }
Bind(&try_polymorphic);
{
// Check polymorphic case.
Comment("KeyedLoadIC_try_polymorphic");
GotoUnless(WordEqual(LoadMap(feedback), FixedArrayMapConstant()),
&try_megamorphic);
HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
&miss, 2);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
Comment("KeyedLoadIC_try_megamorphic");
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&try_polymorphic_name);
// TODO(jkummerow): Inline this? Or some of it?
TailCallStub(CodeFactory::KeyedLoadIC_Megamorphic(isolate()), p->context,
p->receiver, p->name, p->slot, p->vector);
}
Bind(&try_polymorphic_name);
{
// We might have a name in feedback, and a fixed array in the next slot.
Comment("KeyedLoadIC_try_polymorphic_name");
GotoUnless(WordEqual(feedback, p->name), &miss);
// If the name comparison succeeded, we know we have a fixed array with
// at least one map/handler pair.
Node* offset = ElementOffsetFromIndex(
p->slot, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS,
FixedArray::kHeaderSize + kPointerSize - kHeapObjectTag);
Node* array = Load(MachineType::AnyTagged(), p->vector, offset);
HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler, &miss,
1);
}
Bind(&miss);
{
Comment("KeyedLoadIC_miss");
TailCallRuntime(Runtime::kKeyedLoadIC_Miss, p->context, p->receiver,
p->name, p->slot, p->vector);
}
}
void AccessorAssemblerImpl::KeyedLoadICGeneric(const LoadICParameters* p) {
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_details(this, MachineRepresentation::kWord32);
Variable var_value(this, MachineRepresentation::kTagged);
Label if_index(this), if_unique_name(this), if_element_hole(this),
if_oob(this), slow(this), stub_cache_miss(this),
if_property_dictionary(this), if_found_on_receiver(this);
Node* receiver = p->receiver;
GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
// Receivers requiring non-standard element accesses (interceptors, access
// checks, strings and string wrappers, proxies) are handled in the runtime.
GotoIf(Int32LessThanOrEqual(instance_type,
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
&slow);
Node* key = p->name;
TryToName(key, &if_index, &var_index, &if_unique_name, &slow);
Bind(&if_index);
{
Comment("integer index");
Node* index = var_index.value();
Node* elements = LoadElements(receiver);
Node* elements_kind = LoadMapElementsKind(receiver_map);
Node* is_jsarray_condition =
Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE));
Variable var_double_value(this, 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);
Bind(&rebox_double);
Return(AllocateHeapNumberWithValue(var_double_value.value()));
}
Bind(&if_oob);
{
Comment("out of bounds");
Node* index = var_index.value();
// Negative keys can't take the fast OOB path.
GotoIf(IntPtrLessThan(index, IntPtrConstant(0)), &slow);
// Positive OOB indices are effectively the same as hole loads.
Goto(&if_element_hole);
}
Bind(&if_element_hole);
{
Comment("found the hole");
Label return_undefined(this);
BranchIfPrototypesHaveNoElements(receiver_map, &return_undefined, &slow);
Bind(&return_undefined);
Return(UndefinedConstant());
}
Node* properties = nullptr;
Bind(&if_unique_name);
{
Comment("key is unique name");
// Check if the receiver has fast or slow properties.
properties = LoadProperties(receiver);
Node* properties_map = LoadMap(properties);
GotoIf(WordEqual(properties_map, LoadRoot(Heap::kHashTableMapRootIndex)),
&if_property_dictionary);
// Try looking up the property on the receiver; if unsuccessful, look
// for a handler in the stub cache.
Comment("DescriptorArray lookup");
// Skip linear search if there are too many descriptors.
// TODO(jkummerow): Consider implementing binary search.
// See also TryLookupProperty() which has the same limitation.
const int32_t kMaxLinear = 210;
Label stub_cache(this);
Node* bitfield3 = LoadMapBitField3(receiver_map);
Node* nof =
DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
GotoIf(UintPtrGreaterThan(nof, IntPtrConstant(kMaxLinear)), &stub_cache);
Node* descriptors = LoadMapDescriptors(receiver_map);
Variable var_name_index(this, MachineType::PointerRepresentation());
Label if_descriptor_found(this);
DescriptorLookupLinear(key, descriptors, nof, &if_descriptor_found,
&var_name_index, &stub_cache);
Bind(&if_descriptor_found);
{
LoadPropertyFromFastObject(receiver, receiver_map, descriptors,
var_name_index.value(), &var_details,
&var_value);
Goto(&if_found_on_receiver);
}
Bind(&stub_cache);
{
Comment("stub cache probe for fast property load");
Variable var_handler(this, MachineRepresentation::kTagged);
Label found_handler(this, &var_handler), stub_cache_miss(this);
TryProbeStubCache(isolate()->load_stub_cache(), receiver, key,
&found_handler, &var_handler, &stub_cache_miss);
Bind(&found_handler);
{ HandleLoadICHandlerCase(p, var_handler.value(), &slow); }
Bind(&stub_cache_miss);
{
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).
Variable var_name_index(this, MachineType::PointerRepresentation());
Label dictionary_found(this, &var_name_index);
NameDictionaryLookup<NameDictionary>(properties, key, &dictionary_found,
&var_name_index, &slow);
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(&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 AccessorAssemblerImpl::StoreIC(const StoreICParameters* p) {
Variable var_handler(this, MachineRepresentation::kTagged);
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* 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);
}
Bind(&try_polymorphic);
{
// Check polymorphic case.
Comment("StoreIC_try_polymorphic");
GotoUnless(
WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
&try_megamorphic);
HandlePolymorphicCase(receiver_map, feedback, &if_handler, &var_handler,
&miss, 2);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&miss);
TryProbeStubCache(isolate()->store_stub_cache(), p->receiver, p->name,
&if_handler, &var_handler, &miss);
}
Bind(&miss);
{
TailCallRuntime(Runtime::kStoreIC_Miss, p->context, p->value, p->slot,
p->vector, p->receiver, p->name);
}
}
void AccessorAssemblerImpl::KeyedStoreIC(const StoreICParameters* p,
LanguageMode language_mode) {
Variable var_handler(this, MachineRepresentation::kTagged);
// This is to make |miss| label see the var_handler bound on all paths.
var_handler.Bind(IntPtrConstant(0));
// TODO(ishell): defer blocks when it works.
Label if_handler(this, &var_handler), try_polymorphic(this),
try_megamorphic(this /*, Label::kDeferred*/),
try_polymorphic_name(this /*, Label::kDeferred*/),
miss(this /*, Label::kDeferred*/);
Node* receiver_map = LoadReceiverMap(p->receiver);
// Check monomorphic case.
Node* 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);
}
Bind(&try_polymorphic);
{
// CheckPolymorphic case.
Comment("KeyedStoreIC_try_polymorphic");
GotoUnless(
WordEqual(LoadMap(feedback), LoadRoot(Heap::kFixedArrayMapRootIndex)),
&try_megamorphic);
Label if_transition_handler(this);
Variable var_transition_map_cell(this, MachineRepresentation::kTagged);
HandleKeyedStorePolymorphicCase(receiver_map, feedback, &if_handler,
&var_handler, &if_transition_handler,
&var_transition_map_cell, &miss);
Bind(&if_transition_handler);
Comment("KeyedStoreIC_polymorphic_transition");
Node* transition_map =
LoadWeakCellValue(var_transition_map_cell.value(), &miss);
StoreTransitionDescriptor descriptor(isolate());
TailCallStub(descriptor, var_handler.value(), p->context, p->receiver,
p->name, transition_map, p->value, p->slot, p->vector);
}
Bind(&try_megamorphic);
{
// Check megamorphic case.
Comment("KeyedStoreIC_try_megamorphic");
GotoUnless(
WordEqual(feedback, LoadRoot(Heap::kmegamorphic_symbolRootIndex)),
&try_polymorphic_name);
TailCallStub(
CodeFactory::KeyedStoreIC_Megamorphic(isolate(), language_mode),
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");
GotoUnless(WordEqual(feedback, p->name), &miss);
// If the name comparison succeeded, we know we have a FixedArray with
// at least one map/handler pair.
Node* offset = ElementOffsetFromIndex(
p->slot, FAST_HOLEY_ELEMENTS, SMI_PARAMETERS,
FixedArray::kHeaderSize + kPointerSize - kHeapObjectTag);
Node* array = Load(MachineType::AnyTagged(), p->vector, offset);
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);
}
}
//////////////////// Public methods.
void AccessorAssemblerImpl::GenerateLoadIC() {
typedef LoadICStub::Descriptor 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 AccessorAssemblerImpl::GenerateLoadICTrampoline() {
typedef LoadICTrampolineStub::Descriptor Descriptor;
Node* receiver = Parameter(Descriptor::kReceiver);
Node* name = Parameter(Descriptor::kName);
Node* slot = Parameter(Descriptor::kSlot);
Node* context = Parameter(Descriptor::kContext);
Node* vector = LoadTypeFeedbackVectorForStub();
LoadICParameters p(context, receiver, name, slot, vector);
LoadIC(&p);
}
void AccessorAssemblerImpl::GenerateLoadICProtoArray() {
typedef LoadICProtoArrayStub::Descriptor Descriptor;
Node* receiver = Parameter(Descriptor::kReceiver);
Node* name = Parameter(Descriptor::kName);
Node* slot = Parameter(Descriptor::kSlot);
Node* vector = Parameter(Descriptor::kVector);
Node* handler = Parameter(Descriptor::kHandler);
Node* context = Parameter(Descriptor::kContext);
LoadICParameters p(context, receiver, name, slot, vector);
LoadICProtoArray(&p, handler);
}
void AccessorAssemblerImpl::GenerateLoadGlobalIC() {
typedef LoadGlobalICStub::Descriptor Descriptor;
Node* slot = Parameter(Descriptor::kSlot);
Node* vector = Parameter(Descriptor::kVector);
Node* context = Parameter(Descriptor::kContext);
LoadICParameters p(context, nullptr, nullptr, slot, vector);
LoadGlobalIC(&p);
}
void AccessorAssemblerImpl::GenerateLoadGlobalICTrampoline() {
typedef LoadGlobalICTrampolineStub::Descriptor Descriptor;
Node* slot = Parameter(Descriptor::kSlot);
Node* context = Parameter(Descriptor::kContext);
Node* vector = LoadTypeFeedbackVectorForStub();
LoadICParameters p(context, nullptr, nullptr, slot, vector);
LoadGlobalIC(&p);
}
void AccessorAssemblerImpl::GenerateKeyedLoadICTF() {
typedef KeyedLoadICTFStub::Descriptor 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 AccessorAssemblerImpl::GenerateKeyedLoadICTrampolineTF() {
typedef KeyedLoadICTrampolineTFStub::Descriptor Descriptor;
Node* receiver = Parameter(Descriptor::kReceiver);
Node* name = Parameter(Descriptor::kName);
Node* slot = Parameter(Descriptor::kSlot);
Node* context = Parameter(Descriptor::kContext);
Node* vector = LoadTypeFeedbackVectorForStub();
LoadICParameters p(context, receiver, name, slot, vector);
KeyedLoadIC(&p);
}
void AccessorAssemblerImpl::GenerateKeyedLoadICMegamorphic() {
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 AccessorAssemblerImpl::GenerateStoreIC() {
typedef StoreICStub::Descriptor 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 AccessorAssemblerImpl::GenerateStoreICTrampoline() {
typedef StoreICTrampolineStub::Descriptor 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 = LoadTypeFeedbackVectorForStub();
StoreICParameters p(context, receiver, name, value, slot, vector);
StoreIC(&p);
}
void AccessorAssemblerImpl::GenerateKeyedStoreICTF(LanguageMode language_mode) {
typedef KeyedStoreICTFStub::Descriptor 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, language_mode);
}
void AccessorAssemblerImpl::GenerateKeyedStoreICTrampolineTF(
LanguageMode language_mode) {
typedef KeyedStoreICTrampolineTFStub::Descriptor 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 = LoadTypeFeedbackVectorForStub();
StoreICParameters p(context, receiver, name, value, slot, vector);
KeyedStoreIC(&p, language_mode);
}
//////////////////// AccessorAssembler implementation.
#define DISPATCH_TO_IMPL(Name) \
void AccessorAssembler::Generate##Name(CodeAssemblerState* state) { \
AccessorAssemblerImpl assembler(state); \
assembler.Generate##Name(); \
}
ACCESSOR_ASSEMBLER_PUBLIC_INTERFACE(DISPATCH_TO_IMPL)
#undef DISPATCH_TO_IMPL
void AccessorAssembler::GenerateKeyedStoreICTF(CodeAssemblerState* state,
LanguageMode language_mode) {
AccessorAssemblerImpl assembler(state);
assembler.GenerateKeyedStoreICTF(language_mode);
}
void AccessorAssembler::GenerateKeyedStoreICTrampolineTF(
CodeAssemblerState* state, LanguageMode language_mode) {
AccessorAssemblerImpl assembler(state);
assembler.GenerateKeyedStoreICTrampolineTF(language_mode);
}
#undef ACCESSOR_ASSEMBLER_PUBLIC_INTERFACE
} // namespace internal
} // namespace v8
// 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.
#ifndef V8_SRC_IC_ACCESSOR_ASSEMBLER_H_
#define V8_SRC_IC_ACCESSOR_ASSEMBLER_H_
#include "src/globals.h"
namespace v8 {
namespace internal {
namespace compiler {
class CodeAssemblerState;
}
class AccessorAssembler {
public:
static void GenerateLoadIC(compiler::CodeAssemblerState* state);
static void GenerateLoadICTrampoline(compiler::CodeAssemblerState* state);
static void GenerateLoadICProtoArray(compiler::CodeAssemblerState* state);
static void GenerateLoadGlobalIC(compiler::CodeAssemblerState* state);
static void GenerateLoadGlobalICTrampoline(
compiler::CodeAssemblerState* state);
static void GenerateKeyedLoadICTF(compiler::CodeAssemblerState* state);
static void GenerateKeyedLoadICTrampolineTF(
compiler::CodeAssemblerState* state);
static void GenerateKeyedLoadICMegamorphic(
compiler::CodeAssemblerState* state);
static void GenerateStoreIC(compiler::CodeAssemblerState* state);
static void GenerateStoreICTrampoline(compiler::CodeAssemblerState* state);
static void GenerateKeyedStoreICTF(compiler::CodeAssemblerState* state,
LanguageMode language_mode);
static void GenerateKeyedStoreICTrampolineTF(
compiler::CodeAssemblerState* state, LanguageMode language_mode);
};
} // namespace internal
} // namespace v8
#endif // V8_SRC_IC_ACCESSOR_ASSEMBLER_H_
......@@ -6,6 +6,7 @@
#include "src/code-stub-assembler.h"
#include "src/contexts.h"
#include "src/ic/accessor-assembler-impl.h"
#include "src/interface-descriptors.h"
#include "src/isolate.h"
......@@ -14,13 +15,12 @@ namespace internal {
using compiler::Node;
class KeyedStoreGenericAssembler : public CodeStubAssembler {
class KeyedStoreGenericAssembler : public AccessorAssemblerImpl {
public:
explicit KeyedStoreGenericAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
: AccessorAssemblerImpl(state) {}
void KeyedStoreGeneric(const StoreICParameters* p,
LanguageMode language_mode);
void KeyedStoreGeneric(LanguageMode language_mode);
private:
enum UpdateLength {
......@@ -67,19 +67,8 @@ class KeyedStoreGenericAssembler : public CodeStubAssembler {
void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state,
LanguageMode language_mode) {
typedef StoreWithVectorDescriptor Descriptor;
KeyedStoreGenericAssembler assembler(state);
Node* receiver = assembler.Parameter(Descriptor::kReceiver);
Node* name = assembler.Parameter(Descriptor::kName);
Node* value = assembler.Parameter(Descriptor::kValue);
Node* slot = assembler.Parameter(Descriptor::kSlot);
Node* vector = assembler.Parameter(Descriptor::kVector);
Node* context = assembler.Parameter(Descriptor::kContext);
CodeStubAssembler::StoreICParameters p(context, receiver, name, value, slot,
vector);
assembler.KeyedStoreGeneric(&p, language_mode);
assembler.KeyedStoreGeneric(language_mode);
}
void KeyedStoreGenericAssembler::BranchIfPrototypesHaveNonFastElements(
......@@ -104,9 +93,7 @@ void KeyedStoreGenericAssembler::BranchIfPrototypesHaveNonFastElements(
non_fast_elements);
Node* elements_kind = LoadMapElementsKind(prototype_map);
STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND);
GotoIf(Int32LessThanOrEqual(elements_kind,
Int32Constant(LAST_FAST_ELEMENTS_KIND)),
&loop_body);
GotoIf(IsFastElementsKind(elements_kind), &loop_body);
GotoIf(Word32Equal(elements_kind, Int32Constant(NO_ELEMENTS)), &loop_body);
Goto(non_fast_elements);
}
......@@ -409,14 +396,13 @@ void KeyedStoreGenericAssembler::StoreElementWithCapacity(
void KeyedStoreGenericAssembler::EmitGenericElementStore(
Node* receiver, Node* receiver_map, Node* instance_type, Node* intptr_index,
Node* value, Node* context, Label* slow) {
Label if_in_bounds(this), if_increment_length_by_one(this),
Label if_fast(this), if_in_bounds(this), if_increment_length_by_one(this),
if_bump_length_with_gap(this), if_grow(this), if_nonfast(this),
if_typed_array(this), if_dictionary(this);
Node* elements = LoadElements(receiver);
Node* elements_kind = LoadMapElementsKind(receiver_map);
GotoIf(
Int32GreaterThan(elements_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
&if_nonfast);
Branch(IsFastElementsKind(elements_kind), &if_fast, &if_nonfast);
Bind(&if_fast);
Label if_array(this);
GotoIf(Word32Equal(instance_type, Int32Constant(JS_ARRAY_TYPE)), &if_array);
......@@ -517,12 +503,19 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
}
}
void KeyedStoreGenericAssembler::KeyedStoreGeneric(const StoreICParameters* p,
LanguageMode language_mode) {
void KeyedStoreGenericAssembler::KeyedStoreGeneric(LanguageMode language_mode) {
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);
Variable var_index(this, MachineType::PointerRepresentation());
Label if_index(this), if_unique_name(this), slow(this);
Node* receiver = p->receiver;
GotoIf(TaggedIsSmi(receiver), &slow);
Node* receiver_map = LoadMap(receiver);
Node* instance_type = LoadMapInstanceType(receiver_map);
......@@ -532,26 +525,28 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(const StoreICParameters* p,
Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)),
&slow);
TryToName(p->name, &if_index, &var_index, &if_unique_name, &slow);
TryToName(name, &if_index, &var_index, &if_unique_name, &slow);
Bind(&if_index);
{
Comment("integer index");
EmitGenericElementStore(receiver, receiver_map, instance_type,
var_index.value(), p->value, p->context, &slow);
var_index.value(), value, context, &slow);
}
Bind(&if_unique_name);
{
Comment("key is unique name");
EmitGenericPropertyStore(receiver, receiver_map, p, &slow);
KeyedStoreGenericAssembler::StoreICParameters p(context, receiver, name,
value, slot, vector);
EmitGenericPropertyStore(receiver, receiver_map, &p, &slow);
}
Bind(&slow);
{
Comment("KeyedStoreGeneric_slow");
TailCallRuntime(Runtime::kSetProperty, p->context, p->receiver, p->name,
p->value, SmiConstant(language_mode));
TailCallRuntime(Runtime::kSetProperty, context, receiver, name, value,
SmiConstant(language_mode));
}
}
......
......@@ -952,6 +952,9 @@
'ic/access-compiler-data.h',
'ic/access-compiler.cc',
'ic/access-compiler.h',
'ic/accessor-assembler.cc',
'ic/accessor-assembler-impl.h',
'ic/accessor-assembler.h',
'ic/call-optimization.cc',
'ic/call-optimization.h',
'ic/handler-compiler.cc',
......
......@@ -98,6 +98,7 @@ v8_executable("cctest") {
"profiler-extension.cc",
"profiler-extension.h",
"test-access-checks.cc",
"test-accessor-assembler.cc",
"test-accessors.cc",
"test-api-accessors.cc",
"test-api-fast-accessor-builder.cc",
......
......@@ -119,6 +119,7 @@
'profiler-extension.cc',
'profiler-extension.h',
'test-access-checks.cc',
'test-accessor-assembler.cc',
'test-accessors.cc',
'test-api.cc',
'test-api.h',
......
// 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 "test/cctest/cctest.h"
#include "src/base/utils/random-number-generator.h"
#include "src/ic/accessor-assembler-impl.h"
#include "src/ic/stub-cache.h"
#include "test/cctest/compiler/code-assembler-tester.h"
#include "test/cctest/compiler/function-tester.h"
namespace v8 {
namespace internal {
using compiler::CodeAssemblerTester;
using compiler::FunctionTester;
using compiler::Node;
namespace {
void TestStubCacheOffsetCalculation(StubCache::Table table) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 2;
CodeAssemblerTester data(isolate, kNumParams);
AccessorAssemblerImpl m(data.state());
{
Node* name = m.Parameter(0);
Node* map = m.Parameter(1);
Node* primary_offset = m.StubCachePrimaryOffsetForTesting(name, map);
Node* result;
if (table == StubCache::kPrimary) {
result = primary_offset;
} else {
CHECK_EQ(StubCache::kSecondary, table);
result = m.StubCacheSecondaryOffsetForTesting(name, primary_offset);
}
m.Return(m.SmiFromWord32(result));
}
Handle<Code> code = data.GenerateCode();
FunctionTester ft(code, kNumParams);
Factory* factory = isolate->factory();
Handle<Name> names[] = {
factory->NewSymbol(),
factory->InternalizeUtf8String("a"),
factory->InternalizeUtf8String("bb"),
factory->InternalizeUtf8String("ccc"),
factory->NewPrivateSymbol(),
factory->InternalizeUtf8String("dddd"),
factory->InternalizeUtf8String("eeeee"),
factory->InternalizeUtf8String("name"),
factory->NewSymbol(),
factory->NewPrivateSymbol(),
};
Handle<Map> maps[] = {
Handle<Map>(nullptr, isolate),
factory->cell_map(),
Map::Create(isolate, 0),
factory->meta_map(),
factory->code_map(),
Map::Create(isolate, 0),
factory->hash_table_map(),
factory->symbol_map(),
factory->string_map(),
Map::Create(isolate, 0),
factory->sloppy_arguments_elements_map(),
};
for (size_t name_index = 0; name_index < arraysize(names); name_index++) {
Handle<Name> name = names[name_index];
for (size_t map_index = 0; map_index < arraysize(maps); map_index++) {
Handle<Map> map = maps[map_index];
int expected_result;
{
int primary_offset = StubCache::PrimaryOffsetForTesting(*name, *map);
if (table == StubCache::kPrimary) {
expected_result = primary_offset;
} else {
expected_result =
StubCache::SecondaryOffsetForTesting(*name, primary_offset);
}
}
Handle<Object> result = ft.Call(name, map).ToHandleChecked();
Smi* expected = Smi::FromInt(expected_result & Smi::kMaxValue);
CHECK_EQ(expected, Smi::cast(*result));
}
}
}
} // namespace
TEST(StubCachePrimaryOffset) {
TestStubCacheOffsetCalculation(StubCache::kPrimary);
}
TEST(StubCacheSecondaryOffset) {
TestStubCacheOffsetCalculation(StubCache::kSecondary);
}
namespace {
Handle<Code> CreateCodeWithFlags(Code::Flags flags) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeAssemblerTester data(isolate, flags);
CodeStubAssembler m(data.state());
m.Return(m.UndefinedConstant());
return data.GenerateCodeCloseAndEscape();
}
} // namespace
TEST(TryProbeStubCache) {
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 3;
CodeAssemblerTester data(isolate, kNumParams);
AccessorAssemblerImpl m(data.state());
Code::Kind ic_kind = Code::LOAD_IC;
StubCache stub_cache(isolate, ic_kind);
stub_cache.Clear();
{
Node* receiver = m.Parameter(0);
Node* name = m.Parameter(1);
Node* expected_handler = m.Parameter(2);
Label passed(&m), failed(&m);
Variable var_handler(&m, MachineRepresentation::kTagged);
Label if_handler(&m), if_miss(&m);
m.TryProbeStubCache(&stub_cache, receiver, name, &if_handler, &var_handler,
&if_miss);
m.Bind(&if_handler);
m.Branch(m.WordEqual(expected_handler, var_handler.value()), &passed,
&failed);
m.Bind(&if_miss);
m.Branch(m.WordEqual(expected_handler, m.IntPtrConstant(0)), &passed,
&failed);
m.Bind(&passed);
m.Return(m.BooleanConstant(true));
m.Bind(&failed);
m.Return(m.BooleanConstant(false));
}
Handle<Code> code = data.GenerateCode();
FunctionTester ft(code, kNumParams);
std::vector<Handle<Name>> names;
std::vector<Handle<JSObject>> receivers;
std::vector<Handle<Code>> handlers;
base::RandomNumberGenerator rand_gen(FLAG_random_seed);
Factory* factory = isolate->factory();
// Generate some number of names.
for (int i = 0; i < StubCache::kPrimaryTableSize / 7; i++) {
Handle<Name> name;
switch (rand_gen.NextInt(3)) {
case 0: {
// Generate string.
std::stringstream ss;
ss << "s" << std::hex
<< (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize);
name = factory->InternalizeUtf8String(ss.str().c_str());
break;
}
case 1: {
// Generate number string.
std::stringstream ss;
ss << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize);
name = factory->InternalizeUtf8String(ss.str().c_str());
break;
}
case 2: {
// Generate symbol.
name = factory->NewSymbol();
break;
}
default:
UNREACHABLE();
}
names.push_back(name);
}
// Generate some number of receiver maps and receivers.
for (int i = 0; i < StubCache::kSecondaryTableSize / 2; i++) {
Handle<Map> map = Map::Create(isolate, 0);
receivers.push_back(factory->NewJSObjectFromMap(map));
}
// Generate some number of handlers.
for (int i = 0; i < 30; i++) {
Code::Flags flags =
Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(ic_kind));
handlers.push_back(CreateCodeWithFlags(flags));
}
// Ensure that GC does happen because from now on we are going to fill our
// own stub cache instance with raw values.
DisallowHeapAllocation no_gc;
// Populate {stub_cache}.
const int N = StubCache::kPrimaryTableSize + StubCache::kSecondaryTableSize;
for (int i = 0; i < N; i++) {
int index = rand_gen.NextInt();
Handle<Name> name = names[index % names.size()];
Handle<JSObject> receiver = receivers[index % receivers.size()];
Handle<Code> handler = handlers[index % handlers.size()];
stub_cache.Set(*name, receiver->map(), *handler);
}
// Perform some queries.
bool queried_existing = false;
bool queried_non_existing = false;
for (int i = 0; i < N; i++) {
int index = rand_gen.NextInt();
Handle<Name> name = names[index % names.size()];
Handle<JSObject> receiver = receivers[index % receivers.size()];
Object* handler = stub_cache.Get(*name, receiver->map());
if (handler == nullptr) {
queried_non_existing = true;
} else {
queried_existing = true;
}
Handle<Object> expected_handler(handler, isolate);
ft.CheckTrue(receiver, name, expected_handler);
}
for (int i = 0; i < N; i++) {
int index1 = rand_gen.NextInt();
int index2 = rand_gen.NextInt();
Handle<Name> name = names[index1 % names.size()];
Handle<JSObject> receiver = receivers[index2 % receivers.size()];
Object* handler = stub_cache.Get(*name, receiver->map());
if (handler == nullptr) {
queried_non_existing = true;
} else {
queried_existing = true;
}
Handle<Object> expected_handler(handler, isolate);
ft.CheckTrue(receiver, name, expected_handler);
}
// Ensure we performed both kind of queries.
CHECK(queried_existing && queried_non_existing);
}
} // namespace internal
} // namespace v8
......@@ -6,7 +6,6 @@
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/compiler/node.h"
#include "src/ic/stub-cache.h"
#include "src/isolate.h"
#include "test/cctest/compiler/code-assembler-tester.h"
#include "test/cctest/compiler/function-tester.h"
......@@ -1274,248 +1273,6 @@ TEST(TestOutOfScopeVariable) {
CHECK(!data.GenerateCode().is_null());
}
namespace {
void TestStubCacheOffsetCalculation(StubCache::Table table) {
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 2;
CodeAssemblerTester data(isolate, kNumParams);
CodeStubAssembler m(data.state());
{
Node* name = m.Parameter(0);
Node* map = m.Parameter(1);
Node* primary_offset = m.StubCachePrimaryOffset(name, map);
Node* result;
if (table == StubCache::kPrimary) {
result = primary_offset;
} else {
CHECK_EQ(StubCache::kSecondary, table);
result = m.StubCacheSecondaryOffset(name, primary_offset);
}
m.Return(m.SmiFromWord32(result));
}
Handle<Code> code = data.GenerateCode();
FunctionTester ft(code, kNumParams);
Factory* factory = isolate->factory();
Handle<Name> names[] = {
factory->NewSymbol(),
factory->InternalizeUtf8String("a"),
factory->InternalizeUtf8String("bb"),
factory->InternalizeUtf8String("ccc"),
factory->NewPrivateSymbol(),
factory->InternalizeUtf8String("dddd"),
factory->InternalizeUtf8String("eeeee"),
factory->InternalizeUtf8String("name"),
factory->NewSymbol(),
factory->NewPrivateSymbol(),
};
Handle<Map> maps[] = {
Handle<Map>(nullptr, isolate),
factory->cell_map(),
Map::Create(isolate, 0),
factory->meta_map(),
factory->code_map(),
Map::Create(isolate, 0),
factory->hash_table_map(),
factory->symbol_map(),
factory->string_map(),
Map::Create(isolate, 0),
factory->sloppy_arguments_elements_map(),
};
for (size_t name_index = 0; name_index < arraysize(names); name_index++) {
Handle<Name> name = names[name_index];
for (size_t map_index = 0; map_index < arraysize(maps); map_index++) {
Handle<Map> map = maps[map_index];
int expected_result;
{
int primary_offset = StubCache::PrimaryOffsetForTesting(*name, *map);
if (table == StubCache::kPrimary) {
expected_result = primary_offset;
} else {
expected_result =
StubCache::SecondaryOffsetForTesting(*name, primary_offset);
}
}
Handle<Object> result = ft.Call(name, map).ToHandleChecked();
Smi* expected = Smi::FromInt(expected_result & Smi::kMaxValue);
CHECK_EQ(expected, Smi::cast(*result));
}
}
}
} // namespace
TEST(StubCachePrimaryOffset) {
TestStubCacheOffsetCalculation(StubCache::kPrimary);
}
TEST(StubCacheSecondaryOffset) {
TestStubCacheOffsetCalculation(StubCache::kSecondary);
}
namespace {
Handle<Code> CreateCodeWithFlags(Code::Flags flags) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeAssemblerTester data(isolate, flags);
CodeStubAssembler m(data.state());
m.Return(m.UndefinedConstant());
return data.GenerateCodeCloseAndEscape();
}
} // namespace
TEST(TryProbeStubCache) {
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
Isolate* isolate(CcTest::InitIsolateOnce());
const int kNumParams = 3;
CodeAssemblerTester data(isolate, kNumParams);
CodeStubAssembler m(data.state());
Code::Kind ic_kind = Code::LOAD_IC;
StubCache stub_cache(isolate, ic_kind);
stub_cache.Clear();
{
Node* receiver = m.Parameter(0);
Node* name = m.Parameter(1);
Node* expected_handler = m.Parameter(2);
Label passed(&m), failed(&m);
Variable var_handler(&m, MachineRepresentation::kTagged);
Label if_handler(&m), if_miss(&m);
m.TryProbeStubCache(&stub_cache, receiver, name, &if_handler, &var_handler,
&if_miss);
m.Bind(&if_handler);
m.Branch(m.WordEqual(expected_handler, var_handler.value()), &passed,
&failed);
m.Bind(&if_miss);
m.Branch(m.WordEqual(expected_handler, m.IntPtrConstant(0)), &passed,
&failed);
m.Bind(&passed);
m.Return(m.BooleanConstant(true));
m.Bind(&failed);
m.Return(m.BooleanConstant(false));
}
Handle<Code> code = data.GenerateCode();
FunctionTester ft(code, kNumParams);
std::vector<Handle<Name>> names;
std::vector<Handle<JSObject>> receivers;
std::vector<Handle<Code>> handlers;
base::RandomNumberGenerator rand_gen(FLAG_random_seed);
Factory* factory = isolate->factory();
// Generate some number of names.
for (int i = 0; i < StubCache::kPrimaryTableSize / 7; i++) {
Handle<Name> name;
switch (rand_gen.NextInt(3)) {
case 0: {
// Generate string.
std::stringstream ss;
ss << "s" << std::hex
<< (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize);
name = factory->InternalizeUtf8String(ss.str().c_str());
break;
}
case 1: {
// Generate number string.
std::stringstream ss;
ss << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize);
name = factory->InternalizeUtf8String(ss.str().c_str());
break;
}
case 2: {
// Generate symbol.
name = factory->NewSymbol();
break;
}
default:
UNREACHABLE();
}
names.push_back(name);
}
// Generate some number of receiver maps and receivers.
for (int i = 0; i < StubCache::kSecondaryTableSize / 2; i++) {
Handle<Map> map = Map::Create(isolate, 0);
receivers.push_back(factory->NewJSObjectFromMap(map));
}
// Generate some number of handlers.
for (int i = 0; i < 30; i++) {
Code::Flags flags =
Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(ic_kind));
handlers.push_back(CreateCodeWithFlags(flags));
}
// Ensure that GC does happen because from now on we are going to fill our
// own stub cache instance with raw values.
DisallowHeapAllocation no_gc;
// Populate {stub_cache}.
const int N = StubCache::kPrimaryTableSize + StubCache::kSecondaryTableSize;
for (int i = 0; i < N; i++) {
int index = rand_gen.NextInt();
Handle<Name> name = names[index % names.size()];
Handle<JSObject> receiver = receivers[index % receivers.size()];
Handle<Code> handler = handlers[index % handlers.size()];
stub_cache.Set(*name, receiver->map(), *handler);
}
// Perform some queries.
bool queried_existing = false;
bool queried_non_existing = false;
for (int i = 0; i < N; i++) {
int index = rand_gen.NextInt();
Handle<Name> name = names[index % names.size()];
Handle<JSObject> receiver = receivers[index % receivers.size()];
Object* handler = stub_cache.Get(*name, receiver->map());
if (handler == nullptr) {
queried_non_existing = true;
} else {
queried_existing = true;
}
Handle<Object> expected_handler(handler, isolate);
ft.CheckTrue(receiver, name, expected_handler);
}
for (int i = 0; i < N; i++) {
int index1 = rand_gen.NextInt();
int index2 = rand_gen.NextInt();
Handle<Name> name = names[index1 % names.size()];
Handle<JSObject> receiver = receivers[index2 % receivers.size()];
Object* handler = stub_cache.Get(*name, receiver->map());
if (handler == nullptr) {
queried_non_existing = true;
} else {
queried_existing = true;
}
Handle<Object> expected_handler(handler, isolate);
ft.CheckTrue(receiver, name, expected_handler);
}
// Ensure we performed both kind of queries.
CHECK(queried_existing && queried_non_existing);
}
TEST(GotoIfException) {
typedef CodeStubAssembler::Label Label;
typedef CodeStubAssembler::Variable Variable;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment