Commit 73460009 authored by ishell's avatar ishell Committed by Commit bot

[ic] Support data handlers that represent loads from prototypes.

This CL also adds separate runtime call stats buckets for data handlers.

BUG=

Review-Url: https://codereview.chromium.org/2419513002
Cr-Commit-Position: refs/heads/master@{#40281}
parent fb5b2f52
......@@ -8303,8 +8303,8 @@ class Internals {
static const int kNodeIsPartiallyDependentShift = 4;
static const int kNodeIsActiveShift = 4;
static const int kJSObjectType = 0xba;
static const int kJSApiObjectType = 0xb9;
static const int kJSObjectType = 0xbb;
static const int kJSApiObjectType = 0xba;
static const int kFirstNonstringType = 0x80;
static const int kOddballType = 0x83;
static const int kForeignType = 0x87;
......
......@@ -277,6 +277,7 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
case CELL_TYPE:
case WEAK_CELL_TYPE:
case PROTOTYPE_INFO_TYPE:
case TUPLE3_TYPE:
case CONTEXT_EXTENSION_TYPE:
UNREACHABLE();
return kNone;
......
......@@ -4543,11 +4543,11 @@ void CodeStubAssembler::TryProbeStubCacheTable(
DCHECK_EQ(kPointerSize, stub_cache->value_reference(table).address() -
stub_cache->key_reference(table).address());
Node* code = Load(MachineType::Pointer(), key_base,
IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)));
Node* handler = Load(MachineType::Pointer(), key_base,
IntPtrAdd(entry_offset, IntPtrConstant(kPointerSize)));
// We found the handler.
var_handler->Bind(code);
var_handler->Bind(handler);
Goto(if_handler);
}
......@@ -4833,16 +4833,26 @@ void CodeStubAssembler::HandleLoadICHandlerCase(
const LoadICParameters* p, Node* handler, Label* miss,
ElementSupport support_elements) {
Comment("have_handler");
Label call_handler(this);
GotoUnless(TaggedIsSmi(handler), &call_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_cell_handler(this), call_handler(this);
Branch(TaggedIsSmi(handler), &if_smi_handler, &try_proto_cell_handler);
// |handler| is a Smi, encoding what to do. See handler-configuration.h
// for the encoding format.
Bind(&if_smi_handler);
{
Variable var_double_value(this, MachineRepresentation::kFloat64);
Label rebox_double(this, &var_double_value);
Node* handler_word = SmiUntag(handler);
Node* holder = var_holder.value();
Node* handler_word = SmiUntag(var_smi_handler.value());
if (support_elements == kSupportElements) {
Label property(this);
Node* handler_type =
......@@ -4853,14 +4863,14 @@ void CodeStubAssembler::HandleLoadICHandlerCase(
Comment("element_load");
Node* intptr_index = TryToIntptr(p->name, miss);
Node* elements = LoadElements(p->receiver);
Node* elements = LoadElements(holder);
Node* is_jsarray =
WordAnd(handler_word, IntPtrConstant(KeyedLoadIsJsArray::kMask));
Node* is_jsarray_condition = WordNotEqual(is_jsarray, IntPtrConstant(0));
Node* elements_kind = BitFieldDecode<KeyedLoadElementsKind>(handler_word);
Label if_hole(this), unimplemented_elements_kind(this);
Label* out_of_bounds = miss;
EmitElementLoad(p->receiver, elements, elements_kind, intptr_index,
EmitElementLoad(holder, elements, elements_kind, intptr_index,
is_jsarray_condition, &if_hole, &rebox_double,
&var_double_value, &unimplemented_elements_kind,
out_of_bounds, miss);
......@@ -4907,20 +4917,20 @@ void CodeStubAssembler::HandleLoadICHandlerCase(
GotoIf(WordEqual(inobject_bit, IntPtrConstant(0)), &out_of_object);
GotoUnless(WordEqual(double_bit, IntPtrConstant(0)), &inobject_double);
Return(LoadObjectField(p->receiver, offset));
Return(LoadObjectField(holder, offset));
Bind(&inobject_double);
if (FLAG_unbox_double_fields) {
var_double_value.Bind(
LoadObjectField(p->receiver, offset, MachineType::Float64()));
LoadObjectField(holder, offset, MachineType::Float64()));
} else {
Node* mutable_heap_number = LoadObjectField(p->receiver, offset);
Node* mutable_heap_number = LoadObjectField(holder, offset);
var_double_value.Bind(LoadHeapNumberValue(mutable_heap_number));
}
Goto(&rebox_double);
Bind(&out_of_object);
Node* properties = LoadProperties(p->receiver);
Node* properties = LoadProperties(holder);
Node* value = LoadObjectField(properties, offset);
GotoUnless(WordEqual(double_bit, IntPtrConstant(0)), &out_of_object_double);
Return(value);
......@@ -4933,14 +4943,40 @@ void CodeStubAssembler::HandleLoadICHandlerCase(
Return(AllocateHeapNumberWithValue(var_double_value.value()));
}
Bind(&try_proto_cell_handler);
{
GotoIf(WordNotEqual(LoadMap(handler), LoadRoot(Heap::kTuple3MapRootIndex)),
&call_handler);
Node* validity_cell = LoadObjectField(handler, Tuple3::kValue1Offset);
Node* cell_value = LoadObjectField(validity_cell, Cell::kValueOffset);
GotoIf(WordNotEqual(cell_value,
SmiConstant(Smi::FromInt(Map::kPrototypeChainValid))),
miss);
Node* holder =
LoadWeakCellValue(LoadObjectField(handler, Tuple3::kValue2Offset));
// The |holder| is guaranteed to be alive at this point since we passed
// both the receiver map check and the validity cell check.
Assert(WordNotEqual(holder, IntPtrConstant(0)));
Node* smi_handler = LoadObjectField(handler, Tuple3::kValue3Offset);
Assert(TaggedIsSmi(smi_handler));
var_holder.Bind(holder);
var_smi_handler.Bind(smi_handler);
Goto(&if_smi_handler);
}
// |handler| is a heap object. Must be code, call it.
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));
{
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::LoadIC(const LoadICParameters* p) {
......
......@@ -272,6 +272,7 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case CELL_TYPE:
case WEAK_CELL_TYPE:
case PROTOTYPE_INFO_TYPE:
case TUPLE3_TYPE:
case CONTEXT_EXTENSION_TYPE:
UNREACHABLE();
return kNone;
......
......@@ -729,6 +729,8 @@ class RuntimeCallTimer {
V(LoadIC_LoadCallback) \
V(LoadIC_LoadConstant) \
V(LoadIC_LoadConstantStub) \
V(LoadIC_LoadFieldDH) \
V(LoadIC_LoadFieldFromPrototypeDH) \
V(LoadIC_LoadField) \
V(LoadIC_LoadFieldStub) \
V(LoadIC_LoadGlobal) \
......
......@@ -102,6 +102,15 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
return result;
}
Handle<Tuple3> Factory::NewTuple3(Handle<Object> value1, Handle<Object> value2,
Handle<Object> value3) {
Handle<Tuple3> result = Handle<Tuple3>::cast(NewStruct(TUPLE3_TYPE));
result->set_value1(*value1);
result->set_value2(*value2);
result->set_value3(*value3);
return result;
}
Handle<ContextExtension> Factory::NewContextExtension(
Handle<ScopeInfo> scope_info, Handle<Object> extension) {
Handle<ContextExtension> result =
......
......@@ -75,6 +75,10 @@ class Factory final {
// Create a new PrototypeInfo struct.
Handle<PrototypeInfo> NewPrototypeInfo();
// Create a new Tuple3 struct.
Handle<Tuple3> NewTuple3(Handle<Object> value1, Handle<Object> value2,
Handle<Object> value3);
// Create a new ContextExtension struct.
Handle<ContextExtension> NewContextExtension(Handle<ScopeInfo> scope_info,
Handle<Object> extension);
......
......@@ -93,7 +93,7 @@ Code* IC::target() const {
}
bool IC::IsHandler(Object* object) {
return (object->IsSmi() && (object != nullptr)) ||
return (object->IsSmi() && (object != nullptr)) || (object->IsTuple3()) ||
(object->IsCode() && Code::cast(object)->is_handler());
}
......
......@@ -848,6 +848,7 @@ Handle<Code> KeyedStoreIC::ChooseMegamorphicStub(Isolate* isolate,
Handle<Object> LoadIC::SimpleFieldLoad(FieldIndex index) {
if (FLAG_tf_load_ic_stub) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldDH);
return handle(Smi::FromInt(index.GetLoadByFieldOffset()), isolate());
}
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldStub);
......@@ -855,6 +856,46 @@ Handle<Object> LoadIC::SimpleFieldLoad(FieldIndex index) {
return stub.GetCode();
}
Handle<Object> LoadIC::SimpleFieldLoadFromPrototype(FieldIndex index,
Handle<Map> receiver_map,
Handle<JSObject> holder) {
if (!FLAG_tf_load_ic_stub) return Handle<Object>::null();
DCHECK(holder->HasFastProperties());
// The following kinds of receiver maps require custom handler compilation.
if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap() ||
receiver_map->IsJSGlobalObjectMap() ||
receiver_map->is_dictionary_map()) {
return Handle<Object>::null();
}
// Switch to custom compiled handler if the prototype chain contains global
// or dictionary objects.
for (PrototypeIterator iter(*receiver_map); !iter.IsAtEnd(); iter.Advance()) {
JSObject* current = iter.GetCurrent<JSObject>();
if (current == *holder) break;
Map* current_map = current->map();
if (current_map->IsJSGlobalObjectMap() ||
current_map->IsJSGlobalProxyMap() || current_map->is_dictionary_map()) {
return Handle<Object>::null();
}
// Only objects that do not require access checks are allowed in stubs.
DCHECK(!current_map->is_access_check_needed());
}
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldFromPrototypeDH);
Handle<Cell> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
DCHECK(!validity_cell.is_null());
Handle<Object> handler(Smi::FromInt(index.GetLoadByFieldOffset()), isolate());
DCHECK(handler->IsSmi());
Factory* factory = isolate()->factory();
Handle<WeakCell> holder_cell = factory->NewWeakCell(holder);
return factory->NewTuple3(validity_cell, holder_cell, handler);
}
bool IsCompatibleReceiver(LookupIterator* lookup, Handle<Map> receiver_map) {
DCHECK(lookup->state() == LookupIterator::ACCESSOR);
......@@ -1159,6 +1200,11 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
if (receiver_is_holder) {
return SimpleFieldLoad(field);
}
Handle<Object> handler =
SimpleFieldLoadFromPrototype(field, map, holder);
if (!handler.is_null()) {
return handler;
}
break; // Custom-compiled handler.
}
......
......@@ -310,6 +310,9 @@ class LoadIC : public IC {
private:
Handle<Object> SimpleFieldLoad(FieldIndex index);
Handle<Object> SimpleFieldLoadFromPrototype(FieldIndex index,
Handle<Map> receiver_map,
Handle<JSObject> holder);
friend class IC;
};
......
......@@ -980,6 +980,13 @@ void PrototypeInfo::PrototypeInfoVerify() {
CHECK(validity_cell()->IsCell() || validity_cell()->IsSmi());
}
void Tuple3::Tuple3Verify() {
CHECK(IsTuple3());
VerifyObjectField(kValue1Offset);
VerifyObjectField(kValue2Offset);
VerifyObjectField(kValue3Offset);
}
void ContextExtension::ContextExtensionVerify() {
CHECK(IsContextExtension());
VerifyObjectField(kScopeInfoOffset);
......
......@@ -5724,6 +5724,10 @@ ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
SMI_ACCESSORS(PrototypeInfo, bit_field, kBitFieldOffset)
BOOL_ACCESSORS(PrototypeInfo, bit_field, should_be_fast_map, kShouldBeFastBit)
ACCESSORS(Tuple3, value1, Object, kValue1Offset)
ACCESSORS(Tuple3, value2, Object, kValue2Offset)
ACCESSORS(Tuple3, value3, Object, kValue3Offset)
ACCESSORS(ContextExtension, scope_info, ScopeInfo, kScopeInfoOffset)
ACCESSORS(ContextExtension, extension, Object, kExtensionOffset)
......
......@@ -1208,6 +1208,14 @@ void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) { // NOLINT
os << "\n";
}
void Tuple3::Tuple3Print(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "Tuple3");
os << "\n - value1: " << Brief(value1());
os << "\n - value2: " << Brief(value2());
os << "\n - value3: " << Brief(value3());
os << "\n";
}
void ContextExtension::ContextExtensionPrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "ContextExtension");
os << "\n - scope_info: " << Brief(scope_info());
......
......@@ -404,6 +404,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE) \
V(PROMISE_REACTION_JOB_INFO_TYPE) \
V(PROTOTYPE_INFO_TYPE) \
V(TUPLE3_TYPE) \
V(CONTEXT_EXTENSION_TYPE) \
V(MODULE_TYPE) \
\
......@@ -529,6 +530,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(DEBUG_INFO, DebugInfo, debug_info) \
V(BREAK_POINT_INFO, BreakPointInfo, break_point_info) \
V(PROTOTYPE_INFO, PrototypeInfo, prototype_info) \
V(TUPLE3, Tuple3, tuple3) \
V(MODULE, Module, module) \
V(CONTEXT_EXTENSION, ContextExtension, context_extension)
......@@ -706,6 +708,7 @@ enum InstanceType {
TRANSITION_ARRAY_TYPE,
PROPERTY_CELL_TYPE,
PROTOTYPE_INFO_TYPE,
TUPLE3_TYPE,
CONTEXT_EXTENSION_TYPE,
MODULE_TYPE,
......@@ -6817,6 +6820,26 @@ class PrototypeInfo : public Struct {
DISALLOW_IMPLICIT_CONSTRUCTORS(PrototypeInfo);
};
class Tuple3 : public Struct {
public:
DECL_ACCESSORS(value1, Object)
DECL_ACCESSORS(value2, Object)
DECL_ACCESSORS(value3, Object)
DECLARE_CAST(Tuple3)
// Dispatched behavior.
DECLARE_PRINTER(Tuple3)
DECLARE_VERIFIER(Tuple3)
static const int kValue1Offset = HeapObject::kHeaderSize;
static const int kValue2Offset = kValue1Offset + kPointerSize;
static const int kValue3Offset = kValue2Offset + kPointerSize;
static const int kSize = kValue3Offset + kPointerSize;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Tuple3);
};
// Pair used to store both a ScopeInfo and an extension object in the extension
// slot of a block, catch, or with context. Needed in the rare case where a
......
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