Currently this mostly just moves code around.

- Later the different parts will become individual code objects that tailcall into one another.
- The mapcheck/symbol check still needs to be separated from the overall prototype-chain check so that the ICs can reuse the handlers.

Review URL: https://chromiumcodereview.appspot.com/12209021

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13768 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
verwaest@chromium.org 2013-02-27 15:28:53 +00:00
parent 574dc38f96
commit 3ab255f55d
7 changed files with 580 additions and 910 deletions

View File

@ -1168,126 +1168,124 @@ Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
}
void StubCompiler::GenerateLoadField(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
PropertyIndex index,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
void BaseLoadStubCompiler::HandlerFrontendFooter(Label* success,
Label* miss) {
__ b(success);
__ bind(miss);
GenerateLoadMiss(masm(), kind());
}
// Check that the maps haven't changed.
Register reg = CheckPrototypes(
object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
Register BaseLoadStubCompiler::CallbackHandlerFrontend(
Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* success,
FrontendCheckType check,
Handle<ExecutableAccessorInfo> callback) {
Label miss;
Register reg = HandlerFrontendHeader(
object, object_reg, holder, name, &miss, check);
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
ASSERT(!reg.is(scratch2()));
ASSERT(!reg.is(scratch3()));
ASSERT(!reg.is(scratch4()));
// Load the properties dictionary.
Register dictionary = scratch4();
__ ldr(dictionary, FieldMemOperand(reg, JSObject::kPropertiesOffset));
// Probe the dictionary.
Label probe_done;
StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
&miss,
&probe_done,
dictionary,
this->name(),
scratch2(),
scratch3());
__ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch3 contains the
// pointer into the dictionary. Check that the value is the callback.
Register pointer = scratch3();
const int kElementsStartOffset = StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ ldr(scratch2(), FieldMemOperand(pointer, kValueOffset));
__ cmp(scratch2(), Operand(callback));
__ b(ne, &miss);
}
HandlerFrontendFooter(success, &miss);
return reg;
}
void BaseLoadStubCompiler::NonexistentHandlerFrontend(
Handle<JSObject> object,
Handle<JSObject> last,
Handle<String> name,
Label* success,
Handle<GlobalObject> global) {
Label miss;
Register reg = HandlerFrontendHeader(
object, receiver(), last, name, &miss, PERFORM_INITIAL_CHECKS);
// If the last object in the prototype chain is a global object,
// check that the global property cell is empty.
if (!global.is_null()) {
GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss);
}
if (!last->HasFastProperties()) {
__ ldr(scratch2(), FieldMemOperand(reg, HeapObject::kMapOffset));
__ ldr(scratch2(), FieldMemOperand(scratch2(), Map::kPrototypeOffset));
__ cmp(scratch2(), Operand(isolate()->factory()->null_value()));
__ b(ne, &miss);
}
HandlerFrontendFooter(success, &miss);
}
void BaseLoadStubCompiler::GenerateLoadField(Register reg,
Handle<JSObject> holder,
PropertyIndex index) {
GenerateFastPropertyLoad(masm(), r0, reg, holder, index);
__ Ret();
}
void StubCompiler::GenerateLoadConstant(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<JSFunction> value,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// Check that the maps haven't changed.
CheckPrototypes(
object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
void BaseLoadStubCompiler::GenerateLoadConstant(Handle<JSFunction> value) {
// Return the constant value.
__ LoadHeapObject(r0, value);
__ Ret();
}
void StubCompiler::GenerateDictionaryLoadCallback(
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss) {
ASSERT(!receiver.is(scratch1));
ASSERT(!receiver.is(scratch2));
ASSERT(!receiver.is(scratch3));
// Load the properties dictionary.
Register dictionary = scratch1;
__ ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
// Probe the dictionary.
Label probe_done;
StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
miss,
&probe_done,
dictionary,
name_reg,
scratch2,
scratch3);
__ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch3 contains the
// pointer into the dictionary. Check that the value is the callback.
Register pointer = scratch3;
const int kElementsStartOffset = StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ ldr(scratch2, FieldMemOperand(pointer, kValueOffset));
__ cmp(scratch2, Operand(callback));
__ b(ne, miss);
}
void StubCompiler::GenerateLoadCallback(
Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Register scratch4,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// Check that the maps haven't changed.
Register reg = CheckPrototypes(object, receiver, holder, scratch1,
scratch2, scratch3, name, miss);
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
GenerateDictionaryLoadCallback(
reg, name_reg, scratch2, scratch3, scratch4, callback, name, miss);
}
void BaseLoadStubCompiler::GenerateLoadCallback(
Register reg,
Handle<ExecutableAccessorInfo> callback) {
// Build AccessorInfo::args_ list on the stack and push property name below
// the exit frame to make GC aware of them and store pointers to them.
__ push(receiver);
__ mov(scratch2, sp); // scratch2 = AccessorInfo::args_
__ push(receiver());
__ mov(scratch2(), sp); // scratch2 = AccessorInfo::args_
if (heap()->InNewSpace(callback->data())) {
__ Move(scratch3, callback);
__ ldr(scratch3,
FieldMemOperand(scratch3, ExecutableAccessorInfo::kDataOffset));
__ Move(scratch3(), callback);
__ ldr(scratch3(), FieldMemOperand(scratch3(),
ExecutableAccessorInfo::kDataOffset));
} else {
__ Move(scratch3, Handle<Object>(callback->data(), isolate()));
__ Move(scratch3(), Handle<Object>(callback->data(),
callback->GetIsolate()));
}
__ Push(reg, scratch3);
__ mov(scratch3, Operand(ExternalReference::isolate_address()));
__ Push(scratch3, name_reg);
__ Push(reg, scratch3());
__ mov(scratch3(), Operand(ExternalReference::isolate_address()));
__ Push(scratch3(), name());
__ mov(r0, sp); // r0 = Handle<String>
const int kApiStackSpace = 1;
@ -1296,7 +1294,7 @@ void StubCompiler::GenerateLoadCallback(
// Create AccessorInfo instance on the stack above the exit frame with
// scratch2 (internal::Object** args_) as the data.
__ str(scratch2, MemOperand(sp, 1 * kPointerSize));
__ str(scratch2(), MemOperand(sp, 1 * kPointerSize));
__ add(r1, sp, Operand(1 * kPointerSize)); // r1 = AccessorInfo&
const int kStackUnwindSpace = 5;
@ -1310,22 +1308,15 @@ void StubCompiler::GenerateLoadCallback(
}
void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
Handle<JSObject> interceptor_holder,
LookupResult* lookup,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<String> name,
Label* miss) {
void BaseLoadStubCompiler::GenerateLoadInterceptor(
Register holder_reg,
Handle<JSObject> object,
Handle<JSObject> interceptor_holder,
LookupResult* lookup,
Handle<String> name) {
ASSERT(interceptor_holder->HasNamedInterceptor());
ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
// later.
@ -1346,17 +1337,14 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
// 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.
Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
scratch1, scratch2, scratch3,
name, miss);
ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
ASSERT(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 CALLBACKS case needs the receiver to be passed into C++ code,
// the FIELD case might cause a miss during the prototype check.
bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
bool must_preserve_receiver_reg = !receiver.is(holder_reg) &&
bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
(lookup->type() == CALLBACKS || must_perfrom_prototype_check);
// Save necessary data before invoking an interceptor.
@ -1364,93 +1352,42 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
{
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ Push(receiver, holder_reg, name_reg);
__ Push(receiver(), holder_reg, this->name());
} else {
__ Push(holder_reg, name_reg);
__ Push(holder_reg, this->name());
}
// 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,
receiver(),
holder_reg,
name_reg,
this->name(),
interceptor_holder);
// 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);
__ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex);
__ cmp(r0, scratch1());
__ b(eq, &interceptor_failed);
frame_scope.GenerateLeaveFrame();
__ Ret();
__ bind(&interceptor_failed);
__ pop(name_reg);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver);
__ pop(receiver());
}
// Leave the internal frame.
}
// Check that the maps from interceptor's holder to lookup's holder
// haven't changed. And load lookup's holder into |holder| register.
if (must_perfrom_prototype_check) {
holder_reg = CheckPrototypes(interceptor_holder,
holder_reg,
Handle<JSObject>(lookup->holder()),
scratch1,
scratch2,
scratch3,
name,
miss);
}
if (lookup->IsField()) {
// We found FIELD property in prototype chain of interceptor's holder.
// Retrieve a field from field's holder.
GenerateFastPropertyLoad(masm(), r0, holder_reg,
Handle<JSObject>(lookup->holder()),
lookup->GetFieldIndex());
__ Ret();
} else {
// We found CALLBACKS property in prototype chain of interceptor's
// holder.
ASSERT(lookup->type() == CALLBACKS);
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
ASSERT(callback->getter() != NULL);
// Tail call to runtime.
// Important invariant in CALLBACKS case: the code above must be
// structured to never clobber |receiver| register.
__ Move(scratch2, callback);
// holder_reg is either receiver or scratch1.
if (!receiver.is(holder_reg)) {
ASSERT(scratch1.is(holder_reg));
__ Push(receiver, holder_reg);
} else {
__ push(receiver);
__ push(holder_reg);
}
__ ldr(scratch3,
FieldMemOperand(scratch2, ExecutableAccessorInfo::kDataOffset));
__ mov(scratch1, Operand(ExternalReference::isolate_address()));
__ Push(scratch3, scratch1, scratch2, name_reg);
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty),
masm()->isolate());
__ TailCallExternalReference(ref, 6, 1);
}
GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup);
} else { // !compile_followup_inline
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
scratch1, scratch2, scratch3,
name, miss);
PushInterceptorArguments(masm(), receiver, holder_reg,
name_reg, interceptor_holder);
PushInterceptorArguments(masm(), receiver(), holder_reg,
this->name(), interceptor_holder);
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad),
@ -2908,43 +2845,16 @@ Handle<Code> LoadStubCompiler::CompileLoadNonexistent(
Handle<JSObject> last,
Handle<String> name,
Handle<GlobalObject> global) {
// ----------- S t a t e -------------
// -- r0 : receiver
// -- lr : return address
// -----------------------------------
Label miss;
Label success;
// Check that receiver is not a smi.
__ JumpIfSmi(r0, &miss);
Register scratch = r1;
// Check the maps of the full prototype chain.
Register result =
CheckPrototypes(object, r0, last, r3, scratch, r4, name, &miss);
// If the last object in the prototype chain is a global object,
// check that the global property cell is empty.
if (!global.is_null()) {
GenerateCheckPropertyCell(masm(), global, name, scratch, &miss);
}
if (!last->HasFastProperties()) {
__ ldr(scratch, FieldMemOperand(result, HeapObject::kMapOffset));
__ ldr(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
__ cmp(scratch, Operand(isolate()->factory()->null_value()));
__ b(ne, &miss);
}
NonexistentHandlerFrontend(object, last, name, &success, global);
__ bind(&success);
// Return undefined if maps of the full prototype chain are still the
// same and no global property with this name contains a value.
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
__ Ret();
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::NONEXISTENT, factory()->empty_string());
}
@ -3009,48 +2919,16 @@ void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
#define __ ACCESS_MASM(masm())
Handle<Code> LoadStubCompiler::CompileLoadViaGetter(
Handle<JSObject> receiver,
Handle<JSObject> holder,
Handle<String> name,
Handle<JSFunction> getter) {
// ----------- S t a t e -------------
// -- r0 : receiver
// -- r2 : name
// -- lr : return address
// -----------------------------------
Label miss;
// Check that the maps haven't changed.
__ JumpIfSmi(r0, &miss);
CheckPrototypes(receiver, r0, holder, r3, r4, r1, name, &miss);
GenerateLoadViaGetter(masm(), getter);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::CALLBACKS, name);
}
Handle<Code> LoadStubCompiler::CompileLoadGlobal(
Handle<JSObject> object,
Handle<GlobalObject> holder,
Handle<GlobalObject> global,
Handle<JSGlobalPropertyCell> cell,
Handle<String> name,
bool is_dont_delete) {
// ----------- S t a t e -------------
// -- r0 : receiver
// -- r2 : name
// -- lr : return address
// -----------------------------------
Label miss;
Label success, miss;
// Check that the map of the global has not changed.
__ JumpIfSmi(r0, &miss);
CheckPrototypes(object, r0, holder, r3, r4, r1, name, &miss);
HandlerFrontendHeader(object, receiver(), Handle<JSObject>::cast(global),
name, &miss, PERFORM_INITIAL_CHECKS);
// Get the value from the cell.
__ mov(r3, Operand(cell));
@ -3063,15 +2941,14 @@ Handle<Code> LoadStubCompiler::CompileLoadGlobal(
__ b(eq, &miss);
}
__ mov(r0, r4);
HandlerFrontendFooter(&success, &miss);
__ bind(&success);
Counters* counters = masm()->isolate()->counters();
__ IncrementCounter(counters->named_load_global_stub(), 1, r1, r3);
__ mov(r0, r4);
__ Ret();
__ bind(&miss);
__ IncrementCounter(counters->named_load_global_stub_miss(), 1, r1, r3);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::NORMAL, name);
}

