Commit 4d2dd669 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[ic] Turn load-interceptor into a smi-handler

This doesn't support "lookup after interceptor", but that should be unnecessary by now since we have non-masking interceptors.

BUG=

Change-Id: I8650a47ab2ce6fa314de25d0c4775b5c165df179
Reviewed-on: https://chromium-review.googlesource.com/453376Reviewed-by: 's avatarHannes Payer <hpayer@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43766}
parent f20261bf
......@@ -186,7 +186,6 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
if (map == heap->boolean_map()) return kBoolean;
if (map == heap->the_hole_map()) return kHole;
DCHECK(map == heap->uninitialized_map() ||
map == heap->no_interceptor_result_sentinel_map() ||
map == heap->termination_exception_map() ||
map == heap->arguments_marker_map() ||
map == heap->optimized_out_map() ||
......
......@@ -181,7 +181,6 @@ Type::bitset BitsetType::Lub(i::Map* map) {
if (map == heap->boolean_map()) return kBoolean;
if (map == heap->the_hole_map()) return kHole;
DCHECK(map == heap->uninitialized_map() ||
map == heap->no_interceptor_result_sentinel_map() ||
map == heap->termination_exception_map() ||
map == heap->arguments_marker_map() ||
map == heap->optimized_out_map() ||
......
......@@ -786,7 +786,9 @@ class RuntimeCallTimer final {
V(LoadIC_LoadFieldFromPrototypeDH) \
V(LoadIC_LoadField) \
V(LoadIC_LoadGlobal) \
V(LoadIC_LoadInterceptor) \
V(LoadIC_LoadInterceptorDH) \
V(LoadIC_LoadNonMaskingInterceptorDH) \
V(LoadIC_LoadInterceptorFromPrototypeDH) \
V(LoadIC_LoadNonexistentDH) \
V(LoadIC_LoadNonexistent) \
V(LoadIC_LoadNormalDH) \
......
......@@ -2330,7 +2330,6 @@ bool Heap::CreateInitialMaps() {
Context::BOOLEAN_FUNCTION_INDEX);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, uninitialized);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, arguments_marker);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, no_interceptor_result_sentinel);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, exception);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, termination_exception);
ALLOCATE_MAP(ODDBALL_TYPE, Oddball::kSize, optimized_out);
......@@ -2670,11 +2669,6 @@ void Heap::CreateInitialObjects() {
handle(Smi::FromInt(-4), isolate()), "undefined",
Oddball::kArgumentsMarker));
set_no_interceptor_result_sentinel(*factory->NewOddball(
factory->no_interceptor_result_sentinel_map(),
"no_interceptor_result_sentinel", handle(Smi::FromInt(-2), isolate()),
"undefined", Oddball::kOther));
set_termination_exception(*factory->NewOddball(
factory->termination_exception_map(), "termination_exception",
handle(Smi::FromInt(-3), isolate()), "undefined", Oddball::kOther));
......
......@@ -67,7 +67,6 @@ using v8::MemoryPressureLevel;
/* This means they are never in new space and never on a page that is */ \
/* being compacted. */ \
/* Oddballs */ \
V(Oddball, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \
V(Oddball, arguments_marker, ArgumentsMarker) \
V(Oddball, exception, Exception) \
V(Oddball, termination_exception, TerminationException) \
......@@ -216,7 +215,6 @@ using v8::MemoryPressureLevel;
V(Map, boolean_map, BooleanMap) \
V(Map, uninitialized_map, UninitializedMap) \
V(Map, arguments_marker_map, ArgumentsMarkerMap) \
V(Map, no_interceptor_result_sentinel_map, NoInterceptorResultSentinelMap) \
V(Map, exception_map, ExceptionMap) \
V(Map, termination_exception_map, TerminationExceptionMap) \
V(Map, optimized_out_map, OptimizedOutMap) \
......@@ -278,7 +276,6 @@ using v8::MemoryPressureLevel;
V(FixedDoubleArrayMap) \
V(WeakCellMap) \
V(TransitionArrayMap) \
V(NoInterceptorResultSentinel) \
V(HashTableMap) \
V(OrderedHashTableMap) \
V(EmptyFixedArray) \
......
......@@ -183,7 +183,7 @@ void AccessorAssembler::HandleLoadICHandlerCase(
Bind(&if_smi_handler);
{
HandleLoadICSmiHandlerCase(p, var_holder.value(), var_smi_handler.value(),
miss, exit_point, support_elements);
miss, exit_point, false, support_elements);
}
Bind(&try_proto_handler);
......@@ -245,7 +245,8 @@ void AccessorAssembler::HandleLoadField(Node* holder, Node* handler_word,
void AccessorAssembler::HandleLoadICSmiHandlerCase(
const LoadICParameters* p, Node* holder, Node* smi_handler, Label* miss,
ExitPoint* exit_point, ElementSupport support_elements) {
ExitPoint* exit_point, bool throw_reference_error_if_nonexistent,
ElementSupport support_elements) {
Variable var_double_value(this, MachineRepresentation::kFloat64);
Label rebox_double(this, &var_double_value);
......@@ -253,9 +254,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
Node* handler_kind = DecodeWord<LoadHandler::KindBits>(handler_word);
if (support_elements == kSupportElements) {
Label property(this);
GotoIfNot(
WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForElements)),
&property);
GotoIfNot(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kElement)),
&property);
Comment("element_load");
Node* intptr_index = TryToIntptr(p->name, miss);
......@@ -296,17 +296,32 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
Comment("property_load");
}
Label constant(this), field(this), normal(this, Label::kDeferred);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForFields)),
&field);
Label constant(this), field(this), normal(this, Label::kDeferred),
interceptor(this, Label::kDeferred), nonexistent(this);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field);
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kForConstants)),
&constant, &normal);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)),
&constant);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNonExistent)),
&nonexistent);
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kNormal)), &normal,
&interceptor);
Bind(&field);
HandleLoadField(holder, handler_word, &var_double_value, &rebox_double,
exit_point);
Bind(&nonexistent);
// This is a handler for a load of a non-existent value.
if (throw_reference_error_if_nonexistent) {
exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context,
p->name);
} else {
exit_point->Return(UndefinedConstant());
}
Bind(&constant);
{
Comment("constant_load");
......@@ -354,6 +369,14 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
}
}
Bind(&interceptor);
{
Comment("load_interceptor");
exit_point->ReturnCallRuntime(Runtime::kLoadPropertyWithInterceptor,
p->context, p->name, p->receiver, holder,
p->slot, p->vector);
}
Bind(&rebox_double);
exit_point->Return(AllocateHeapNumberWithValue(var_double_value.value()));
}
......@@ -420,23 +443,27 @@ void AccessorAssembler::HandleLoadICProtoHandlerCase(
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.
if (throw_reference_error_if_nonexistent) {
exit_point->ReturnCallRuntime(Runtime::kThrowReferenceError, p->context,
p->name);
} else {
exit_point->Return(UndefinedConstant());
Label load_from_cached_holder(this), done(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()),
&load_from_cached_holder);
{
var_holder->Bind(p->receiver);
Goto(&done);
}
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)));
Bind(&load_from_cached_holder);
{
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);
Goto(&done);
}
var_holder->Bind(holder);
Bind(&done);
var_smi_handler->Bind(smi_handler);
Goto(if_smi_handler);
}
......@@ -450,10 +477,11 @@ void AccessorAssembler::HandleLoadICProtoHandlerCase(
}
}
Node* AccessorAssembler::EmitLoadICProtoArrayCheck(
const LoadICParameters* p, Node* handler, Node* handler_length,
Node* handler_flags, Label* miss,
bool throw_reference_error_if_nonexistent) {
Node* AccessorAssembler::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));
......@@ -494,22 +522,22 @@ Node* AccessorAssembler::EmitLoadICProtoArrayCheck(
Node* maybe_holder_cell =
LoadFixedArrayElement(handler, LoadHandler::kHolderCellIndex);
Label load_existent(this);
GotoIf(WordNotEqual(maybe_holder_cell, NullConstant()), &load_existent);
// This is a handler for a load of a non-existent value.
if (throw_reference_error_if_nonexistent) {
TailCallRuntime(Runtime::kThrowReferenceError, p->context, p->name);
} else {
Return(UndefinedConstant());
Variable var_holder(this, MachineRepresentation::kTagged, p->receiver);
Label done(this);
GotoIf(WordEqual(maybe_holder_cell, NullConstant()), &done);
{
var_holder.Bind(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(var_holder.value(), IntPtrConstant(0)));
Goto(&done);
}
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;
Bind(&done);
return var_holder.value();
}
void AccessorAssembler::HandleLoadGlobalICHandlerCase(
......@@ -523,12 +551,14 @@ void AccessorAssembler::HandleLoadGlobalICHandlerCase(
Variable var_holder(this, MachineRepresentation::kTagged);
Variable var_smi_handler(this, MachineRepresentation::kTagged);
Label if_smi_handler(this);
HandleLoadICProtoHandlerCase(&p, handler, &var_holder, &var_smi_handler,
&if_smi_handler, miss, exit_point,
throw_reference_error_if_nonexistent);
Bind(&if_smi_handler);
HandleLoadICSmiHandlerCase(&p, var_holder.value(), var_smi_handler.value(),
miss, exit_point, kOnlyProperties);
HandleLoadICSmiHandlerCase(
&p, var_holder.value(), var_smi_handler.value(), miss, exit_point,
throw_reference_error_if_nonexistent, kOnlyProperties);
}
void AccessorAssembler::JumpIfDataProperty(Node* details, Label* writable,
......@@ -1813,11 +1843,11 @@ void AccessorAssembler::LoadICProtoArray(
Node* handler_length = LoadAndUntagFixedArrayBaseLength(handler);
Node* holder =
EmitLoadICProtoArrayCheck(p, handler, handler_length, handler_flags,
&miss, throw_reference_error_if_nonexistent);
Node* holder = EmitLoadICProtoArrayCheck(p, handler, handler_length,
handler_flags, &miss);
HandleLoadICSmiHandlerCase(p, holder, smi_handler, &miss, &direct_exit,
throw_reference_error_if_nonexistent,
kOnlyProperties);
Bind(&miss);
......@@ -1844,33 +1874,47 @@ void AccessorAssembler::LoadGlobalIC_TryPropertyCellCase(
exit_point->Return(value);
}
void AccessorAssembler::LoadGlobalIC_TryHandlerCase(const LoadICParameters* p,
void AccessorAssembler::LoadGlobalIC_TryHandlerCase(const LoadICParameters* pp,
TypeofMode typeof_mode,
ExitPoint* exit_point,
Label* miss) {
Comment("LoadGlobalIC_TryHandlerCase");
Label call_handler(this);
Label call_handler(this), non_smi(this);
Node* handler =
LoadFixedArrayElement(p->vector, p->slot, kPointerSize, SMI_PARAMETERS);
CSA_ASSERT(this, Word32BinaryNot(TaggedIsSmi(handler)));
LoadFixedArrayElement(pp->vector, pp->slot, kPointerSize, SMI_PARAMETERS);
GotoIf(WordEqual(handler, LoadRoot(Heap::kuninitialized_symbolRootIndex)),
miss);
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
GotoIfNot(TaggedIsSmi(handler), &non_smi);
bool throw_reference_error_if_nonexistent = typeof_mode == NOT_INSIDE_TYPEOF;
HandleLoadGlobalICHandlerCase(p, handler, miss, exit_point,
{
LoadICParameters p = *pp;
DCHECK_NULL(p.receiver);
Node* native_context = LoadNativeContext(p.context);
p.receiver = LoadContextElement(native_context, Context::EXTENSION_INDEX);
HandleLoadICSmiHandlerCase(&p, p.receiver, handler, miss, exit_point,
throw_reference_error_if_nonexistent,
kOnlyProperties);
}
Bind(&non_smi);
GotoIf(IsCodeMap(LoadMap(handler)), &call_handler);
HandleLoadGlobalICHandlerCase(pp, handler, miss, exit_point,
throw_reference_error_if_nonexistent);
Bind(&call_handler);
{
LoadWithVectorDescriptor descriptor(isolate());
Node* native_context = LoadNativeContext(p->context);
Node* native_context = LoadNativeContext(pp->context);
Node* receiver =
LoadContextElement(native_context, Context::EXTENSION_INDEX);
exit_point->ReturnCallStub(descriptor, handler, p->context, receiver,
p->name, p->slot, p->vector);
exit_point->ReturnCallStub(descriptor, handler, pp->context, receiver,
pp->name, pp->slot, pp->vector);
}
}
......
......@@ -139,6 +139,7 @@ class AccessorAssembler : public CodeStubAssembler {
void HandleLoadICSmiHandlerCase(const LoadICParameters* p, Node* holder,
Node* smi_handler, Label* miss,
ExitPoint* exit_point,
bool throw_reference_error_if_nonexistent,
ElementSupport support_elements);
void HandleLoadICProtoHandlerCase(const LoadICParameters* p, Node* handler,
......@@ -154,8 +155,7 @@ class AccessorAssembler : public CodeStubAssembler {
Node* EmitLoadICProtoArrayCheck(const LoadICParameters* p, Node* handler,
Node* handler_length, Node* handler_flags,
Label* miss,
bool throw_reference_error_if_nonexistent);
Label* miss);
// LoadGlobalIC implementation.
......
......@@ -200,24 +200,6 @@ void PropertyHandlerCompiler::GenerateCheckPropertyCell(
__ b(ne, miss);
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ push(name);
__ push(receiver);
__ push(holder);
__ CallRuntime(id);
}
// Generate call to api function.
void PropertyHandlerCompiler::GenerateApiAccessorCall(
MacroAssembler* masm, const CallOptimization& optimization,
......@@ -445,86 +427,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameAndConstantPoolScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex);
__ cmp(r0, scratch1());
__ b(eq, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
__ bind(&interceptor_failed);
InterceptorVectorSlotPop(holder_reg);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name(), receiver(), holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ Push(slot(), vector());
} else {
__ Push(scratch3(), scratch2()); // slot, vector
}
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
}
......
......@@ -99,22 +99,6 @@ void PropertyHandlerCompiler::GenerateCheckPropertyCell(
__ JumpIfNotRoot(scratch, Heap::kTheHoleValueRootIndex, miss);
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name, receiver, holder);
__ CallRuntime(id);
}
// Generate call to api function.
void PropertyHandlerCompiler::GenerateApiAccessorCall(
MacroAssembler* masm, const CallOptimization& optimization,
......@@ -474,87 +458,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(!AreAliased(receiver(), this->name(), scratch1(), scratch2(),
scratch3()));
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ JumpIfRoot(x0, Heap::kNoInterceptorResultSentinelRootIndex,
&interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
__ Bind(&interceptor_failed);
InterceptorVectorSlotPop(holder_reg);
if (must_preserve_receiver_reg) {
__ Pop(this->name(), holder_reg, receiver());
} else {
__ Pop(this->name(), holder_reg);
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name(), receiver(), holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ Push(slot(), vector());
} else {
__ Push(scratch3(), scratch2()); // slot, vector
}
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
}
......
......@@ -101,7 +101,15 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
GenerateTailCall(masm(), slow_stub);
}
Register reg = Frontend(name);
GenerateLoadCallback(reg, callback);
DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister()));
__ Move(ApiGetterDescriptor::HolderRegister(), reg);
// The callback is alive if this instruction is executed,
// so the weak cell is not cleared and points to data.
Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
__ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell);
CallApiGetterStub stub(isolate());
__ TailCallStub(&stub);
return GetCode(kind(), name);
}
......@@ -118,191 +126,6 @@ Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
return GetCode(kind(), name);
}
void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
if (IC::ShouldPushPopSlotAndVector(kind())) {
if (holder_reg.is(receiver())) {
PushVectorAndSlot();
} else {
DCHECK(holder_reg.is(scratch1()));
PushVectorAndSlot(scratch2(), scratch3());
}
}
}
void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
PopMode mode) {
if (IC::ShouldPushPopSlotAndVector(kind())) {
if (mode == DISCARD) {
DiscardVectorAndSlot();
} else {
if (holder_reg.is(receiver())) {
PopVectorAndSlot();
} else {
DCHECK(holder_reg.is(scratch1()));
PopVectorAndSlot(scratch2(), scratch3());
}
}
}
}
Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
LookupIterator* it) {
// So far the most popular follow ups for interceptor loads are DATA and
// AccessorInfo, so inline only them. Other cases may be added
// later.
bool inline_followup = false;
switch (it->state()) {
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::ACCESS_CHECK:
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::INTEGER_INDEXED_EXOTIC:
break;
case LookupIterator::DATA: {
PropertyDetails details = it->property_details();
inline_followup = details.kind() == kData &&
details.location() == kField &&
!it->is_dictionary_holder();
break;
}
case LookupIterator::ACCESSOR: {
Handle<Object> accessors = it->GetAccessors();
if (accessors->IsAccessorInfo()) {
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
inline_followup =
info->getter() != NULL &&
AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map());
} else if (accessors->IsAccessorPair()) {
Handle<JSObject> property_holder(it->GetHolder<JSObject>());
Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
isolate());
if (!(getter->IsJSFunction() || getter->IsFunctionTemplateInfo())) {
break;
}
if (!property_holder->HasFastProperties()) break;
CallOptimization call_optimization(getter);
Handle<Map> receiver_map = map();
inline_followup = call_optimization.is_simple_api_call() &&
call_optimization.IsCompatibleReceiverMap(
receiver_map, property_holder);
}
}
}
Label miss;
InterceptorVectorSlotPush(receiver());
bool lost_holder_register = false;
auto holder_orig = holder();
// non masking interceptors must check the entire chain, so temporarily reset
// the holder to be that last element for the FrontendHeader call.
if (holder()->GetNamedInterceptor()->non_masking()) {
DCHECK(!inline_followup);
JSObject* last = *holder();
PrototypeIterator iter(isolate(), last);
while (!iter.IsAtEnd()) {
lost_holder_register = true;
// Casting to JSObject is fine here. The LookupIterator makes sure to
// look behind non-masking interceptors during the original lookup, and
// we wouldn't try to compile a handler if there was a Proxy anywhere.
last = iter.GetCurrent<JSObject>();
iter.Advance();
}
auto last_handle = handle(last);
set_holder(last_handle);
}
Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER);
// Reset the holder so further calculations are correct.
set_holder(holder_orig);
if (lost_holder_register) {
if (*it->GetReceiver() == *holder()) {
reg = receiver();
} else {
// Reload lost holder register.
auto cell = isolate()->factory()->NewWeakCell(holder());
__ LoadWeakValue(reg, cell, &miss);
}
}
FrontendFooter(it->name(), &miss);
InterceptorVectorSlotPop(reg);
if (inline_followup) {
// TODO(368): Compile in the whole chain: all the interceptors in
// prototypes and ultimate answer.
GenerateLoadInterceptorWithFollowup(it, reg);
} else {
GenerateLoadInterceptor(reg);
}
return GetCode(kind(), it->name());
}
void NamedLoadHandlerCompiler::GenerateLoadCallback(
Register reg, Handle<AccessorInfo> callback) {
DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister()));
__ Move(ApiGetterDescriptor::HolderRegister(), reg);
// The callback is alive if this instruction is executed,
// so the weak cell is not cleared and points to data.
Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
__ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell);
CallApiGetterStub stub(isolate());
__ TailCallStub(&stub);
}
void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
LookupIterator* it, Register interceptor_reg) {
Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
Handle<Map> holder_map(holder()->map());
set_map(holder_map);
set_holder(real_named_property_holder);
Label miss;
InterceptorVectorSlotPush(interceptor_reg);
Register reg =
FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER);
FrontendFooter(it->name(), &miss);
// We discard the vector and slot now because we don't miss below this point.
InterceptorVectorSlotPop(reg, DISCARD);
switch (it->state()) {
case LookupIterator::ACCESS_CHECK:
case LookupIterator::INTERCEPTOR:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND:
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::TRANSITION:
UNREACHABLE();
case LookupIterator::DATA: {
DCHECK_EQ(kData, it->property_details().kind());
DCHECK_EQ(kField, it->property_details().location());
__ Move(LoadFieldDescriptor::ReceiverRegister(), reg);
Handle<Object> smi_handler =
LoadIC::SimpleFieldLoad(isolate(), it->GetFieldIndex());
__ Move(LoadFieldDescriptor::SmiHandlerRegister(), smi_handler);
GenerateTailCall(masm(), isolate()->builtins()->LoadField());
break;
}
case LookupIterator::ACCESSOR:
if (it->GetAccessors()->IsAccessorInfo()) {
Handle<AccessorInfo> info =
Handle<AccessorInfo>::cast(it->GetAccessors());
DCHECK_NOT_NULL(info->getter());
GenerateLoadCallback(reg, info);
} else {
Handle<Object> function = handle(
AccessorPair::cast(*it->GetAccessors())->getter(), isolate());
CallOptimization call_optimization(function);
GenerateApiAccessorCall(masm(), call_optimization, holder_map,
receiver(), scratch2(), false, no_reg, reg,
it->GetAccessorIndex());
}
}
}
Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
Handle<Name> name, int accessor_index, int expected_arguments) {
Register holder = Frontend(name);
......
......@@ -137,11 +137,6 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler {
const CallOptimization& call_optimization,
int accessor_index, Handle<Code> slow_stub);
// The LookupIterator is used to perform a lookup behind the interceptor. If
// the iterator points to a LookupIterator::PROPERTY, its access will be
// inlined.
Handle<Code> CompileLoadInterceptor(LookupIterator* it);
Handle<Code> CompileLoadViaGetter(Handle<Name> name, int accessor_index,
int expected_arguments);
......@@ -158,15 +153,6 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler {
no_reg);
}
// These constants describe the structure of the interceptor arguments on the
// stack. The arguments are pushed by the (platform-specific)
// PushInterceptorArguments and read by LoadPropertyWithInterceptorOnly and
// LoadWithInterceptor.
static const int kInterceptorArgsNameIndex = 0;
static const int kInterceptorArgsThisIndex = 1;
static const int kInterceptorArgsHolderIndex = 2;
static const int kInterceptorArgsLength = 3;
protected:
virtual Register FrontendHeader(Register object_reg, Handle<Name> name,
Label* miss, ReturnHolder return_what);
......@@ -174,18 +160,6 @@ class NamedLoadHandlerCompiler : public PropertyHandlerCompiler {
virtual void FrontendFooter(Handle<Name> name, Label* miss);
private:
void GenerateLoadCallback(Register reg, Handle<AccessorInfo> callback);
// Helper emits no code if vector-ics are disabled.
void InterceptorVectorSlotPush(Register holder_reg);
enum PopMode { POP, DISCARD };
void InterceptorVectorSlotPop(Register holder_reg, PopMode mode = POP);
void GenerateLoadInterceptor(Register holder_reg);
void GenerateLoadInterceptorWithFollowup(LookupIterator* it,
Register holder_reg);
void GenerateLoadPostInterceptor(LookupIterator* it, Register reg);
Register scratch3() { return registers_[4]; }
};
......
......@@ -13,68 +13,68 @@
namespace v8 {
namespace internal {
Handle<Object> LoadHandler::LoadNormal(Isolate* isolate) {
int config = KindBits::encode(kForNormal);
Handle<Smi> LoadHandler::LoadNormal(Isolate* isolate) {
int config = KindBits::encode(kNormal);
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::LoadField(Isolate* isolate,
FieldIndex field_index) {
int config = KindBits::encode(kForFields) |
Handle<Smi> LoadHandler::LoadInterceptor(Isolate* isolate) {
int config = KindBits::encode(kInterceptor);
return handle(Smi::FromInt(config), isolate);
}
Handle<Smi> LoadHandler::LoadField(Isolate* isolate, FieldIndex field_index) {
int config = KindBits::encode(kField) |
IsInobjectBits::encode(field_index.is_inobject()) |
IsDoubleBits::encode(field_index.is_double()) |
FieldOffsetBits::encode(field_index.offset());
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::LoadConstant(Isolate* isolate, int descriptor) {
int config = KindBits::encode(kForConstants) |
IsAccessorInfoBits::encode(false) |
Handle<Smi> LoadHandler::LoadConstant(Isolate* isolate, int descriptor) {
int config = KindBits::encode(kConstant) | IsAccessorInfoBits::encode(false) |
DescriptorBits::encode(descriptor);
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::LoadApiGetter(Isolate* isolate, int descriptor) {
int config = KindBits::encode(kForConstants) |
IsAccessorInfoBits::encode(true) |
Handle<Smi> LoadHandler::LoadApiGetter(Isolate* isolate, int descriptor) {
int config = KindBits::encode(kConstant) | IsAccessorInfoBits::encode(true) |
DescriptorBits::encode(descriptor);
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::EnableAccessCheckOnReceiver(
Isolate* isolate, Handle<Object> smi_handler) {
int config = Smi::cast(*smi_handler)->value();
Handle<Smi> LoadHandler::EnableAccessCheckOnReceiver(Isolate* isolate,
Handle<Smi> smi_handler) {
int config = smi_handler->value();
#ifdef DEBUG
Kind kind = KindBits::decode(config);
DCHECK_NE(kForElements, kind);
DCHECK_NE(kElement, kind);
#endif
config = DoAccessCheckOnReceiverBits::update(config, true);
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::EnableLookupOnReceiver(Isolate* isolate,
Handle<Object> smi_handler) {
int config = Smi::cast(*smi_handler)->value();
Handle<Smi> LoadHandler::EnableLookupOnReceiver(Isolate* isolate,
Handle<Smi> smi_handler) {
int config = smi_handler->value();
#ifdef DEBUG
Kind kind = KindBits::decode(config);
DCHECK_NE(kForElements, kind);
DCHECK_NE(kElement, kind);
#endif
config = LookupOnReceiverBits::update(config, true);
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::LoadNonExistent(Isolate* isolate,
bool do_lookup_on_receiver) {
int config = KindBits::encode(kForNonExistent) |
LookupOnReceiverBits::encode(do_lookup_on_receiver);
Handle<Smi> LoadHandler::LoadNonExistent(Isolate* isolate) {
int config = KindBits::encode(kNonExistent);
return handle(Smi::FromInt(config), isolate);
}
Handle<Object> LoadHandler::LoadElement(Isolate* isolate,
ElementsKind elements_kind,
bool convert_hole_to_undefined,
bool is_js_array) {
int config = KindBits::encode(kForElements) |
Handle<Smi> LoadHandler::LoadElement(Isolate* isolate,
ElementsKind elements_kind,
bool convert_hole_to_undefined,
bool is_js_array) {
int config = KindBits::encode(kElement) |
ElementsKindBits::encode(elements_kind) |
ConvertHoleBits::encode(convert_hole_to_undefined) |
IsJsArrayBits::encode(is_js_array);
......
......@@ -17,24 +17,24 @@ namespace internal {
class LoadHandler {
public:
enum Kind {
kForElements,
kForNormal,
kForFields,
kForConstants,
kForNonExistent
kElement,
kNormal,
kField,
kConstant,
kInterceptor,
kNonExistent
};
class KindBits : public BitField<Kind, 0, 3> {};
// Defines whether access rights check should be done on receiver object.
// Applicable to kForFields, kForConstants and kForNonExistent kinds only when
// loading value from prototype chain. Ignored when loading from holder.
// Applicable to named property kinds only when loading value from prototype
// chain. Ignored when loading from holder.
class DoAccessCheckOnReceiverBits
: public BitField<bool, KindBits::kNext, 1> {};
// Defines whether a lookup should be done on receiver object before
// proceeding to the prototype chain. Applicable to kForFields, kForConstants
// and kForNonExistent kinds only when loading value from prototype chain.
// Ignored when loading from holder.
// proceeding to the prototype chain. Applicable to named property kinds only
// when loading value from prototype chain. Ignored when loading from holder.
class LookupOnReceiverBits
: public BitField<bool, DoAccessCheckOnReceiverBits::kNext, 1> {};
......@@ -51,7 +51,7 @@ class LoadHandler {
STATIC_ASSERT(DescriptorBits::kNext <= kSmiValueSize);
//
// Encoding when KindBits contains kForFields.
// Encoding when KindBits contains kField.
//
class IsInobjectBits : public BitField<bool, LookupOnReceiverBits::kNext, 1> {
};
......@@ -64,7 +64,7 @@ class LoadHandler {
STATIC_ASSERT(FieldOffsetBits::kNext <= kSmiValueSize);
//
// Encoding when KindBits contains kForElements.
// Encoding when KindBits contains kElement.
//
class IsJsArrayBits : public BitField<bool, KindBits::kNext, 1> {};
class ConvertHoleBits : public BitField<bool, IsJsArrayBits::kNext, 1> {};
......@@ -89,38 +89,40 @@ class LoadHandler {
static const int kFirstPrototypeIndex = 3;
// Creates a Smi-handler for loading a property from a slow object.
static inline Handle<Object> LoadNormal(Isolate* isolate);
static inline Handle<Smi> LoadNormal(Isolate* isolate);
// Creates a Smi-handler for loading a property from an object with an
// interceptor.
static inline Handle<Smi> LoadInterceptor(Isolate* isolate);
// Creates a Smi-handler for loading a field from fast object.
static inline Handle<Object> LoadField(Isolate* isolate,
FieldIndex field_index);
static inline Handle<Smi> LoadField(Isolate* isolate, FieldIndex field_index);
// Creates a Smi-handler for loading a constant from fast object.
static inline Handle<Object> LoadConstant(Isolate* isolate, int descriptor);
static inline Handle<Smi> LoadConstant(Isolate* isolate, int descriptor);
// Creates a Smi-handler for loading an Api getter property from fast object.
static inline Handle<Object> LoadApiGetter(Isolate* isolate, int descriptor);
static inline Handle<Smi> LoadApiGetter(Isolate* isolate, int descriptor);
// Sets DoAccessCheckOnReceiverBits in given Smi-handler. The receiver
// check is a part of a prototype chain check.
static inline Handle<Object> EnableAccessCheckOnReceiver(
Isolate* isolate, Handle<Object> smi_handler);
static inline Handle<Smi> EnableAccessCheckOnReceiver(
Isolate* isolate, Handle<Smi> smi_handler);
// Sets LookupOnReceiverBits in given Smi-handler. The receiver
// check is a part of a prototype chain check.
static inline Handle<Object> EnableLookupOnReceiver(
Isolate* isolate, Handle<Object> smi_handler);
static inline Handle<Smi> EnableLookupOnReceiver(Isolate* isolate,
Handle<Smi> smi_handler);
// Creates a Smi-handler for loading a non-existent property. Works only as
// a part of prototype chain check.
static inline Handle<Object> LoadNonExistent(Isolate* isolate,
bool do_lookup_on_receiver);
static inline Handle<Smi> LoadNonExistent(Isolate* isolate);
// Creates a Smi-handler for loading an element.
static inline Handle<Object> LoadElement(Isolate* isolate,
ElementsKind elements_kind,
bool convert_hole_to_undefined,
bool is_js_array);
static inline Handle<Smi> LoadElement(Isolate* isolate,
ElementsKind elements_kind,
bool convert_hole_to_undefined,
bool is_js_array);
};
// A set of bit fields representing Smi handlers for stores.
......
......@@ -287,23 +287,6 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
}
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ push(name);
__ push(receiver);
__ push(holder);
__ CallRuntime(id);
}
#undef __
#define __ ACCESS_MASM(masm())
......@@ -445,102 +428,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ push(receiver());
}
__ push(holder_reg);
__ push(this->name());
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ cmp(eax, factory()->no_interceptor_result_sentinel());
__ j(equal, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ ret(0);
// Clobber registers when generating debug-code to provoke errors.
__ bind(&interceptor_failed);
if (FLAG_debug_code) {
__ mov(receiver(), Immediate(bit_cast<int32_t>(kZapValue)));
__ mov(holder_reg, Immediate(bit_cast<int32_t>(kZapValue)));
__ mov(this->name(), Immediate(bit_cast<int32_t>(kZapValue)));
}
InterceptorVectorSlotPop(holder_reg);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Call the runtime system to load the interceptor.
// Stack:
// return address
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ push(receiver());
__ push(holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ push(slot());
__ push(vector());
} else {
__ push(scratch3()); // slot
__ push(scratch2()); // vector
}
__ push(Operand(esp, 4 * kPointerSize)); // return address
__ mov(Operand(esp, 5 * kPointerSize), name());
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
// Zap register aliases of the arguments passed on the stack to ensure they
// are properly loaded by the handler (debug-only).
......
......@@ -869,7 +869,7 @@ void IC::PatchCache(Handle<Name> name, Handle<Object> handler) {
}
}
Handle<Object> LoadIC::SimpleFieldLoad(Isolate* isolate, FieldIndex index) {
Handle<Smi> LoadIC::SimpleFieldLoad(Isolate* isolate, FieldIndex index) {
TRACE_HANDLER_STATS(isolate, LoadIC_LoadFieldDH);
return LoadHandler::LoadField(isolate, index);
}
......@@ -967,12 +967,13 @@ int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map,
Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
Handle<JSObject> holder,
Handle<Name> name,
Handle<Object> smi_handler) {
Handle<Smi> smi_handler) {
int checks_count =
GetPrototypeCheckCount(isolate(), receiver_map, holder, name);
DCHECK_LE(0, checks_count);
if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) {
if (receiver_map->IsPrimitiveMap() ||
receiver_map->is_access_check_needed()) {
DCHECK(!receiver_map->is_dictionary_map());
DCHECK_LE(1, checks_count); // For native context.
smi_handler =
......@@ -1003,43 +1004,45 @@ Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
return handler_array;
}
Handle<Object> LoadIC::LoadNonExistent(Handle<Map> receiver_map,
Handle<Name> name) {
Handle<JSObject> holder; // null handle
int checks_count =
GetPrototypeCheckCount(isolate(), receiver_map, holder, name);
Handle<Object> LoadIC::LoadFullChain(Handle<Map> receiver_map,
Handle<Object> holder, Handle<Name> name,
Handle<Smi> smi_handler) {
Handle<JSObject> end; // null handle
int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, end, name);
DCHECK_LE(0, checks_count);
bool do_negative_lookup_on_receiver =
receiver_map->is_dictionary_map() && !receiver_map->IsJSGlobalObjectMap();
Handle<Object> smi_handler =
LoadHandler::LoadNonExistent(isolate(), do_negative_lookup_on_receiver);
if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) {
if (receiver_map->IsPrimitiveMap() ||
receiver_map->is_access_check_needed()) {
DCHECK(!receiver_map->is_dictionary_map());
DCHECK_LE(1, checks_count); // For native context.
smi_handler =
LoadHandler::EnableAccessCheckOnReceiver(isolate(), smi_handler);
} else if (receiver_map->is_dictionary_map() &&
!receiver_map->IsJSGlobalObjectMap()) {
smi_handler = LoadHandler::EnableLookupOnReceiver(isolate(), smi_handler);
}
Handle<Object> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
if (validity_cell.is_null()) {
DCHECK_EQ(0, checks_count);
validity_cell = handle(Smi::FromInt(0), isolate());
// Lookup on receiver isn't supported in case of a simple smi handler.
if (!LoadHandler::LookupOnReceiverBits::decode(smi_handler->value())) {
return smi_handler;
}
validity_cell = handle(Smi::kZero, isolate());
}
Factory* factory = isolate()->factory();
if (checks_count == 0) {
return factory->NewTuple3(factory->null_value(), smi_handler,
validity_cell);
return factory->NewTuple3(holder, smi_handler, validity_cell);
}
Handle<FixedArray> handler_array(factory->NewFixedArray(
LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
handler_array->set(LoadHandler::kSmiHandlerIndex, *smi_handler);
handler_array->set(LoadHandler::kValidityCellIndex, *validity_cell);
handler_array->set(LoadHandler::kHolderCellIndex, *factory->null_value());
InitPrototypeChecks(isolate(), receiver_map, holder, name, handler_array,
handler_array->set(LoadHandler::kHolderCellIndex, *holder);
InitPrototypeChecks(isolate(), receiver_map, end, name, handler_array,
LoadHandler::kFirstPrototypeIndex);
return handler_array;
}
......@@ -1099,7 +1102,9 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
code = slow_stub();
} else if (!lookup->IsFound()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH);
code = LoadNonExistent(receiver_map(), lookup->name());
Handle<Smi> smi_handler = LoadHandler::LoadNonExistent(isolate());
code = LoadFullChain(receiver_map(), isolate()->factory()->null_value(),
lookup->name(), smi_handler);
} else {
if (IsLoadGlobalIC() && lookup->state() == LookupIterator::DATA &&
lookup->GetReceiver().is_identical_to(lookup->GetHolder<Object>())) {
......@@ -1304,8 +1309,26 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
bool receiver_is_holder = receiver.is_identical_to(holder);
switch (lookup->state()) {
case LookupIterator::INTERCEPTOR:
break; // Custom-compiled handler.
case LookupIterator::INTERCEPTOR: {
Handle<Smi> smi_handler = LoadHandler::LoadInterceptor(isolate());
if (holder->GetNamedInterceptor()->non_masking()) {
Handle<Object> holder_ref = isolate()->factory()->null_value();
if (!receiver_is_holder) {
holder_ref = Map::GetOrCreatePrototypeWeakCell(holder, isolate());
}
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonMaskingInterceptorDH);
return LoadFullChain(map, holder_ref, lookup->name(), smi_handler);
}
if (receiver_is_holder) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptorDH);
return smi_handler;
}
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptorFromPrototypeDH);
return LoadFromPrototype(map, holder, lookup->name(), smi_handler);
}
case LookupIterator::ACCESSOR: {
// Use simple field loads for some well-known callback properties.
......@@ -1345,7 +1368,7 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub);
return slow_stub();
}
Handle<Object> smi_handler =
Handle<Smi> smi_handler =
LoadHandler::LoadApiGetter(isolate(), lookup->GetAccessorIndex());
if (receiver_is_holder) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterDH);
......@@ -1364,7 +1387,7 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
case LookupIterator::DATA: {
DCHECK_EQ(kData, lookup->property_details().kind());
Handle<Object> smi_handler;
Handle<Smi> smi_handler;
if (lookup->is_dictionary_holder()) {
if (holder->IsJSGlobalObject()) {
break; // Custom-compiled handler.
......@@ -1427,17 +1450,9 @@ Handle<Object> LoadIC::CompileHandler(LookupIterator* lookup,
Handle<Map> map = receiver_map();
switch (lookup->state()) {
case LookupIterator::INTERCEPTOR: {
DCHECK(!holder->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptor);
NamedLoadHandlerCompiler compiler(isolate(), map, holder, cache_holder);
// Perform a lookup behind the interceptor. Copy the LookupIterator since
// the original iterator will be used to fetch the value.
LookupIterator it = *lookup;
it.Next();
LookupForRead(&it);
return compiler.CompileLoadInterceptor(&it);
}
case LookupIterator::INTERCEPTOR:
UNREACHABLE();
break;
case LookupIterator::ACCESSOR: {
#ifdef DEBUG
......@@ -3055,57 +3070,16 @@ RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) {
}
/**
* Attempts to load a property with an interceptor (which must be present),
* but doesn't search the prototype chain.
*
* Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't
* provide any value for the given name.
*/
RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptorOnly) {
DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength);
Handle<Name> name =
args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex);
Handle<Object> receiver =
args.at(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex);
Handle<JSObject> holder =
args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex);
HandleScope scope(isolate);
if (!receiver->IsJSReceiver()) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, receiver, Object::ConvertReceiver(isolate, receiver));
}
InterceptorInfo* interceptor = holder->GetNamedInterceptor();
PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver,
*holder, Object::DONT_THROW);
v8::GenericNamedPropertyGetterCallback getter =
v8::ToCData<v8::GenericNamedPropertyGetterCallback>(
interceptor->getter());
Handle<Object> result = arguments.Call(getter, name);
RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
if (!result.is_null()) return *result;
return isolate->heap()->no_interceptor_result_sentinel();
}
/**
* Loads a property with an interceptor performing post interceptor
* lookup if interceptor failed.
*/
RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) {
HandleScope scope(isolate);
DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength + 2);
Handle<Name> name =
args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex);
Handle<Object> receiver =
args.at(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex);
Handle<JSObject> holder =
args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex);
DCHECK_EQ(5, args.length());
Handle<Name> name = args.at<Name>(0);
Handle<Object> receiver = args.at(1);
Handle<JSObject> holder = args.at<JSObject>(2);
if (!receiver->IsJSReceiver()) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
......
......@@ -311,17 +311,20 @@ class LoadIC : public IC {
private:
// Creates a data handler that represents a load of a field by given index.
static Handle<Object> SimpleFieldLoad(Isolate* isolate, FieldIndex index);
static Handle<Smi> SimpleFieldLoad(Isolate* isolate, FieldIndex index);
// Creates a data handler that represents a prototype chain check followed
// by given Smi-handler that encoded a load from the holder.
// Can be used only if GetPrototypeCheckCount() returns non negative value.
Handle<Object> LoadFromPrototype(Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<Name> name,
Handle<Object> smi_handler);
Handle<Smi> smi_handler);
// Creates a data handler that represents a load of a non-existent property.
Handle<Object> LoadNonExistent(Handle<Map> receiver_map, Handle<Name> name);
// {holder} is the object from which the property is loaded. If no holder is
// needed (e.g., for "nonexistent"), null_value() may be passed in.
Handle<Object> LoadFullChain(Handle<Map> receiver_map, Handle<Object> holder,
Handle<Name> name, Handle<Smi> smi_handler);
friend class IC;
friend class NamedLoadHandlerCompiler;
......
......@@ -190,22 +190,6 @@ void PropertyHandlerCompiler::GenerateCheckPropertyCell(
__ Branch(miss, ne, scratch, Operand(at));
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name, receiver, holder);
__ CallRuntime(id);
}
// Generate call to api function.
void PropertyHandlerCompiler::GenerateApiAccessorCall(
MacroAssembler* masm, const CallOptimization& optimization,
......@@ -428,85 +412,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method).
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex);
__ Branch(&interceptor_failed, eq, v0, Operand(scratch1()));
frame_scope.GenerateLeaveFrame();
__ Ret();
__ bind(&interceptor_failed);
InterceptorVectorSlotPop(holder_reg);
if (must_preserve_receiver_reg) {
__ Pop(receiver(), holder_reg, this->name());
} else {
__ Pop(holder_reg, this->name());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name(), receiver(), holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ Push(slot(), vector());
} else {
__ Push(scratch3(), scratch2()); // slot, vector
}
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
}
......
......@@ -190,22 +190,6 @@ void PropertyHandlerCompiler::GenerateCheckPropertyCell(
__ Branch(miss, ne, scratch, Operand(at));
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name, receiver, holder);
__ CallRuntime(id);
}
// Generate call to api function.
void PropertyHandlerCompiler::GenerateApiAccessorCall(
MacroAssembler* masm, const CallOptimization& optimization,
......@@ -428,85 +412,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method).
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex);
__ Branch(&interceptor_failed, eq, v0, Operand(scratch1()));
frame_scope.GenerateLeaveFrame();
__ Ret();
__ bind(&interceptor_failed);
InterceptorVectorSlotPop(holder_reg);
if (must_preserve_receiver_reg) {
__ Pop(receiver(), holder_reg, this->name());
} else {
__ Pop(holder_reg, this->name());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name(), receiver(), holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ Push(slot(), vector());
} else {
__ Push(scratch3(), scratch2()); // slot, vector
}
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
}
......
......@@ -195,22 +195,6 @@ void PropertyHandlerCompiler::GenerateCheckPropertyCell(
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name, receiver, holder);
__ CallRuntime(id);
}
// Generate call to api function.
void PropertyHandlerCompiler::GenerateApiAccessorCall(
MacroAssembler* masm, const CallOptimization& optimization,
......@@ -443,86 +427,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameAndConstantPoolScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex);
__ cmp(r3, scratch1());
__ beq(&interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
__ bind(&interceptor_failed);
InterceptorVectorSlotPop(holder_reg);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name(), receiver(), holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ Push(slot(), vector());
} else {
__ Push(scratch3(), scratch2()); // slot, vector
}
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
}
......
......@@ -186,21 +186,6 @@ void PropertyHandlerCompiler::GenerateCheckPropertyCell(
__ bne(miss);
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name, receiver, holder);
__ CallRuntime(id);
}
// Generate call to api function.
void PropertyHandlerCompiler::GenerateApiAccessorCall(
MacroAssembler* masm, const CallOptimization& optimization,
......@@ -425,84 +410,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, this->name());
}
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ CompareRoot(r2, Heap::kNoInterceptorResultSentinelRootIndex);
__ beq(&interceptor_failed, Label::kNear);
frame_scope.GenerateLeaveFrame();
__ Ret();
__ bind(&interceptor_failed);
InterceptorVectorSlotPop(holder_reg);
__ Pop(this->name());
__ Pop(holder_reg);
if (must_preserve_receiver_reg) {
__ Pop(receiver());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name(), receiver(), holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ Push(slot(), vector());
} else {
__ Push(scratch3(), scratch2()); // slot, vector
}
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
}
......
......@@ -83,24 +83,6 @@ void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
__ DecrementCounter(counters->negative_lookups_miss(), 1);
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(name);
__ Push(receiver);
__ Push(holder);
__ CallRuntime(id);
}
// Generate call to api function.
void PropertyHandlerCompiler::GenerateApiAccessorCall(
MacroAssembler* masm, const CallOptimization& optimization,
......@@ -437,96 +419,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver());
}
__ Push(holder_reg);
__ Push(this->name());
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
__ j(equal, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ ret(0);
__ bind(&interceptor_failed);
InterceptorVectorSlotPop(holder_reg);
__ Pop(this->name());
__ Pop(holder_reg);
if (must_preserve_receiver_reg) {
__ Pop(receiver());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
// Call the runtime system to load the interceptor.
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Stack:
// return address
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ Push(receiver());
__ Push(holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ Push(slot());
__ Push(vector());
} else {
__ Push(scratch3()); // slot
__ Push(scratch2()); // vector
}
__ Push(Operand(rsp, 4 * kPointerSize)); // return address
__ movp(Operand(rsp, 5 * kPointerSize), name());
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
}
......
......@@ -287,23 +287,6 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
}
}
static void CompileCallLoadPropertyWithInterceptor(
MacroAssembler* masm, Register receiver, Register holder, Register name,
Handle<JSObject> holder_obj, Runtime::FunctionId id) {
DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
Runtime::FunctionForId(id)->nargs);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ push(name);
__ push(receiver);
__ push(holder);
__ CallRuntime(id);
}
#undef __
#define __ ACCESS_MASM(masm())
......@@ -445,102 +428,6 @@ void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
}
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
LookupIterator* it, Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Compile the interceptor call, followed by inline code to load the
// property from further up the prototype chain if the call fails.
// Check that the maps haven't changed.
DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
// Preserve the receiver register explicitly whenever it is different from the
// holder and it is needed should the interceptor return without any result.
// The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
// case might cause a miss during the prototype check.
bool must_perform_prototype_check =
!holder().is_identical_to(it->GetHolder<JSObject>());
bool must_preserve_receiver_reg =
!receiver().is(holder_reg) &&
(it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
// Save necessary data before invoking an interceptor.
// Requires a frame to make GC aware of pushed pointers.
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ push(receiver());
}
__ push(holder_reg);
__ push(this->name());
InterceptorVectorSlotPush(holder_reg);
// Invoke an interceptor. Note: map checks from receiver to
// interceptor's holder has been compiled before (see a caller
// of this method.)
CompileCallLoadPropertyWithInterceptor(
masm(), receiver(), holder_reg, this->name(), holder(),
Runtime::kLoadPropertyWithInterceptorOnly);
// Check if interceptor provided a value for property. If it's
// the case, return immediately.
Label interceptor_failed;
__ cmp(eax, factory()->no_interceptor_result_sentinel());
__ j(equal, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ ret(0);
// Clobber registers when generating debug-code to provoke errors.
__ bind(&interceptor_failed);
if (FLAG_debug_code) {
__ mov(receiver(), Immediate(bit_cast<int32_t>(kZapValue)));
__ mov(holder_reg, Immediate(bit_cast<int32_t>(kZapValue)));
__ mov(this->name(), Immediate(bit_cast<int32_t>(kZapValue)));
}
InterceptorVectorSlotPop(holder_reg);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver());
}
// Leave the internal frame.
}
GenerateLoadPostInterceptor(it, holder_reg);
}
void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
DCHECK(holder()->HasNamedInterceptor());
DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
// Call the runtime system to load the interceptor.
// Stack:
// return address
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
__ push(receiver());
__ push(holder_reg);
// See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
if (holder_reg.is(receiver())) {
__ push(slot());
__ push(vector());
} else {
__ push(scratch3()); // slot
__ push(scratch2()); // vector
}
__ push(Operand(esp, 4 * kPointerSize)); // return address
__ mov(Operand(esp, 5 * kPointerSize), name());
__ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
}
void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
// Zap register aliases of the arguments passed on the stack to ensure they
// are properly loaded by the handler (debug-only).
......
......@@ -694,8 +694,6 @@ void Oddball::OddballVerify() {
this == heap->false_value());
} else if (map() == heap->uninitialized_map()) {
CHECK(this == heap->uninitialized_value());
} else if (map() == heap->no_interceptor_result_sentinel_map()) {
CHECK(this == heap->no_interceptor_result_sentinel());
} else if (map() == heap->arguments_marker_map()) {
CHECK(this == heap->arguments_marker());
} else if (map() == heap->termination_exception_map()) {
......
......@@ -658,7 +658,6 @@ namespace internal {
F(LoadGlobalIC_Slow, 3, 1) \
F(LoadIC_Miss, 4, 1) \
F(LoadPropertyWithInterceptor, 5, 1) \
F(LoadPropertyWithInterceptorOnly, 3, 1) \
F(StoreCallbackProperty, 6, 1) \
F(StoreIC_Miss, 5, 1) \
F(StorePropertyWithInterceptor, 5, 1) \
......
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