[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}
This commit is contained in:
parent
fb5b2f5241
commit
73460009a6
@ -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());
|
||||
}
|
||||
|
||||
|
46
src/ic/ic.cc
46
src/ic/ic.cc
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user