View File

@ -1044,117 +1044,128 @@ Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
}
void StubCompiler::GenerateLoadField(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
PropertyIndex index,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
void BaseLoadStubCompiler::HandlerFrontendFooter(Label* success,
Label* miss) {
__ jmp(success);
__ bind(miss);
GenerateLoadMiss(masm(), kind());
}
// Check the prototype chain.
Register reg = CheckPrototypes(
object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
Register BaseLoadStubCompiler::CallbackHandlerFrontend(
Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* success,
FrontendCheckType check,
Handle<ExecutableAccessorInfo> callback) {
Label miss;
Register reg = HandlerFrontendHeader(
object, object_reg, holder, name, &miss, check);
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
ASSERT(!reg.is(scratch2()));
ASSERT(!reg.is(scratch3()));
Register dictionary = scratch1();
bool must_preserve_dictionary_reg = reg.is(dictionary);
// Load the properties dictionary.
if (must_preserve_dictionary_reg) {
__ push(dictionary);
}
__ mov(dictionary, FieldOperand(reg, JSObject::kPropertiesOffset));
// Probe the dictionary.
Label probe_done, pop_and_miss;
StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
&pop_and_miss,
&probe_done,
dictionary,
this->name(),
scratch2(),
scratch3());
__ bind(&pop_and_miss);
if (must_preserve_dictionary_reg) {
__ pop(dictionary);
}
__ jmp(&miss);
__ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch2 contains the
// index into the dictionary. Check that the value is the callback.
Register index = scratch2();
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ mov(scratch3(),
Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag));
if (must_preserve_dictionary_reg) {
__ pop(dictionary);
}
__ cmp(scratch3(), callback);
__ j(not_equal, &miss);
}
HandlerFrontendFooter(success, &miss);
return reg;
}
void BaseLoadStubCompiler::NonexistentHandlerFrontend(
Handle<JSObject> object,
Handle<JSObject> last,
Handle<String> name,
Label* success,
Handle<GlobalObject> global) {
Label miss;
Register reg = HandlerFrontendHeader(
object, receiver(), last, name, &miss, PERFORM_INITIAL_CHECKS);
// If the last object in the prototype chain is a global object,
// check that the global property cell is empty.
if (!global.is_null()) {
GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss);
}
if (!last->HasFastProperties()) {
__ mov(scratch2(), FieldOperand(reg, HeapObject::kMapOffset));
__ mov(scratch2(), FieldOperand(scratch2(), Map::kPrototypeOffset));
__ cmp(scratch2(), isolate()->factory()->null_value());
__ j(not_equal, &miss);
}
HandlerFrontendFooter(success, &miss);
}
void BaseLoadStubCompiler::GenerateLoadField(Register reg,
Handle<JSObject> holder,
PropertyIndex index) {
// Get the value from the properties.
GenerateFastPropertyLoad(masm(), eax, reg, holder, index);
__ ret(0);
}
void StubCompiler::GenerateDictionaryLoadCallback(
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss) {
ASSERT(!receiver.is(scratch2));
ASSERT(!receiver.is(scratch3));
Register dictionary = scratch1;
bool must_preserve_dictionary_reg = receiver.is(dictionary);
// Load the properties dictionary.
if (must_preserve_dictionary_reg) {
__ push(dictionary);
}
__ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
// Probe the dictionary.
Label probe_done, pop_and_miss;
StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
&pop_and_miss,
&probe_done,
dictionary,
name_reg,
scratch2,
scratch3);
__ bind(&pop_and_miss);
if (must_preserve_dictionary_reg) {
__ pop(dictionary);
}
__ jmp(miss);
__ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch2 contains the
// index into the dictionary. Check that the value is the callback.
Register index = scratch2;
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ mov(scratch3,
Operand(dictionary, index, times_4, kValueOffset - kHeapObjectTag));
if (must_preserve_dictionary_reg) {
__ pop(dictionary);
}
__ cmp(scratch3, callback);
__ j(not_equal, miss);
}
void StubCompiler::GenerateLoadCallback(
Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Register scratch4,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// Check that the maps haven't changed.
Register reg = CheckPrototypes(object, receiver, holder, scratch1,
scratch2, scratch3, name, miss);
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
GenerateDictionaryLoadCallback(
reg, name_reg, scratch1, scratch2, scratch3, callback, name, miss);
}
void BaseLoadStubCompiler::GenerateLoadCallback(
Register reg,
Handle<ExecutableAccessorInfo> callback) {
// Insert additional parameters into the stack frame above return address.
ASSERT(!scratch3.is(reg));
__ pop(scratch3); // Get return address to place it below.
ASSERT(!scratch3().is(reg));
__ pop(scratch3()); // Get return address to place it below.
__ push(receiver); // receiver
__ mov(scratch2, esp);
ASSERT(!scratch2.is(reg));
__ push(receiver()); // receiver
__ mov(scratch2(), esp);
ASSERT(!scratch2().is(reg));
__ push(reg); // holder
// Push data from AccessorInfo.
if (isolate()->heap()->InNewSpace(callback->data())) {
__ mov(scratch1, Immediate(callback));
__ push(FieldOperand(scratch1, ExecutableAccessorInfo::kDataOffset));
__ mov(scratch1(), Immediate(callback));
__ push(FieldOperand(scratch1(), ExecutableAccessorInfo::kDataOffset));
} else {
__ push(Immediate(Handle<Object>(callback->data(), isolate())));
}
@ -1162,12 +1173,12 @@ void StubCompiler::GenerateLoadCallback(
// Save a pointer to where we pushed the arguments pointer.
// This will be passed as the const AccessorInfo& to the C++ callback.
__ push(scratch2);
__ push(scratch2());
__ push(name_reg); // name
__ push(name()); // name
__ mov(ebx, esp); // esp points to reference to name (handler).
__ push(scratch3); // Restore return address.
__ push(scratch3()); // Restore return address.
// 4 elements array for v8::Arguments::values_, handler for name and pointer
// to the values (it considered as smi in GC).
@ -1188,44 +1199,22 @@ void StubCompiler::GenerateLoadCallback(
}
void StubCompiler::GenerateLoadConstant(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<JSFunction> value,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// Check that the maps haven't changed.
CheckPrototypes(
object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
void BaseLoadStubCompiler::GenerateLoadConstant(Handle<JSFunction> value) {
// Return the constant value.
__ LoadHeapObject(eax, value);
__ ret(0);
}
void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
Handle<JSObject> interceptor_holder,
LookupResult* lookup,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<String> name,
Label* miss) {
void BaseLoadStubCompiler::GenerateLoadInterceptor(
Register holder_reg,
Handle<JSObject> object,
Handle<JSObject> interceptor_holder,
LookupResult* lookup,
Handle<String> name) {
ASSERT(interceptor_holder->HasNamedInterceptor());
ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
// later.
@ -1246,17 +1235,14 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
// 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.
Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
scratch1, scratch2, scratch3,
name, miss);
ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
ASSERT(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 CALLBACKS case needs the receiver to be passed into C++ code,
// the FIELD case might cause a miss during the prototype check.
bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
bool must_preserve_receiver_reg = !receiver.is(holder_reg) &&
bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
(lookup->type() == CALLBACKS || must_perfrom_prototype_check);
// Save necessary data before invoking an interceptor.
@ -1265,18 +1251,18 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ push(receiver);
__ push(receiver());
}
__ push(holder_reg);
__ push(name_reg);
__ push(this->name());
// 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,
receiver(),
holder_reg,
name_reg,
this->name(),
interceptor_holder);
// Check if interceptor provided a value for property. If it's
@ -1290,76 +1276,28 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
// Clobber registers when generating debug-code to provoke errors.
__ bind(&interceptor_failed);
if (FLAG_debug_code) {
__ mov(receiver, Immediate(BitCast<int32_t>(kZapValue)));
__ mov(receiver(), Immediate(BitCast<int32_t>(kZapValue)));
__ mov(holder_reg, Immediate(BitCast<int32_t>(kZapValue)));
__ mov(name_reg, Immediate(BitCast<int32_t>(kZapValue)));
__ mov(this->name(), Immediate(BitCast<int32_t>(kZapValue)));
}
__ pop(name_reg);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver);
__ pop(receiver());
}
// Leave the internal frame.
}
// Check that the maps from interceptor's holder to lookup's holder
// haven't changed. And load lookup's holder into holder_reg.
if (must_perfrom_prototype_check) {
holder_reg = CheckPrototypes(interceptor_holder,
holder_reg,
Handle<JSObject>(lookup->holder()),
scratch1,
scratch2,
scratch3,
name,
miss);
}
if (lookup->IsField()) {
// We found FIELD property in prototype chain of interceptor's holder.
// Retrieve a field from field's holder.
GenerateFastPropertyLoad(masm(), eax, holder_reg,
Handle<JSObject>(lookup->holder()),
lookup->GetFieldIndex());
__ ret(0);
} else {
// We found CALLBACKS property in prototype chain of interceptor's
// holder.
ASSERT(lookup->type() == CALLBACKS);
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
ASSERT(callback->getter() != NULL);
// Tail call to runtime.
// Important invariant in CALLBACKS case: the code above must be
// structured to never clobber |receiver| register.
__ pop(scratch2); // return address
__ push(receiver);
__ push(holder_reg);
__ mov(holder_reg, Immediate(callback));
__ push(FieldOperand(holder_reg, ExecutableAccessorInfo::kDataOffset));
__ push(Immediate(reinterpret_cast<int>(isolate())));
__ push(holder_reg);
__ push(name_reg);
__ push(scratch2); // restore return address
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty),
masm()->isolate());
__ TailCallExternalReference(ref, 6, 1);
}
GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup);
} else { // !compile_followup_inline
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
Register holder_reg =
CheckPrototypes(object, receiver, interceptor_holder,
scratch1, scratch2, scratch3, name, miss);
__ pop(scratch2); // save old return address
PushInterceptorArguments(masm(), receiver, holder_reg,
name_reg, interceptor_holder);
__ push(scratch2); // restore old return address
__ pop(scratch2()); // save old return address
PushInterceptorArguments(masm(), receiver(), holder_reg,
this->name(), interceptor_holder);
__ push(scratch2()); // restore old return address
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad),
@ -2978,45 +2916,16 @@ Handle<Code> LoadStubCompiler::CompileLoadNonexistent(
Handle<JSObject> last,
Handle<String> name,
Handle<GlobalObject> global) {
// ----------- S t a t e -------------
// -- ecx : name
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
Label miss;
Label success;
// Check that the receiver isn't a smi.
__ JumpIfSmi(edx, &miss);
Register scratch = eax;
// Check the maps of the full prototype chain. Also check that
// global property cells up to (but not including) the last object
// in the prototype chain are empty.
Register result =
CheckPrototypes(object, edx, last, ebx, scratch, edi, name, &miss);
// If the last object in the prototype chain is a global object,
// check that the global property cell is empty.
if (!global.is_null()) {
GenerateCheckPropertyCell(masm(), global, name, scratch, &miss);
}
if (!last->HasFastProperties()) {
__ mov(scratch, FieldOperand(result, HeapObject::kMapOffset));
__ mov(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
__ cmp(scratch, isolate()->factory()->null_value());
__ j(not_equal, &miss);
}
NonexistentHandlerFrontend(object, last, name, &success, global);
__ bind(&success);
// Return undefined if maps of the full prototype chain are still the
// same and no global property with this name contains a value.
__ mov(eax, isolate()->factory()->undefined_value());
__ ret(0);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::NONEXISTENT, factory()->empty_string());
}
@ -3081,75 +2990,41 @@ void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
#define __ ACCESS_MASM(masm())
Handle<Code> LoadStubCompiler::CompileLoadViaGetter(
Handle<JSObject> receiver,
Handle<JSObject> holder,
Handle<String> name,
Handle<JSFunction> getter) {
// ----------- S t a t e -------------
// -- ecx : name
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
Label miss;
// Check that the maps haven't changed.
__ JumpIfSmi(edx, &miss);
CheckPrototypes(receiver, edx, holder, ebx, eax, edi, name, &miss);
GenerateLoadViaGetter(masm(), getter);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::CALLBACKS, name);
}
Handle<Code> LoadStubCompiler::CompileLoadGlobal(
Handle<JSObject> object,
Handle<GlobalObject> holder,
Handle<GlobalObject> global,
Handle<JSGlobalPropertyCell> cell,
Handle<String> name,
bool is_dont_delete) {
// ----------- S t a t e -------------
// -- ecx : name
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
Label miss;
// Check that the maps haven't changed.
__ JumpIfSmi(edx, &miss);
CheckPrototypes(object, edx, holder, ebx, eax, edi, name, &miss);
Label success, miss;
HandlerFrontendHeader(object, receiver(), Handle<JSObject>::cast(global),
name, &miss, PERFORM_INITIAL_CHECKS);
// Get the value from the cell.
if (Serializer::enabled()) {
__ mov(ebx, Immediate(cell));
__ mov(ebx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
__ mov(eax, Immediate(cell));
__ mov(eax, FieldOperand(eax, JSGlobalPropertyCell::kValueOffset));
} else {
__ mov(ebx, Operand::Cell(cell));
__ mov(eax, Operand::Cell(cell));
}
// Check for deleted property if property can actually be deleted.
if (!is_dont_delete) {
__ cmp(ebx, factory()->the_hole_value());
__ cmp(eax, factory()->the_hole_value());
__ j(equal, &miss);
} else if (FLAG_debug_code) {
__ cmp(ebx, factory()->the_hole_value());
__ cmp(eax, factory()->the_hole_value());
__ Check(not_equal, "DontDelete cells can't contain the hole");
}
HandlerFrontendFooter(&success, &miss);
__ bind(&success);
Counters* counters = isolate()->counters();
__ IncrementCounter(counters->named_load_global_stub(), 1);
__ mov(eax, ebx);
// The code above already loads the result into the return register.
__ ret(0);
__ bind(&miss);
__ IncrementCounter(counters->named_load_global_stub_miss(), 1);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::NORMAL, name);
}

View File

@ -50,7 +50,6 @@ namespace internal {
ICU(KeyedStoreIC_MissForceGeneric) \
ICU(KeyedStoreIC_Slow) \
/* Utilities for IC stubs. */ \
ICU(LoadCallbackProperty) \
ICU(StoreCallbackProperty) \
ICU(LoadPropertyWithInterceptorOnly) \
ICU(LoadPropertyWithInterceptorForLoad) \

View File

@ -1016,32 +1016,6 @@ void StubCache::CollectMatchingMaps(SmallMapList* types,
// StubCompiler implementation.
RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) {
ASSERT(args[0]->IsJSObject());
ASSERT(args[1]->IsJSObject());
ASSERT(args[3]->IsSmi());
ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[4]);
Address getter_address = v8::ToCData<Address>(callback->getter());
v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(getter_address);
ASSERT(fun != NULL);
ASSERT(callback->IsCompatibleReceiver(args[0]));
v8::AccessorInfo info(&args[0]);
HandleScope scope(isolate);
v8::Handle<v8::Value> result;
{
// Leaving JavaScript.
VMState state(isolate, EXTERNAL);
ExternalCallbackScope call_scope(isolate, getter_address);
result = fun(v8::Utils::ToLocal(args.at<String>(5)), info);
}
RETURN_IF_SCHEDULED_EXCEPTION(isolate);
if (result.IsEmpty()) return HEAP->undefined_value();
Handle<Object> result_internal = v8::Utils::OpenHandle(*result);
result_internal->VerifyApiCallResultType();
return *result_internal;
}
RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) {
JSObject* recv = JSObject::cast(args[0]);
ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]);
@ -1409,86 +1383,159 @@ void StubCompiler::LookupPostInterceptor(Handle<JSObject> holder,
#define __ ACCESS_MASM(masm())
Register BaseLoadStubCompiler::HandlerFrontendHeader(Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* miss,
FrontendCheckType check) {
if (check == PERFORM_INITIAL_CHECKS) {
GenerateNameCheck(name, this->name(), miss);
// Check that the receiver isn't a smi.
__ JumpIfSmi(object_reg, miss);
}
// Check the prototype chain.
return CheckPrototypes(object, object_reg, holder,
scratch1(), scratch2(), scratch3(),
name, miss);
}
Register BaseLoadStubCompiler::HandlerFrontend(Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* success,
FrontendCheckType check) {
Label miss;
Register reg = HandlerFrontendHeader(
object, object_reg, holder, name, &miss, check);
HandlerFrontendFooter(success, &miss);
return reg;
}
Handle<Code> BaseLoadStubCompiler::CompileLoadField(Handle<JSObject> object,
Handle<JSObject> holder,
Handle<String> name,
PropertyIndex index) {
Label miss;
GenerateNameCheck(name, this->name(), &miss);
GenerateLoadField(object, holder, receiver(),
scratch1(), scratch2(), scratch3(),
index, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), kind());
Label success;
Register reg = HandlerFrontend(object, receiver(), holder, name,
&success, PERFORM_INITIAL_CHECKS);
__ bind(&success);
GenerateLoadField(reg, holder, index);
// Return the generated code.
return GetCode(Code::FIELD, name);
}
Handle<Code> BaseLoadStubCompiler::CompileLoadCallback(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<String> name,
Handle<ExecutableAccessorInfo> callback) {
Label miss;
GenerateNameCheck(name, this->name(), &miss);
GenerateLoadCallback(object, holder, receiver(), this->name(),
scratch1(), scratch2(), scratch3(), scratch4(),
callback, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), kind());
// Return the generated code.
return GetCode(Code::CALLBACKS, name);
}
Handle<Code> BaseLoadStubCompiler::CompileLoadConstant(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<String> name,
Handle<JSFunction> value) {
Label miss;
GenerateNameCheck(name, this->name(), &miss);
GenerateLoadConstant(object, holder, receiver(),
scratch1(), scratch2(), scratch3(),
value, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), kind());
Label success;
HandlerFrontend(object, receiver(), holder, name,
&success, PERFORM_INITIAL_CHECKS);
__ bind(&success);
GenerateLoadConstant(value);
// Return the generated code.
return GetCode(Code::CONSTANT_FUNCTION, name);
}
Handle<Code> BaseLoadStubCompiler::CompileLoadCallback(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<String> name,
Handle<ExecutableAccessorInfo> callback) {
Label success;
Register reg = CallbackHandlerFrontend(
object, receiver(), holder, name, &success,
PERFORM_INITIAL_CHECKS, callback);
__ bind(&success);
GenerateLoadCallback(reg, callback);
// Return the generated code.
return GetCode(Code::CALLBACKS, name);
}
Handle<Code> BaseLoadStubCompiler::CompileLoadInterceptor(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<String> name) {
Label miss;
Label success;
LookupResult lookup(isolate());
LookupPostInterceptor(holder, name, &lookup);
GenerateNameCheck(name, this->name(), &miss);
Register reg = HandlerFrontend(object, receiver(), holder, name,
&success, PERFORM_INITIAL_CHECKS);
__ bind(&success);
// TODO(368): Compile in the whole chain: all the interceptors in
// prototypes and ultimate answer.
GenerateLoadInterceptor(object, holder, &lookup, receiver(), this->name(),
scratch1(), scratch2(), scratch3(),
name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), kind());
GenerateLoadInterceptor(reg, object, holder, &lookup, name);
// Return the generated code.
return GetCode(Code::INTERCEPTOR, name);
}
void BaseLoadStubCompiler::GenerateLoadPostInterceptor(
Register interceptor_reg,
Handle<JSObject> interceptor_holder,
Handle<String> name,
LookupResult* lookup) {
Label success;
Handle<JSObject> holder(lookup->holder());
if (lookup->IsField()) {
// We found FIELD property in prototype chain of interceptor's holder.
// Retrieve a field from field's holder.
Register reg = HandlerFrontend(interceptor_holder, interceptor_reg, holder,
name, &success, SKIP_INITIAL_CHECKS);
__ bind(&success);
GenerateLoadField(reg, holder, lookup->GetFieldIndex());
} else {
// We found CALLBACKS property in prototype chain of interceptor's
// holder.
ASSERT(lookup->type() == CALLBACKS);
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
ASSERT(callback->getter() != NULL);
Register reg = CallbackHandlerFrontend(
interceptor_holder, interceptor_reg, holder,
name, &success, SKIP_INITIAL_CHECKS, callback);
__ bind(&success);
GenerateLoadCallback(reg, callback);
}
}
Handle<Code> LoadStubCompiler::CompileLoadViaGetter(
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<String> name,
Handle<JSFunction> getter) {
Label success;
HandlerFrontend(object, receiver(), holder, name,
&success, PERFORM_INITIAL_CHECKS);
__ bind(&success);
GenerateLoadViaGetter(masm(), getter);
// Return the generated code.
return GetCode(Code::CALLBACKS, name);
}
#undef __

View File

@ -402,7 +402,6 @@ class StubCache {
// Support functions for IC stubs for callbacks.
DECLARE_RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty);
DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty);
@ -536,58 +535,6 @@ class StubCompiler BASE_EMBEDDED {
MacroAssembler* masm() { return &masm_; }
void set_failure(Failure* failure) { failure_ = failure; }
void GenerateLoadField(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
PropertyIndex index,
Handle<String> name,
Label* miss);
void GenerateLoadCallback(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Register scratch4,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss);
void GenerateDictionaryLoadCallback(Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss);
void GenerateLoadConstant(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<JSFunction> value,
Handle<String> name,
Label* miss);
void GenerateLoadInterceptor(Handle<JSObject> object,
Handle<JSObject> holder,
LookupResult* lookup,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<String> name,
Label* miss);
static void LookupPostInterceptor(Handle<JSObject> holder,
Handle<String> name,
LookupResult* lookup);
@ -603,6 +550,9 @@ class StubCompiler BASE_EMBEDDED {
};
enum FrontendCheckType { PERFORM_INITIAL_CHECKS, SKIP_INITIAL_CHECKS };
class BaseLoadStubCompiler: public StubCompiler {
public:
BaseLoadStubCompiler(Isolate* isolate, Register* registers)
@ -629,6 +579,50 @@ class BaseLoadStubCompiler: public StubCompiler {
Handle<String> name);
protected:
Register HandlerFrontendHeader(Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* success,
FrontendCheckType check);
void HandlerFrontendFooter(Label* success, Label* miss);
Register HandlerFrontend(Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* success,
FrontendCheckType check);
Register CallbackHandlerFrontend(Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* success,
FrontendCheckType check,
Handle<ExecutableAccessorInfo> callback);
void NonexistentHandlerFrontend(Handle<JSObject> object,
Handle<JSObject> last,
Handle<String> name,
Label* success,
Handle<GlobalObject> global);
void GenerateLoadField(Register reg,
Handle<JSObject> holder,
PropertyIndex index);
void GenerateLoadConstant(Handle<JSFunction> value);
void GenerateLoadCallback(Register reg,
Handle<ExecutableAccessorInfo> callback);
void GenerateLoadInterceptor(Register holder_reg,
Handle<JSObject> object,
Handle<JSObject> holder,
LookupResult* lookup,
Handle<String> name);
void GenerateLoadPostInterceptor(Register reg,
Handle<JSObject> interceptor_holder,
Handle<String> name,
LookupResult* lookup);
Register receiver() { return registers_[0]; }
Register name() { return registers_[1]; }
Register scratch1() { return registers_[2]; }

View File

@ -1021,109 +1021,122 @@ Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
}
void StubCompiler::GenerateLoadField(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
PropertyIndex index,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
void BaseLoadStubCompiler::HandlerFrontendFooter(Label* success,
Label* miss) {
__ jmp(success);
__ bind(miss);
GenerateLoadMiss(masm(), kind());
}
// Check the prototype chain.
Register reg = CheckPrototypes(
object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
Register BaseLoadStubCompiler::CallbackHandlerFrontend(
Handle<JSObject> object,
Register object_reg,
Handle<JSObject> holder,
Handle<String> name,
Label* success,
FrontendCheckType check,
Handle<ExecutableAccessorInfo> callback) {
Label miss;
Register reg = HandlerFrontendHeader(
object, object_reg, holder, name, &miss, check);
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
ASSERT(!reg.is(scratch2()));
ASSERT(!reg.is(scratch3()));
ASSERT(!reg.is(scratch4()));
// Load the properties dictionary.
Register dictionary = scratch4();
__ movq(dictionary, FieldOperand(reg, JSObject::kPropertiesOffset));
// Probe the dictionary.
Label probe_done;
StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
&miss,
&probe_done,
dictionary,
this->name(),
scratch2(),
scratch3());
__ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch3 contains the
// index into the dictionary. Check that the value is the callback.
Register index = scratch3();
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ movq(scratch2(),
Operand(dictionary, index, times_pointer_size,
kValueOffset - kHeapObjectTag));
__ movq(scratch3(), callback, RelocInfo::EMBEDDED_OBJECT);
__ cmpq(scratch2(), scratch3());
__ j(not_equal, &miss);
}
HandlerFrontendFooter(success, &miss);
return reg;
}
void BaseLoadStubCompiler::NonexistentHandlerFrontend(
Handle<JSObject> object,
Handle<JSObject> last,
Handle<String> name,
Label* success,
Handle<GlobalObject> global) {
Label miss;
Register reg = HandlerFrontendHeader(
object, receiver(), last, name, &miss, PERFORM_INITIAL_CHECKS);
// If the last object in the prototype chain is a global object,
// check that the global property cell is empty.
if (!global.is_null()) {
GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss);
}
if (!last->HasFastProperties()) {
__ movq(scratch2(), FieldOperand(reg, HeapObject::kMapOffset));
__ movq(scratch2(), FieldOperand(scratch2(), Map::kPrototypeOffset));
__ Cmp(scratch2(), isolate()->factory()->null_value());
__ j(not_equal, &miss);
}
HandlerFrontendFooter(success, &miss);
}
void BaseLoadStubCompiler::GenerateLoadField(Register reg,
Handle<JSObject> holder,
PropertyIndex index) {
// Get the value from the properties.
GenerateFastPropertyLoad(masm(), rax, reg, holder, index);
__ ret(0);
}
void StubCompiler::GenerateDictionaryLoadCallback(
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss) {
ASSERT(!receiver.is(scratch1));
ASSERT(!receiver.is(scratch2));
ASSERT(!receiver.is(scratch3));
// Load the properties dictionary.
Register dictionary = scratch1;
__ movq(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
// Probe the dictionary.
Label probe_done;
StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
miss,
&probe_done,
dictionary,
name_reg,
scratch2,
scratch3);
__ bind(&probe_done);
// If probing finds an entry in the dictionary, scratch3 contains the
// index into the dictionary. Check that the value is the callback.
Register index = scratch3;
const int kElementsStartOffset =
StringDictionary::kHeaderSize +
StringDictionary::kElementsStartIndex * kPointerSize;
const int kValueOffset = kElementsStartOffset + kPointerSize;
__ movq(scratch2,
Operand(dictionary, index, times_pointer_size,
kValueOffset - kHeapObjectTag));
__ movq(scratch3, callback, RelocInfo::EMBEDDED_OBJECT);
__ cmpq(scratch2, scratch3);
__ j(not_equal, miss);
}
void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Register scratch4,
Handle<ExecutableAccessorInfo> callback,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// Check that the maps haven't changed.
Register reg = CheckPrototypes(object, receiver, holder, scratch1,
scratch2, scratch3, name, miss);
if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
GenerateDictionaryLoadCallback(
reg, name_reg, scratch2, scratch3, scratch4, callback, name, miss);
}
void BaseLoadStubCompiler::GenerateLoadCallback(
Register reg,
Handle<ExecutableAccessorInfo> callback) {
// Insert additional parameters into the stack frame above return address.
ASSERT(!scratch2.is(reg));
__ pop(scratch2); // Get return address to place it below.
ASSERT(!scratch2().is(reg));
__ pop(scratch2()); // Get return address to place it below.
__ push(receiver); // receiver
__ push(receiver()); // receiver
__ push(reg); // holder
if (heap()->InNewSpace(callback->data())) {
__ Move(scratch1, callback);
__ push(FieldOperand(scratch1, ExecutableAccessorInfo::kDataOffset));
__ Move(scratch1(), callback);
__ push(FieldOperand(scratch1(),
ExecutableAccessorInfo::kDataOffset)); // data
} else {
__ Push(Handle<Object>(callback->data(), isolate()));
}
__ PushAddress(ExternalReference::isolate_address()); // isolate
__ push(name_reg); // name
__ push(name()); // name
// Save a pointer to where we pushed the arguments pointer.
// This will be passed as the const AccessorInfo& to the C++ callback.
@ -1139,9 +1152,9 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
Register name_arg = rdi;
#endif
ASSERT(!name_arg.is(scratch2));
ASSERT(!name_arg.is(scratch2()));
__ movq(name_arg, rsp);
__ push(scratch2); // Restore return address.
__ push(scratch2()); // Restore return address.
// 4 elements array for v8::Arguments::values_ and handler for name.
const int kStackSpace = 5;
@ -1164,44 +1177,22 @@ void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
}
void StubCompiler::GenerateLoadConstant(Handle<JSObject> object,
Handle<JSObject> holder,
Register receiver,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<JSFunction> value,
Handle<String> name,
Label* miss) {
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// Check that the maps haven't changed.
CheckPrototypes(
object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
void BaseLoadStubCompiler::GenerateLoadConstant(Handle<JSFunction> value) {
// Return the constant value.
__ LoadHeapObject(rax, value);
__ ret(0);
}
void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
Handle<JSObject> interceptor_holder,
LookupResult* lookup,
Register receiver,
Register name_reg,
Register scratch1,
Register scratch2,
Register scratch3,
Handle<String> name,
Label* miss) {
void BaseLoadStubCompiler::GenerateLoadInterceptor(
Register holder_reg,
Handle<JSObject> object,
Handle<JSObject> interceptor_holder,
LookupResult* lookup,
Handle<String> name) {
ASSERT(interceptor_holder->HasNamedInterceptor());
ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, miss);
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
// later.
@ -1222,17 +1213,14 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
// 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.
Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
scratch1, scratch2, scratch3,
name, miss);
ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
ASSERT(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 CALLBACKS case needs the receiver to be passed into C++ code,
// the FIELD case might cause a miss during the prototype check.
bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
bool must_preserve_receiver_reg = !receiver.is(holder_reg) &&
bool must_preserve_receiver_reg = !receiver().is(holder_reg) &&
(lookup->type() == CALLBACKS || must_perfrom_prototype_check);
// Save necessary data before invoking an interceptor.
@ -1241,18 +1229,18 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
FrameScope frame_scope(masm(), StackFrame::INTERNAL);
if (must_preserve_receiver_reg) {
__ push(receiver);
__ push(receiver());
}
__ push(holder_reg);
__ push(name_reg);
__ push(this->name());
// 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,
receiver(),
holder_reg,
name_reg,
this->name(),
interceptor_holder);
// Check if interceptor provided a value for property. If it's
@ -1264,71 +1252,23 @@ void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
__ ret(0);
__ bind(&interceptor_failed);
__ pop(name_reg);
__ pop(this->name());
__ pop(holder_reg);
if (must_preserve_receiver_reg) {
__ pop(receiver);
__ pop(receiver());
}
// Leave the internal frame.
}
// Check that the maps from interceptor's holder to lookup's holder
// haven't changed. And load lookup's holder into |holder| register.
if (must_perfrom_prototype_check) {
holder_reg = CheckPrototypes(interceptor_holder,
holder_reg,
Handle<JSObject>(lookup->holder()),
scratch1,
scratch2,
scratch3,
name,
miss);
}
if (lookup->IsField()) {
// We found FIELD property in prototype chain of interceptor's holder.
// Retrieve a field from field's holder.
GenerateFastPropertyLoad(masm(), rax, holder_reg,
Handle<JSObject>(lookup->holder()),
lookup->GetFieldIndex());
__ ret(0);
} else {
// We found CALLBACKS property in prototype chain of interceptor's
// holder.
ASSERT(lookup->type() == CALLBACKS);
Handle<ExecutableAccessorInfo> callback(
ExecutableAccessorInfo::cast(lookup->GetCallbackObject()));
ASSERT(callback->getter() != NULL);
// Tail call to runtime.
// Important invariant in CALLBACKS case: the code above must be
// structured to never clobber |receiver| register.
__ pop(scratch2); // return address
__ push(receiver);
__ push(holder_reg);
__ Move(holder_reg, callback);
__ push(FieldOperand(holder_reg, ExecutableAccessorInfo::kDataOffset));
__ PushAddress(ExternalReference::isolate_address());
__ push(holder_reg);
__ push(name_reg);
__ push(scratch2); // restore return address
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty),
isolate());
__ TailCallExternalReference(ref, 6, 1);
}
GenerateLoadPostInterceptor(holder_reg, interceptor_holder, name, lookup);
} else { // !compile_followup_inline
// Call the runtime system to load the interceptor.
// Check that the maps haven't changed.
Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
scratch1, scratch2, scratch3,
name, miss);
__ pop(scratch2); // save old return address
PushInterceptorArguments(masm(), receiver, holder_reg,
name_reg, interceptor_holder);
__ push(scratch2); // restore old return address
__ pop(scratch2()); // save old return address
PushInterceptorArguments(masm(), receiver(), holder_reg,
this->name(), interceptor_holder);
__ push(scratch2()); // restore old return address
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptorForLoad), isolate());
@ -2801,44 +2741,16 @@ Handle<Code> LoadStubCompiler::CompileLoadNonexistent(
Handle<JSObject> last,
Handle<String> name,
Handle<GlobalObject> global) {
// ----------- S t a t e -------------
// -- rax : receiver
// -- rcx : name
// -- rsp[0] : return address
// -----------------------------------
Label miss;
Label success;
// Check that receiver is not a smi.
__ JumpIfSmi(rax, &miss);
// Check the maps of the full prototype chain. Also check that
// global property cells up to (but not including) the last object
// in the prototype chain are empty.
Register scratch = rdx;
Register result =
CheckPrototypes(object, rax, last, rbx, scratch, rdi, name, &miss);
// If the last object in the prototype chain is a global object,
// check that the global property cell is empty.
if (!global.is_null()) {
GenerateCheckPropertyCell(masm(), global, name, scratch, &miss);
}
if (!last->HasFastProperties()) {
__ movq(scratch, FieldOperand(result, HeapObject::kMapOffset));
__ movq(scratch, FieldOperand(scratch, Map::kPrototypeOffset));
__ Cmp(scratch, isolate()->factory()->null_value());
__ j(not_equal, &miss);
}
NonexistentHandlerFrontend(object, last, name, &success, global);
__ bind(&success);
// Return undefined if maps of the full prototype chain are still the
// same and no global property with this name contains a value.
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
__ ret(0);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::NONEXISTENT, factory()->empty_string());
}
@ -2903,48 +2815,19 @@ void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
#define __ ACCESS_MASM(masm())
Handle<Code> LoadStubCompiler::CompileLoadViaGetter(
Handle<JSObject> receiver,
Handle<JSObject> holder,
Handle<String> name,
Handle<JSFunction> getter) {
// ----------- S t a t e -------------
// -- rax : receiver
// -- rcx : name
// -- rsp[0] : return address
// -----------------------------------
Label miss;
// Check that the maps haven't changed.
__ JumpIfSmi(rax, &miss);
CheckPrototypes(receiver, rax, holder, rbx, rdx, rdi, name, &miss);
GenerateLoadViaGetter(masm(), getter),
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::CALLBACKS, name);
}
Handle<Code> LoadStubCompiler::CompileLoadGlobal(
Handle<JSObject> object,
Handle<GlobalObject> holder,
Handle<GlobalObject> global,
Handle<JSGlobalPropertyCell> cell,
Handle<String> name,
bool is_dont_delete) {
// ----------- S t a t e -------------
// -- rax : receiver
// -- rcx : name
// -- rsp[0] : return address
// -----------------------------------
Label miss;
Label success, miss;
// TODO(verwaest): Directly store to rax. Currently we cannot do this, since
// rax is used as receiver(), which we would otherwise clobber before a
// potential miss.
// Check that the maps haven't changed.
__ JumpIfSmi(rax, &miss);
CheckPrototypes(object, rax, holder, rbx, rdx, rdi, name, &miss);
HandlerFrontendHeader(object, receiver(), Handle<JSObject>::cast(global),
name, &miss, PERFORM_INITIAL_CHECKS);
// Get the value from the cell.
__ Move(rbx, cell);
@ -2959,15 +2842,14 @@ Handle<Code> LoadStubCompiler::CompileLoadGlobal(
__ Check(not_equal, "DontDelete cells can't contain the hole");
}
HandlerFrontendFooter(&success, &miss);
__ bind(&success);
Counters* counters = isolate()->counters();
__ IncrementCounter(counters->named_load_global_stub(), 1);
__ movq(rax, rbx);
__ ret(0);
__ bind(&miss);
__ IncrementCounter(counters->named_load_global_stub_miss(), 1);
GenerateLoadMiss(masm(), Code::LOAD_IC);
// Return the generated code.
return GetCode(Code::NORMAL, name);
}

View File

@ -108,8 +108,6 @@ TEST(ExternalReferenceEncoder) {
Encode(encoder, Builtins::kArrayCode));
CHECK_EQ(make_code(v8::internal::RUNTIME_FUNCTION, Runtime::kAbort),
Encode(encoder, Runtime::kAbort));
CHECK_EQ(make_code(IC_UTILITY, IC::kLoadCallbackProperty),
Encode(encoder, IC_Utility(IC::kLoadCallbackProperty)));
ExternalReference keyed_load_function_prototype =
ExternalReference(isolate->counters()->keyed_load_function_prototype());
CHECK_EQ(make_code(STATS_COUNTER, Counters::k_keyed_load_function_prototype),
@ -148,8 +146,6 @@ TEST(ExternalReferenceDecoder) {
CHECK_EQ(AddressOf(Runtime::kAbort),
decoder.Decode(make_code(v8::internal::RUNTIME_FUNCTION,
Runtime::kAbort)));
CHECK_EQ(AddressOf(IC_Utility(IC::kLoadCallbackProperty)),
decoder.Decode(make_code(IC_UTILITY, IC::kLoadCallbackProperty)));
ExternalReference keyed_load_function =
ExternalReference(isolate->counters()->keyed_load_function_prototype());
CHECK_EQ(keyed_load_function.address(),