Remove redundant checks in and around GenerateDictionaryLoad.
Similar or duplicate checks are scattered around the code before doing the dictionary load. Also the entire branch in GenerateCallNormal that handles global/builtin receiver is guaranteed to bail out from GenerateDictionaryLoad, so there is no point in generating it at all. The purpose of the patch is: - making C++ code more compact and transparent, - not generating dead code. There is a tiny performance gain. The patch is ia32 only for now. Please tell me if I am missing anything. Review URL: http://codereview.chromium.org/2801007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4926 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
0b11e5ba22
commit
927750571c
@ -47,71 +47,97 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
|
||||
Register type,
|
||||
Label* global_object) {
|
||||
// Register usage:
|
||||
// type: holds the receiver instance type on entry.
|
||||
__ cmp(type, Operand(JS_GLOBAL_OBJECT_TYPE));
|
||||
__ b(eq, global_object);
|
||||
__ cmp(type, Operand(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ b(eq, global_object);
|
||||
__ cmp(type, Operand(JS_GLOBAL_PROXY_TYPE));
|
||||
__ b(eq, global_object);
|
||||
}
|
||||
|
||||
|
||||
// Generated code falls through if the receiver is a regular non-global
|
||||
// JS object with slow properties and no interceptors.
|
||||
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
|
||||
Register receiver,
|
||||
Register elements,
|
||||
Register t0,
|
||||
Register t1,
|
||||
Label* miss) {
|
||||
// Register usage:
|
||||
// receiver: holds the receiver on entry and is unchanged.
|
||||
// elements: holds the property dictionary on fall through.
|
||||
// Scratch registers:
|
||||
// t0: used to holds the receiver map.
|
||||
// t1: used to holds the receiver instance type, receiver bit mask and
|
||||
// elements map.
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ tst(receiver, Operand(kSmiTagMask));
|
||||
__ b(eq, miss);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ CompareObjectType(receiver, t0, t1, FIRST_JS_OBJECT_TYPE);
|
||||
__ b(lt, miss);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
GenerateGlobalInstanceTypeCheck(masm, t1, miss);
|
||||
|
||||
// Check that the global object does not require access checks.
|
||||
__ ldrb(t1, FieldMemOperand(t0, Map::kBitFieldOffset));
|
||||
__ tst(t1, Operand((1 << Map::kIsAccessCheckNeeded) |
|
||||
(1 << Map::kHasNamedInterceptor)));
|
||||
__ b(nz, miss);
|
||||
|
||||
__ ldr(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
__ ldr(t1, FieldMemOperand(elements, HeapObject::kMapOffset));
|
||||
__ LoadRoot(ip, Heap::kHashTableMapRootIndex);
|
||||
__ cmp(t1, ip);
|
||||
__ b(nz, miss);
|
||||
}
|
||||
|
||||
|
||||
// Helper function used from LoadIC/CallIC GenerateNormal.
|
||||
// receiver: Receiver. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done. Can be the same as receiver or name clobbering
|
||||
// label is not done. Can be the same as elements or name clobbering
|
||||
// one of these in the case of not jumping to the miss label.
|
||||
// The three scratch registers need to be different from the receiver, name and
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Register receiver,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register result,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
DictionaryCheck check_dictionary) {
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used to hold the property dictionary.
|
||||
// scratch2: Used as temporary and to hold the capacity of the property
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch3: Used as temporary.
|
||||
// scratch2: Used as temporary.
|
||||
|
||||
Label done;
|
||||
|
||||
// Check for the absence of an interceptor.
|
||||
// Load the map into scratch1.
|
||||
__ ldr(scratch1, FieldMemOperand(receiver, JSObject::kMapOffset));
|
||||
|
||||
// Bail out if the receiver has a named interceptor.
|
||||
__ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset));
|
||||
__ tst(scratch2, Operand(1 << Map::kHasNamedInterceptor));
|
||||
__ b(nz, miss);
|
||||
|
||||
// Bail out if we have a JS global proxy object.
|
||||
__ ldrb(scratch2, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
|
||||
__ cmp(scratch2, Operand(JS_GLOBAL_PROXY_TYPE));
|
||||
__ b(eq, miss);
|
||||
|
||||
// Possible work-around for http://crbug.com/16276.
|
||||
// See also: http://codereview.chromium.org/155418.
|
||||
__ cmp(scratch2, Operand(JS_GLOBAL_OBJECT_TYPE));
|
||||
__ b(eq, miss);
|
||||
__ cmp(scratch2, Operand(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ b(eq, miss);
|
||||
|
||||
// Load the properties array.
|
||||
__ ldr(scratch1, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
// Check that the properties array is a dictionary.
|
||||
if (check_dictionary == CHECK_DICTIONARY) {
|
||||
__ ldr(scratch2, FieldMemOperand(scratch1, HeapObject::kMapOffset));
|
||||
__ LoadRoot(ip, Heap::kHashTableMapRootIndex);
|
||||
__ cmp(scratch2, ip);
|
||||
__ b(ne, miss);
|
||||
}
|
||||
|
||||
// Compute the capacity mask.
|
||||
const int kCapacityOffset = StringDictionary::kHeaderSize +
|
||||
StringDictionary::kCapacityIndex * kPointerSize;
|
||||
__ ldr(scratch2, FieldMemOperand(scratch1, kCapacityOffset));
|
||||
__ mov(scratch2, Operand(scratch2, ASR, kSmiTagSize)); // convert smi to int
|
||||
__ sub(scratch2, scratch2, Operand(1));
|
||||
__ ldr(scratch1, FieldMemOperand(elements, kCapacityOffset));
|
||||
__ mov(scratch1, Operand(scratch1, ASR, kSmiTagSize)); // convert smi to int
|
||||
__ sub(scratch1, scratch1, Operand(1));
|
||||
|
||||
const int kElementsStartOffset = StringDictionary::kHeaderSize +
|
||||
StringDictionary::kElementsStartIndex * kPointerSize;
|
||||
@ -122,26 +148,26 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
static const int kProbes = 4;
|
||||
for (int i = 0; i < kProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ ldr(scratch3, FieldMemOperand(name, String::kHashFieldOffset));
|
||||
__ ldr(scratch2, FieldMemOperand(name, String::kHashFieldOffset));
|
||||
if (i > 0) {
|
||||
// Add the probe offset (i + i * i) left shifted to avoid right shifting
|
||||
// the hash in a separate instruction. The value hash + i + i * i is right
|
||||
// shifted in the following and instruction.
|
||||
ASSERT(StringDictionary::GetProbeOffset(i) <
|
||||
1 << (32 - String::kHashFieldOffset));
|
||||
__ add(scratch3, scratch3, Operand(
|
||||
__ add(scratch2, scratch2, Operand(
|
||||
StringDictionary::GetProbeOffset(i) << String::kHashShift));
|
||||
}
|
||||
__ and_(scratch3, scratch2, Operand(scratch3, LSR, String::kHashShift));
|
||||
__ and_(scratch2, scratch1, Operand(scratch2, LSR, String::kHashShift));
|
||||
|
||||
// Scale the index by multiplying by the element size.
|
||||
ASSERT(StringDictionary::kEntrySize == 3);
|
||||
// scratch3 = scratch3 * 3.
|
||||
__ add(scratch3, scratch3, Operand(scratch3, LSL, 1));
|
||||
// scratch2 = scratch2 * 3.
|
||||
__ add(scratch2, scratch2, Operand(scratch2, LSL, 1));
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ add(scratch3, scratch1, Operand(scratch3, LSL, 2));
|
||||
__ ldr(ip, FieldMemOperand(scratch3, kElementsStartOffset));
|
||||
__ add(scratch2, elements, Operand(scratch2, LSL, 2));
|
||||
__ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ cmp(name, Operand(ip));
|
||||
if (i != kProbes - 1) {
|
||||
__ b(eq, &done);
|
||||
@ -151,15 +177,15 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
// Check that the value is a normal property.
|
||||
__ bind(&done); // scratch3 == scratch1 + 4 * index
|
||||
__ ldr(scratch2,
|
||||
FieldMemOperand(scratch3, kElementsStartOffset + 2 * kPointerSize));
|
||||
__ tst(scratch2, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
__ ldr(scratch1,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 2 * kPointerSize));
|
||||
__ tst(scratch1, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
|
||||
__ b(ne, miss);
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ ldr(result,
|
||||
FieldMemOperand(scratch3, kElementsStartOffset + 1 * kPointerSize));
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
@ -310,6 +336,7 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
||||
Register receiver,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
int interceptor_bit,
|
||||
Label* slow) {
|
||||
// Check that the object isn't a smi.
|
||||
__ BranchOnSmi(receiver, slow);
|
||||
@ -317,8 +344,9 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
||||
__ ldr(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
||||
// Check bit field.
|
||||
__ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitFieldOffset));
|
||||
__ tst(scratch2, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
|
||||
__ b(ne, slow);
|
||||
__ tst(scratch2,
|
||||
Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)));
|
||||
__ b(nz, slow);
|
||||
// Check that the object is some kind of JS object EXCEPT JS Value type.
|
||||
// In the case that the object is a value-wrapper object,
|
||||
// we enter the runtime system to make sure that indexing into string
|
||||
@ -502,13 +530,11 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
static void GenerateNormalHelper(MacroAssembler* masm,
|
||||
int argc,
|
||||
bool is_global_object,
|
||||
Label* miss,
|
||||
Register scratch) {
|
||||
// Search dictionary - put result in register r1.
|
||||
GenerateDictionaryLoad(masm, miss, r1, r2, r1, r0, r3, r4, CHECK_DICTIONARY);
|
||||
static void GenerateFunctionTailCall(MacroAssembler* masm,
|
||||
int argc,
|
||||
Label* miss,
|
||||
Register scratch) {
|
||||
// r1: function
|
||||
|
||||
// Check that the value isn't a smi.
|
||||
__ tst(r1, Operand(kSmiTagMask));
|
||||
@ -518,13 +544,6 @@ static void GenerateNormalHelper(MacroAssembler* masm,
|
||||
__ CompareObjectType(r1, scratch, scratch, JS_FUNCTION_TYPE);
|
||||
__ b(ne, miss);
|
||||
|
||||
// Patch the receiver with the global proxy if necessary.
|
||||
if (is_global_object) {
|
||||
__ ldr(r0, MemOperand(sp, argc * kPointerSize));
|
||||
__ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
|
||||
__ str(r0, MemOperand(sp, argc * kPointerSize));
|
||||
}
|
||||
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ InvokeFunction(r1, actual, JUMP_FUNCTION);
|
||||
@ -536,53 +555,18 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
|
||||
// -- r2 : name
|
||||
// -- lr : return address
|
||||
// -----------------------------------
|
||||
Label miss, global_object, non_global_object;
|
||||
Label miss;
|
||||
|
||||
// Get the receiver of the function from the stack into r1.
|
||||
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ tst(r1, Operand(kSmiTagMask));
|
||||
__ b(eq, &miss);
|
||||
GenerateDictionaryLoadReceiverCheck(masm, r1, r0, r3, r4, &miss);
|
||||
|
||||
// Check that the receiver is a valid JS object. Put the map in r3.
|
||||
__ CompareObjectType(r1, r3, r0, FIRST_JS_OBJECT_TYPE);
|
||||
__ b(lt, &miss);
|
||||
// r0: elements
|
||||
// Search the dictionary - put result in register r1.
|
||||
GenerateDictionaryLoad(masm, &miss, r0, r2, r1, r3, r4);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
// Check for access to global object.
|
||||
__ cmp(r0, Operand(JS_GLOBAL_OBJECT_TYPE));
|
||||
__ b(eq, &global_object);
|
||||
__ cmp(r0, Operand(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ b(ne, &non_global_object);
|
||||
|
||||
// Accessing global object: Load and invoke.
|
||||
__ bind(&global_object);
|
||||
// Check that the global object does not require access checks.
|
||||
__ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
|
||||
__ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded));
|
||||
__ b(ne, &miss);
|
||||
GenerateNormalHelper(masm, argc, true, &miss, r4);
|
||||
|
||||
// Accessing non-global object: Check for access to global proxy.
|
||||
Label global_proxy, invoke;
|
||||
__ bind(&non_global_object);
|
||||
__ cmp(r0, Operand(JS_GLOBAL_PROXY_TYPE));
|
||||
__ b(eq, &global_proxy);
|
||||
// Check that the non-global, non-global-proxy object does not
|
||||
// require access checks.
|
||||
__ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
|
||||
__ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded));
|
||||
__ b(ne, &miss);
|
||||
__ bind(&invoke);
|
||||
GenerateNormalHelper(masm, argc, false, &miss, r4);
|
||||
|
||||
// Global object access: Check access rights.
|
||||
__ bind(&global_proxy);
|
||||
__ CheckAccessGlobalProxy(r1, r0, &miss);
|
||||
__ b(&invoke);
|
||||
GenerateFunctionTailCall(masm, argc, &miss, r4);
|
||||
|
||||
__ bind(&miss);
|
||||
}
|
||||
@ -594,6 +578,12 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
// -- lr : return address
|
||||
// -----------------------------------
|
||||
|
||||
if (id == IC::kCallIC_Miss) {
|
||||
__ IncrementCounter(&Counters::call_miss, 1, r3, r4);
|
||||
} else {
|
||||
__ IncrementCounter(&Counters::keyed_call_miss, 1, r3, r4);
|
||||
}
|
||||
|
||||
// Get the receiver of the function from the stack.
|
||||
__ ldr(r3, MemOperand(sp, argc * kPointerSize));
|
||||
|
||||
@ -614,23 +604,26 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
// Check if the receiver is a global object of some sort.
|
||||
Label invoke, global;
|
||||
__ ldr(r2, MemOperand(sp, argc * kPointerSize)); // receiver
|
||||
__ tst(r2, Operand(kSmiTagMask));
|
||||
__ b(eq, &invoke);
|
||||
__ CompareObjectType(r2, r3, r3, JS_GLOBAL_OBJECT_TYPE);
|
||||
__ b(eq, &global);
|
||||
__ cmp(r3, Operand(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ b(ne, &invoke);
|
||||
// This can happen only for regular CallIC but not KeyedCallIC.
|
||||
if (id == IC::kCallIC_Miss) {
|
||||
Label invoke, global;
|
||||
__ ldr(r2, MemOperand(sp, argc * kPointerSize)); // receiver
|
||||
__ tst(r2, Operand(kSmiTagMask));
|
||||
__ b(eq, &invoke);
|
||||
__ CompareObjectType(r2, r3, r3, JS_GLOBAL_OBJECT_TYPE);
|
||||
__ b(eq, &global);
|
||||
__ cmp(r3, Operand(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ b(ne, &invoke);
|
||||
|
||||
// Patch the receiver on the stack.
|
||||
__ bind(&global);
|
||||
__ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
|
||||
__ str(r2, MemOperand(sp, argc * kPointerSize));
|
||||
// Patch the receiver on the stack.
|
||||
__ bind(&global);
|
||||
__ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
|
||||
__ str(r2, MemOperand(sp, argc * kPointerSize));
|
||||
__ bind(&invoke);
|
||||
}
|
||||
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ bind(&invoke);
|
||||
__ InvokeFunction(r1, actual, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
@ -698,7 +691,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// Now the key is known to be a smi. This place is also jumped to from below
|
||||
// where a numeric string is converted to a smi.
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, r1, r0, r3, &slow_call);
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, r1, r0, r3, Map::kHasIndexedInterceptor, &slow_call);
|
||||
|
||||
GenerateFastArrayLoad(
|
||||
masm, r1, r2, r4, r3, r0, r1, &check_number_dictionary, &slow_load);
|
||||
@ -708,14 +702,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// receiver in r1 is not used after this point.
|
||||
// r2: key
|
||||
// r1: function
|
||||
|
||||
// Check that the value in r1 is a JSFunction.
|
||||
__ BranchOnSmi(r1, &slow_call);
|
||||
__ CompareObjectType(r1, r0, r0, JS_FUNCTION_TYPE);
|
||||
__ b(ne, &slow_call);
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ InvokeFunction(r1, actual, JUMP_FUNCTION);
|
||||
GenerateFunctionTailCall(masm, argc, &slow_call, r0);
|
||||
|
||||
__ bind(&check_number_dictionary);
|
||||
// r2: key
|
||||
@ -751,16 +738,16 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// If the receiver is a regular JS object with slow properties then do
|
||||
// a quick inline probe of the receiver's dictionary.
|
||||
// Otherwise do the monomorphic cache probe.
|
||||
GenerateKeyedLoadReceiverCheck(masm, r1, r0, r3, &lookup_monomorphic_cache);
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, r1, r0, r3, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
|
||||
|
||||
__ ldr(r3, FieldMemOperand(r1, JSObject::kPropertiesOffset));
|
||||
__ ldr(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
|
||||
__ ldr(r0, FieldMemOperand(r1, JSObject::kPropertiesOffset));
|
||||
__ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset));
|
||||
__ LoadRoot(ip, Heap::kHashTableMapRootIndex);
|
||||
__ cmp(r3, ip);
|
||||
__ b(ne, &lookup_monomorphic_cache);
|
||||
|
||||
GenerateDictionaryLoad(
|
||||
masm, &slow_load, r1, r2, r1, r0, r3, r4, DICTIONARY_CHECK_DONE);
|
||||
GenerateDictionaryLoad(masm, &slow_load, r0, r2, r1, r3, r4);
|
||||
__ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1, r0, r3);
|
||||
__ jmp(&do_call);
|
||||
|
||||
@ -826,36 +813,14 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
// -- r0 : receiver
|
||||
// -- sp[0] : receiver
|
||||
// -----------------------------------
|
||||
Label miss, probe, global;
|
||||
Label miss;
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ tst(r0, Operand(kSmiTagMask));
|
||||
__ b(eq, &miss);
|
||||
GenerateDictionaryLoadReceiverCheck(masm, r0, r1, r3, r4, &miss);
|
||||
|
||||
// Check that the receiver is a valid JS object. Put the map in r3.
|
||||
__ CompareObjectType(r0, r3, r1, FIRST_JS_OBJECT_TYPE);
|
||||
__ b(lt, &miss);
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
// Check for access to global object (unlikely).
|
||||
__ cmp(r1, Operand(JS_GLOBAL_PROXY_TYPE));
|
||||
__ b(eq, &global);
|
||||
|
||||
// Check for non-global object that requires access check.
|
||||
__ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
|
||||
__ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded));
|
||||
__ b(ne, &miss);
|
||||
|
||||
__ bind(&probe);
|
||||
GenerateDictionaryLoad(masm, &miss, r0, r2, r0, r1, r3, r4, CHECK_DICTIONARY);
|
||||
// r1: elements
|
||||
GenerateDictionaryLoad(masm, &miss, r1, r2, r0, r3, r4);
|
||||
__ Ret();
|
||||
|
||||
// Global object access: Check access rights.
|
||||
__ bind(&global);
|
||||
__ CheckAccessGlobalProxy(r0, r1, &miss);
|
||||
__ b(&probe);
|
||||
|
||||
// Cache miss: Jump to runtime.
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
@ -870,6 +835,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// -- sp[0] : receiver
|
||||
// -----------------------------------
|
||||
|
||||
__ IncrementCounter(&Counters::load_miss, 1, r3, r4);
|
||||
|
||||
__ mov(r3, r0);
|
||||
__ Push(r3, r2);
|
||||
|
||||
@ -1013,6 +980,8 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// -- r1 : receiver
|
||||
// -----------------------------------
|
||||
|
||||
__ IncrementCounter(&Counters::keyed_load_miss, 1, r3, r4);
|
||||
|
||||
__ Push(r1, r0);
|
||||
|
||||
ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss));
|
||||
@ -1045,14 +1014,15 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
Register key = r0;
|
||||
Register receiver = r1;
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, receiver, r2, r3, &slow);
|
||||
|
||||
// Check that the key is a smi.
|
||||
__ BranchOnNotSmi(key, &check_string);
|
||||
__ bind(&index_smi);
|
||||
// Now the key is known to be a smi. This place is also jumped to from below
|
||||
// where a numeric string is converted to a smi.
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, receiver, r2, r3, Map::kHasIndexedInterceptor, &slow);
|
||||
|
||||
GenerateFastArrayLoad(
|
||||
masm, receiver, key, r4, r3, r2, r0, &check_pixel_array, &slow);
|
||||
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1, r2, r3);
|
||||
@ -1095,12 +1065,15 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
__ bind(&check_string);
|
||||
GenerateKeyStringCheck(masm, key, r2, r3, &index_string, &slow);
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, receiver, r2, r3, Map::kHasNamedInterceptor, &slow);
|
||||
|
||||
// If the receiver is a fast-case object, check the keyed lookup
|
||||
// cache. Otherwise probe the dictionary.
|
||||
__ ldr(r3, FieldMemOperand(r1, JSObject::kPropertiesOffset));
|
||||
__ ldr(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
|
||||
__ ldr(r4, FieldMemOperand(r3, HeapObject::kMapOffset));
|
||||
__ LoadRoot(ip, Heap::kHashTableMapRootIndex);
|
||||
__ cmp(r3, ip);
|
||||
__ cmp(r4, ip);
|
||||
__ b(eq, &probe_dictionary);
|
||||
|
||||
// Load the map of the receiver, compute the keyed lookup cache hash
|
||||
@ -1148,9 +1121,14 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
// Do a quick inline probe of the receiver's dictionary, if it
|
||||
// exists.
|
||||
__ bind(&probe_dictionary);
|
||||
// r1: receiver
|
||||
// r0: key
|
||||
// r3: elements
|
||||
__ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
|
||||
__ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
|
||||
GenerateGlobalInstanceTypeCheck(masm, r2, &slow);
|
||||
// Load the property to r0.
|
||||
GenerateDictionaryLoad(
|
||||
masm, &slow, r1, r0, r0, r2, r3, r4, DICTIONARY_CHECK_DONE);
|
||||
GenerateDictionaryLoad(masm, &slow, r3, r0, r0, r2, r4);
|
||||
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1, r2, r3);
|
||||
__ Ret();
|
||||
|
||||
|
@ -45,72 +45,96 @@ namespace internal {
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
|
||||
Register type,
|
||||
Label* global_object) {
|
||||
// Register usage:
|
||||
// type: holds the receiver instance type on entry.
|
||||
__ cmp(type, JS_GLOBAL_OBJECT_TYPE);
|
||||
__ j(equal, global_object, not_taken);
|
||||
__ cmp(type, JS_BUILTINS_OBJECT_TYPE);
|
||||
__ j(equal, global_object, not_taken);
|
||||
__ cmp(type, JS_GLOBAL_PROXY_TYPE);
|
||||
__ j(equal, global_object, not_taken);
|
||||
}
|
||||
|
||||
|
||||
// Generated code falls through if the receiver is a regular non-global
|
||||
// JS object with slow properties and no interceptors.
|
||||
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
|
||||
Register receiver,
|
||||
Register r0,
|
||||
Register r1,
|
||||
Label* miss) {
|
||||
// Register usage:
|
||||
// receiver: holds the receiver on entry and is unchanged.
|
||||
// r0: used to hold receiver instance type.
|
||||
// Holds the property dictionary on fall through.
|
||||
// r1: used to hold receivers map.
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ test(receiver, Immediate(kSmiTagMask));
|
||||
__ j(zero, miss, not_taken);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ mov(r1, FieldOperand(receiver, HeapObject::kMapOffset));
|
||||
__ movzx_b(r0, FieldOperand(r1, Map::kInstanceTypeOffset));
|
||||
__ cmp(r0, FIRST_JS_OBJECT_TYPE);
|
||||
__ j(below, miss, not_taken);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
GenerateGlobalInstanceTypeCheck(masm, r0, miss);
|
||||
|
||||
// Check for non-global object that requires access check.
|
||||
__ test_b(FieldOperand(r1, Map::kBitFieldOffset),
|
||||
(1 << Map::kIsAccessCheckNeeded) |
|
||||
(1 << Map::kHasNamedInterceptor));
|
||||
__ j(not_zero, miss, not_taken);
|
||||
|
||||
__ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset));
|
||||
__ CheckMap(r0, Factory::hash_table_map(), miss, true);
|
||||
}
|
||||
|
||||
|
||||
// Helper function used to load a property from a dictionary backing storage.
|
||||
// This function may return false negatives, so miss_label
|
||||
// must always call a backup property load that is complete.
|
||||
// This function is safe to call if the receiver has fast properties,
|
||||
// or if name is not a symbol, and will jump to the miss_label in that case.
|
||||
// This function is safe to call if name is not a symbol, and will jump to
|
||||
// the miss_label in that case.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
Label* miss_label,
|
||||
Register receiver,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1,
|
||||
Register r2,
|
||||
Register result,
|
||||
DictionaryCheck check_dictionary) {
|
||||
Register result) {
|
||||
// Register use:
|
||||
//
|
||||
// name - holds the name of the property and is unchanged.
|
||||
// receiver - holds the receiver and is unchanged.
|
||||
// elements - holds the property dictionary on entry and is unchanged.
|
||||
//
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// Scratch registers:
|
||||
// r0 - used to hold the property dictionary.
|
||||
//
|
||||
// r1 - used for the index into the property dictionary
|
||||
// r0 - used for the index into the property dictionary
|
||||
//
|
||||
// r2 - used to hold the capacity of the property dictionary.
|
||||
// r1 - used to hold the capacity of the property dictionary.
|
||||
//
|
||||
// result - holds the result on exit.
|
||||
|
||||
Label done;
|
||||
|
||||
// Check for the absence of an interceptor.
|
||||
// Load the map into r0.
|
||||
__ mov(r0, FieldOperand(receiver, JSObject::kMapOffset));
|
||||
|
||||
// Bail out if the receiver has a named interceptor.
|
||||
__ test(FieldOperand(r0, Map::kBitFieldOffset),
|
||||
Immediate(1 << Map::kHasNamedInterceptor));
|
||||
__ j(not_zero, miss_label, not_taken);
|
||||
|
||||
// Bail out if we have a JS global proxy object.
|
||||
__ movzx_b(r0, FieldOperand(r0, Map::kInstanceTypeOffset));
|
||||
__ cmp(r0, JS_GLOBAL_PROXY_TYPE);
|
||||
__ j(equal, miss_label, not_taken);
|
||||
|
||||
// Possible work-around for http://crbug.com/16276.
|
||||
__ cmp(r0, JS_GLOBAL_OBJECT_TYPE);
|
||||
__ j(equal, miss_label, not_taken);
|
||||
__ cmp(r0, JS_BUILTINS_OBJECT_TYPE);
|
||||
__ j(equal, miss_label, not_taken);
|
||||
|
||||
// Load properties array.
|
||||
__ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
// Check that the properties array is a dictionary.
|
||||
if (check_dictionary == CHECK_DICTIONARY) {
|
||||
__ cmp(FieldOperand(r0, HeapObject::kMapOffset),
|
||||
Immediate(Factory::hash_table_map()));
|
||||
__ j(not_equal, miss_label);
|
||||
}
|
||||
|
||||
// Compute the capacity mask.
|
||||
const int kCapacityOffset =
|
||||
StringDictionary::kHeaderSize +
|
||||
StringDictionary::kCapacityIndex * kPointerSize;
|
||||
__ mov(r2, FieldOperand(r0, kCapacityOffset));
|
||||
__ shr(r2, kSmiTagSize); // convert smi to int
|
||||
__ dec(r2);
|
||||
__ mov(r1, FieldOperand(elements, kCapacityOffset));
|
||||
__ shr(r1, kSmiTagSize); // convert smi to int
|
||||
__ dec(r1);
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
@ -121,20 +145,20 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
StringDictionary::kElementsStartIndex * kPointerSize;
|
||||
for (int i = 0; i < kProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ mov(r1, FieldOperand(name, String::kHashFieldOffset));
|
||||
__ shr(r1, String::kHashShift);
|
||||
__ mov(r0, FieldOperand(name, String::kHashFieldOffset));
|
||||
__ shr(r0, String::kHashShift);
|
||||
if (i > 0) {
|
||||
__ add(Operand(r1), Immediate(StringDictionary::GetProbeOffset(i)));
|
||||
__ add(Operand(r0), Immediate(StringDictionary::GetProbeOffset(i)));
|
||||
}
|
||||
__ and_(r1, Operand(r2));
|
||||
__ and_(r0, Operand(r1));
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
ASSERT(StringDictionary::kEntrySize == 3);
|
||||
__ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3
|
||||
__ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ cmp(name,
|
||||
Operand(r0, r1, times_4, kElementsStartOffset - kHeapObjectTag));
|
||||
__ cmp(name, Operand(elements, r0, times_4,
|
||||
kElementsStartOffset - kHeapObjectTag));
|
||||
if (i != kProbes - 1) {
|
||||
__ j(equal, &done, taken);
|
||||
} else {
|
||||
@ -145,13 +169,13 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
// Check that the value is a normal property.
|
||||
__ bind(&done);
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ test(Operand(r0, r1, times_4, kDetailsOffset - kHeapObjectTag),
|
||||
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
|
||||
Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize));
|
||||
__ j(not_zero, miss_label, not_taken);
|
||||
|
||||
// Get the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ mov(result, Operand(r0, r1, times_4, kValueOffset - kHeapObjectTag));
|
||||
__ mov(result, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
|
||||
}
|
||||
|
||||
|
||||
@ -307,6 +331,7 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
|
||||
static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
||||
Register receiver,
|
||||
Register map,
|
||||
int interceptor_bit,
|
||||
Label* slow) {
|
||||
// Register use:
|
||||
// receiver - holds the receiver and is unchanged.
|
||||
@ -322,7 +347,7 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
||||
|
||||
// Check bit field.
|
||||
__ test_b(FieldOperand(map, Map::kBitFieldOffset),
|
||||
KeyedLoadIC::kSlowCaseBitFieldMask);
|
||||
(1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit));
|
||||
__ j(not_zero, slow, not_taken);
|
||||
// Check that the object is some kind of JS object EXCEPT JS Value type.
|
||||
// In the case that the object is a value-wrapper object,
|
||||
@ -432,8 +457,6 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
Label slow, check_string, index_smi, index_string;
|
||||
Label check_pixel_array, probe_dictionary, check_number_dictionary;
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, edx, ecx, &slow);
|
||||
|
||||
// Check that the key is a smi.
|
||||
__ test(eax, Immediate(kSmiTagMask));
|
||||
__ j(not_zero, &check_string, not_taken);
|
||||
@ -441,6 +464,9 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
// Now the key is known to be a smi. This place is also jumped to from
|
||||
// where a numeric string is converted to a smi.
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, edx, ecx, Map::kHasIndexedInterceptor, &slow);
|
||||
|
||||
GenerateFastArrayLoad(masm,
|
||||
edx,
|
||||
eax,
|
||||
@ -503,6 +529,9 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
__ bind(&check_string);
|
||||
GenerateKeyStringCheck(masm, eax, ecx, ebx, &index_string, &slow);
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, edx, ecx, Map::kHasNamedInterceptor, &slow);
|
||||
|
||||
// If the receiver is a fast-case object, check the keyed lookup
|
||||
// cache. Otherwise probe the dictionary.
|
||||
__ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset));
|
||||
@ -555,15 +584,12 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
// Do a quick inline probe of the receiver's dictionary, if it
|
||||
// exists.
|
||||
__ bind(&probe_dictionary);
|
||||
GenerateDictionaryLoad(masm,
|
||||
&slow,
|
||||
edx,
|
||||
eax,
|
||||
ebx,
|
||||
ecx,
|
||||
edi,
|
||||
eax,
|
||||
DICTIONARY_CHECK_DONE);
|
||||
|
||||
__ mov(ecx, FieldOperand(edx, JSObject::kMapOffset));
|
||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||
GenerateGlobalInstanceTypeCheck(masm, ecx, &slow);
|
||||
|
||||
GenerateDictionaryLoad(masm, &slow, ebx, eax, ecx, edi, eax);
|
||||
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
|
||||
__ ret(0);
|
||||
|
||||
@ -1173,24 +1199,18 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
static void GenerateNormalHelper(MacroAssembler* masm,
|
||||
int argc,
|
||||
bool is_global_object,
|
||||
Label* miss) {
|
||||
static void GenerateFunctionTailCall(MacroAssembler* masm,
|
||||
int argc,
|
||||
Label* miss) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- edx : receiver
|
||||
// -- edi : function
|
||||
// -- esp[0] : return address
|
||||
// -- esp[(argc - n) * 4] : arg[n] (zero-based)
|
||||
// -- ...
|
||||
// -- esp[(argc + 1) * 4] : receiver
|
||||
// -----------------------------------
|
||||
|
||||
// Search dictionary - put result in register edi.
|
||||
__ mov(edi, edx);
|
||||
GenerateDictionaryLoad(
|
||||
masm, miss, edx, ecx, eax, edi, ebx, edi, CHECK_DICTIONARY);
|
||||
|
||||
// Check that the result is not a smi.
|
||||
__ test(edi, Immediate(kSmiTagMask));
|
||||
__ j(zero, miss, not_taken);
|
||||
@ -1199,12 +1219,6 @@ static void GenerateNormalHelper(MacroAssembler* masm,
|
||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
|
||||
__ j(not_equal, miss, not_taken);
|
||||
|
||||
// Patch the receiver on stack with the global proxy if necessary.
|
||||
if (is_global_object) {
|
||||
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
|
||||
}
|
||||
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ InvokeFunction(edi, actual, JUMP_FUNCTION);
|
||||
@ -1219,55 +1233,17 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
|
||||
// -- ...
|
||||
// -- esp[(argc + 1) * 4] : receiver
|
||||
// -----------------------------------
|
||||
Label miss, global_object, non_global_object;
|
||||
Label miss;
|
||||
|
||||
// Get the receiver of the function from the stack; 1 ~ return address.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ test(edx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &miss, not_taken);
|
||||
GenerateDictionaryLoadReceiverCheck(masm, edx, eax, ebx, &miss);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
|
||||
__ movzx_b(eax, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ cmp(eax, FIRST_JS_OBJECT_TYPE);
|
||||
__ j(below, &miss, not_taken);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
// Check for access to global object.
|
||||
__ cmp(eax, JS_GLOBAL_OBJECT_TYPE);
|
||||
__ j(equal, &global_object);
|
||||
__ cmp(eax, JS_BUILTINS_OBJECT_TYPE);
|
||||
__ j(not_equal, &non_global_object);
|
||||
|
||||
// Accessing global object: Load and invoke.
|
||||
__ bind(&global_object);
|
||||
// Check that the global object does not require access checks.
|
||||
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
__ j(not_equal, &miss, not_taken);
|
||||
GenerateNormalHelper(masm, argc, true, &miss);
|
||||
|
||||
// Accessing non-global object: Check for access to global proxy.
|
||||
Label global_proxy, invoke;
|
||||
__ bind(&non_global_object);
|
||||
__ cmp(eax, JS_GLOBAL_PROXY_TYPE);
|
||||
__ j(equal, &global_proxy, not_taken);
|
||||
// Check that the non-global, non-global-proxy object does not
|
||||
// require access checks.
|
||||
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
__ j(not_equal, &miss, not_taken);
|
||||
__ bind(&invoke);
|
||||
GenerateNormalHelper(masm, argc, false, &miss);
|
||||
|
||||
// Global object proxy access: Check access rights.
|
||||
__ bind(&global_proxy);
|
||||
__ CheckAccessGlobalProxy(edx, eax, &miss);
|
||||
__ jmp(&invoke);
|
||||
// eax: elements
|
||||
// Search the dictionary placing the result in edi.
|
||||
GenerateDictionaryLoad(masm, &miss, eax, ecx, edi, ebx, edi);
|
||||
GenerateFunctionTailCall(masm, argc, &miss);
|
||||
|
||||
__ bind(&miss);
|
||||
}
|
||||
@ -1282,6 +1258,12 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
// -- esp[(argc + 1) * 4] : receiver
|
||||
// -----------------------------------
|
||||
|
||||
if (id == IC::kCallIC_Miss) {
|
||||
__ IncrementCounter(&Counters::call_miss, 1);
|
||||
} else {
|
||||
__ IncrementCounter(&Counters::keyed_call_miss, 1);
|
||||
}
|
||||
|
||||
// Get the receiver of the function from the stack; 1 ~ return address.
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
||||
|
||||
@ -1303,25 +1285,28 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
// Check if the receiver is a global object of some sort.
|
||||
Label invoke, global;
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver
|
||||
__ test(edx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &invoke, not_taken);
|
||||
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
|
||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ cmp(ebx, JS_GLOBAL_OBJECT_TYPE);
|
||||
__ j(equal, &global);
|
||||
__ cmp(ebx, JS_BUILTINS_OBJECT_TYPE);
|
||||
__ j(not_equal, &invoke);
|
||||
// This can happen only for regular CallIC but not KeyedCallIC.
|
||||
if (id == IC::kCallIC_Miss) {
|
||||
Label invoke, global;
|
||||
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); // receiver
|
||||
__ test(edx, Immediate(kSmiTagMask));
|
||||
__ j(zero, &invoke, not_taken);
|
||||
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
|
||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ cmp(ebx, JS_GLOBAL_OBJECT_TYPE);
|
||||
__ j(equal, &global);
|
||||
__ cmp(ebx, JS_BUILTINS_OBJECT_TYPE);
|
||||
__ j(not_equal, &invoke);
|
||||
|
||||
// Patch the receiver on the stack.
|
||||
__ bind(&global);
|
||||
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
|
||||
// Patch the receiver on the stack.
|
||||
__ bind(&global);
|
||||
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
|
||||
__ bind(&invoke);
|
||||
}
|
||||
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ bind(&invoke);
|
||||
__ InvokeFunction(edi, actual, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
@ -1393,7 +1378,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// Now the key is known to be a smi. This place is also jumped to from
|
||||
// where a numeric string is converted to a smi.
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, edx, eax, &slow_call);
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, edx, eax, Map::kHasIndexedInterceptor, &slow_call);
|
||||
|
||||
GenerateFastArrayLoad(
|
||||
masm, edx, ecx, eax, edi, &check_number_dictionary, &slow_load);
|
||||
@ -1403,15 +1389,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// receiver in edx is not used after this point.
|
||||
// ecx: key
|
||||
// edi: function
|
||||
|
||||
// Check that the value in edi is a JavaScript function.
|
||||
__ test(edi, Immediate(kSmiTagMask));
|
||||
__ j(zero, &slow_call, not_taken);
|
||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, eax);
|
||||
__ j(not_equal, &slow_call, not_taken);
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ InvokeFunction(edi, actual, JUMP_FUNCTION);
|
||||
GenerateFunctionTailCall(masm, argc, &slow_call);
|
||||
|
||||
__ bind(&check_number_dictionary);
|
||||
// eax: elements
|
||||
@ -1451,15 +1429,13 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// If the receiver is a regular JS object with slow properties then do
|
||||
// a quick inline probe of the receiver's dictionary.
|
||||
// Otherwise do the monomorphic cache probe.
|
||||
GenerateKeyedLoadReceiverCheck(masm, edx, eax, &lookup_monomorphic_cache);
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, edx, eax, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
|
||||
|
||||
__ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset));
|
||||
__ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
|
||||
Immediate(Factory::hash_table_map()));
|
||||
__ j(not_equal, &lookup_monomorphic_cache, not_taken);
|
||||
__ CheckMap(ebx, Factory::hash_table_map(), &lookup_monomorphic_cache, true);
|
||||
|
||||
GenerateDictionaryLoad(
|
||||
masm, &slow_load, edx, ecx, ebx, eax, edi, edi, DICTIONARY_CHECK_DONE);
|
||||
GenerateDictionaryLoad(masm, &slow_load, ebx, ecx, eax, edi, edi);
|
||||
__ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1);
|
||||
__ jmp(&do_call);
|
||||
|
||||
@ -1539,49 +1515,15 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
// -- ecx : name
|
||||
// -- esp[0] : return address
|
||||
// -----------------------------------
|
||||
Label miss, probe, global;
|
||||
Label miss;
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ test(eax, Immediate(kSmiTagMask));
|
||||
__ j(zero, &miss, not_taken);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
|
||||
__ movzx_b(edx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ cmp(edx, FIRST_JS_OBJECT_TYPE);
|
||||
__ j(less, &miss, not_taken);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
// Check for access to global object (unlikely).
|
||||
__ cmp(edx, JS_GLOBAL_PROXY_TYPE);
|
||||
__ j(equal, &global, not_taken);
|
||||
|
||||
// Check for non-global object that requires access check.
|
||||
__ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
|
||||
1 << Map::kIsAccessCheckNeeded);
|
||||
__ j(not_zero, &miss, not_taken);
|
||||
GenerateDictionaryLoadReceiverCheck(masm, eax, edx, ebx, &miss);
|
||||
|
||||
// edx: elements
|
||||
// Search the dictionary placing the result in eax.
|
||||
__ bind(&probe);
|
||||
GenerateDictionaryLoad(masm,
|
||||
&miss,
|
||||
eax,
|
||||
ecx,
|
||||
edx,
|
||||
edi,
|
||||
ebx,
|
||||
edi,
|
||||
CHECK_DICTIONARY);
|
||||
__ mov(eax, edi);
|
||||
GenerateDictionaryLoad(masm, &miss, edx, ecx, edi, ebx, eax);
|
||||
__ ret(0);
|
||||
|
||||
// Global object access: Check access rights.
|
||||
__ bind(&global);
|
||||
__ CheckAccessGlobalProxy(eax, edx, &miss);
|
||||
__ jmp(&probe);
|
||||
|
||||
// Cache miss: Jump to runtime.
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
@ -1595,6 +1537,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// -- esp[0] : return address
|
||||
// -----------------------------------
|
||||
|
||||
__ IncrementCounter(&Counters::load_miss, 1);
|
||||
|
||||
__ pop(ebx);
|
||||
__ push(eax); // receiver
|
||||
__ push(ecx); // name
|
||||
@ -1711,6 +1655,8 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// -- esp[0] : return address
|
||||
// -----------------------------------
|
||||
|
||||
__ IncrementCounter(&Counters::keyed_load_miss, 1);
|
||||
|
||||
__ pop(ebx);
|
||||
__ push(edx); // receiver
|
||||
__ push(eax); // name
|
||||
|
4
src/ic.h
4
src/ic.h
@ -33,10 +33,6 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Flag indicating whether an IC stub needs to check that a backing
|
||||
// store is in dictionary case.
|
||||
enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE };
|
||||
|
||||
|
||||
// IC_UTIL_LIST defines all utility functions called from generated
|
||||
// inline caching code. The argument for the macro, ICU, is the function name.
|
||||
|
@ -153,6 +153,10 @@ namespace internal {
|
||||
SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
|
||||
SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
|
||||
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
|
||||
SC(call_miss, V8.CallMiss) \
|
||||
SC(keyed_call_miss, V8.KeyedCallMiss) \
|
||||
SC(load_miss, V8.LoadMiss) \
|
||||
SC(keyed_load_miss, V8.KeyedLoadMiss) \
|
||||
SC(call_const, V8.CallConst) \
|
||||
SC(call_const_fast_api, V8.CallConstFastApi) \
|
||||
SC(call_const_interceptor, V8.CallConstInterceptor) \
|
||||
|
@ -45,71 +45,93 @@ namespace internal {
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
|
||||
static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
|
||||
Register type,
|
||||
Label* global_object) {
|
||||
// Register usage:
|
||||
// type: holds the receiver instance type on entry.
|
||||
__ cmpb(type, Immediate(JS_GLOBAL_OBJECT_TYPE));
|
||||
__ j(equal, global_object);
|
||||
__ cmpb(type, Immediate(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ j(equal, global_object);
|
||||
__ cmpb(type, Immediate(JS_GLOBAL_PROXY_TYPE));
|
||||
__ j(equal, global_object);
|
||||
}
|
||||
|
||||
|
||||
// Generated code falls through if the receiver is a regular non-global
|
||||
// JS object with slow properties and no interceptors.
|
||||
static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
|
||||
Register receiver,
|
||||
Register r0,
|
||||
Register r1,
|
||||
Label* miss) {
|
||||
// Register usage:
|
||||
// receiver: holds the receiver on entry and is unchanged.
|
||||
// r0: used to hold receiver instance type.
|
||||
// Holds the property dictionary on fall through.
|
||||
// r1: used to hold receivers map.
|
||||
|
||||
__ JumpIfSmi(receiver, miss);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ movq(r1, FieldOperand(receiver, HeapObject::kMapOffset));
|
||||
__ movb(r0, FieldOperand(r1, Map::kInstanceTypeOffset));
|
||||
__ cmpb(r0, Immediate(FIRST_JS_OBJECT_TYPE));
|
||||
__ j(below, miss);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
GenerateGlobalInstanceTypeCheck(masm, r0, miss);
|
||||
|
||||
// Check for non-global object that requires access check.
|
||||
__ testb(FieldOperand(r1, Map::kBitFieldOffset),
|
||||
Immediate((1 << Map::kIsAccessCheckNeeded) |
|
||||
(1 << Map::kHasNamedInterceptor)));
|
||||
__ j(not_zero, miss);
|
||||
|
||||
__ movq(r0, FieldOperand(receiver, JSObject::kPropertiesOffset));
|
||||
__ CompareRoot(FieldOperand(r0, HeapObject::kMapOffset),
|
||||
Heap::kHashTableMapRootIndex);
|
||||
__ j(not_equal, miss);
|
||||
}
|
||||
|
||||
|
||||
// Helper function used to load a property from a dictionary backing storage.
|
||||
// This function may return false negatives, so miss_label
|
||||
// must always call a backup property load that is complete.
|
||||
// This function is safe to call if the receiver has fast properties,
|
||||
// or if name is not a symbol, and will jump to the miss_label in that case.
|
||||
// This function is safe to call if name is not a symbol, and will jump to
|
||||
// the miss_label in that case.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
Label* miss_label,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1,
|
||||
Register r2,
|
||||
Register name,
|
||||
Register r4,
|
||||
Register result,
|
||||
DictionaryCheck check_dictionary) {
|
||||
Register result) {
|
||||
// Register use:
|
||||
//
|
||||
// r0 - used to hold the property dictionary and is unchanged.
|
||||
// elements - holds the property dictionary on entry and is unchanged.
|
||||
//
|
||||
// r1 - used to hold the receiver and is unchanged.
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// r2 - used to hold the capacity of the property dictionary.
|
||||
// r0 - used to hold the capacity of the property dictionary.
|
||||
//
|
||||
// name - holds the name of the property and is unchanged.
|
||||
//
|
||||
// r4 - used to hold the index into the property dictionary.
|
||||
// r1 - used to hold the index into the property dictionary.
|
||||
//
|
||||
// result - holds the result on exit if the load succeeded.
|
||||
|
||||
Label done;
|
||||
|
||||
// Check for the absence of an interceptor.
|
||||
// Load the map into r0.
|
||||
__ movq(r0, FieldOperand(r1, JSObject::kMapOffset));
|
||||
|
||||
// Bail out if the receiver has a named interceptor.
|
||||
__ testl(FieldOperand(r0, Map::kBitFieldOffset),
|
||||
Immediate(1 << Map::kHasNamedInterceptor));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Bail out if we have a JS global proxy object.
|
||||
__ movzxbq(r0, FieldOperand(r0, Map::kInstanceTypeOffset));
|
||||
__ cmpb(r0, Immediate(JS_GLOBAL_PROXY_TYPE));
|
||||
__ j(equal, miss_label);
|
||||
|
||||
// Possible work-around for http://crbug.com/16276.
|
||||
__ cmpb(r0, Immediate(JS_GLOBAL_OBJECT_TYPE));
|
||||
__ j(equal, miss_label);
|
||||
__ cmpb(r0, Immediate(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ j(equal, miss_label);
|
||||
|
||||
// Load properties array.
|
||||
__ movq(r0, FieldOperand(r1, JSObject::kPropertiesOffset));
|
||||
|
||||
if (check_dictionary == CHECK_DICTIONARY) {
|
||||
// Check that the properties array is a dictionary.
|
||||
__ Cmp(FieldOperand(r0, HeapObject::kMapOffset), Factory::hash_table_map());
|
||||
__ j(not_equal, miss_label);
|
||||
}
|
||||
|
||||
// Compute the capacity mask.
|
||||
const int kCapacityOffset =
|
||||
StringDictionary::kHeaderSize +
|
||||
StringDictionary::kCapacityIndex * kPointerSize;
|
||||
__ SmiToInteger32(r2, FieldOperand(r0, kCapacityOffset));
|
||||
__ decl(r2);
|
||||
__ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset));
|
||||
__ decl(r0);
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
@ -120,19 +142,19 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
StringDictionary::kElementsStartIndex * kPointerSize;
|
||||
for (int i = 0; i < kProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ movl(r4, FieldOperand(name, String::kHashFieldOffset));
|
||||
__ shrl(r4, Immediate(String::kHashShift));
|
||||
__ movl(r1, FieldOperand(name, String::kHashFieldOffset));
|
||||
__ shrl(r1, Immediate(String::kHashShift));
|
||||
if (i > 0) {
|
||||
__ addl(r4, Immediate(StringDictionary::GetProbeOffset(i)));
|
||||
__ addl(r1, Immediate(StringDictionary::GetProbeOffset(i)));
|
||||
}
|
||||
__ and_(r4, r2);
|
||||
__ and_(r1, r0);
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
ASSERT(StringDictionary::kEntrySize == 3);
|
||||
__ lea(r4, Operand(r4, r4, times_2, 0)); // r4 = r4 * 3
|
||||
__ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ cmpq(name, Operand(r0, r4, times_pointer_size,
|
||||
__ cmpq(name, Operand(elements, r1, times_pointer_size,
|
||||
kElementsStartOffset - kHeapObjectTag));
|
||||
if (i != kProbes - 1) {
|
||||
__ j(equal, &done);
|
||||
@ -144,14 +166,16 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
|
||||
// Check that the value is a normal property.
|
||||
__ bind(&done);
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ Test(Operand(r0, r4, times_pointer_size, kDetailsOffset - kHeapObjectTag),
|
||||
__ Test(Operand(elements, r1, times_pointer_size,
|
||||
kDetailsOffset - kHeapObjectTag),
|
||||
Smi::FromInt(PropertyDetails::TypeField::mask()));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Get the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ movq(result,
|
||||
Operand(r0, r4, times_pointer_size, kValueOffset - kHeapObjectTag));
|
||||
Operand(elements, r1, times_pointer_size,
|
||||
kValueOffset - kHeapObjectTag));
|
||||
}
|
||||
|
||||
|
||||
@ -327,6 +351,8 @@ void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// -- rsp[0] : return address
|
||||
// -----------------------------------
|
||||
|
||||
__ IncrementCounter(&Counters::keyed_load_miss, 1);
|
||||
|
||||
__ pop(rbx);
|
||||
__ push(rdx); // receiver
|
||||
__ push(rax); // name
|
||||
@ -360,6 +386,7 @@ void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
||||
Register receiver,
|
||||
Register map,
|
||||
int interceptor_bit,
|
||||
Label* slow) {
|
||||
// Register use:
|
||||
// receiver - holds the receiver and is unchanged.
|
||||
@ -379,7 +406,8 @@ static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
||||
|
||||
// Check bit field.
|
||||
__ testb(FieldOperand(map, Map::kBitFieldOffset),
|
||||
Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
|
||||
Immediate((1 << Map::kIsAccessCheckNeeded) |
|
||||
(1 << interceptor_bit)));
|
||||
__ j(not_zero, slow);
|
||||
}
|
||||
|
||||
@ -500,14 +528,15 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
Label slow, check_string, index_smi, index_string;
|
||||
Label check_pixel_array, probe_dictionary, check_number_dictionary;
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, rdx, rcx, &slow);
|
||||
|
||||
// Check that the key is a smi.
|
||||
__ JumpIfNotSmi(rax, &check_string);
|
||||
__ bind(&index_smi);
|
||||
// Now the key is known to be a smi. This place is also jumped to from below
|
||||
// where a numeric string is converted to a smi.
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, rdx, rcx, Map::kHasIndexedInterceptor, &slow);
|
||||
|
||||
GenerateFastArrayLoad(masm,
|
||||
rdx,
|
||||
rax,
|
||||
@ -557,6 +586,9 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
__ bind(&check_string);
|
||||
GenerateKeyStringCheck(masm, rax, rcx, rbx, &index_string, &slow);
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, rdx, rcx, Map::kHasNamedInterceptor, &slow);
|
||||
|
||||
// If the receiver is a fast-case object, check the keyed lookup
|
||||
// cache. Otherwise probe the dictionary leaving result in rcx.
|
||||
__ movq(rbx, FieldOperand(rdx, JSObject::kPropertiesOffset));
|
||||
@ -608,15 +640,13 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||
__ bind(&probe_dictionary);
|
||||
// rdx: receiver
|
||||
// rax: key
|
||||
GenerateDictionaryLoad(masm,
|
||||
&slow,
|
||||
rbx,
|
||||
rdx,
|
||||
rcx,
|
||||
rax,
|
||||
rdi,
|
||||
rax,
|
||||
DICTIONARY_CHECK_DONE);
|
||||
// rbx: elements
|
||||
|
||||
__ movq(rcx, FieldOperand(rdx, JSObject::kMapOffset));
|
||||
__ movb(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset));
|
||||
GenerateGlobalInstanceTypeCheck(masm, rcx, &slow);
|
||||
|
||||
GenerateDictionaryLoad(masm, &slow, rbx, rax, rcx, rdi, rax);
|
||||
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
|
||||
__ ret(0);
|
||||
|
||||
@ -1212,6 +1242,13 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
// rsp[argc * 8] : argument 1
|
||||
// rsp[(argc + 1) * 8] : argument 0 = receiver
|
||||
// -----------------------------------
|
||||
|
||||
if (id == IC::kCallIC_Miss) {
|
||||
__ IncrementCounter(&Counters::call_miss, 1);
|
||||
} else {
|
||||
__ IncrementCounter(&Counters::keyed_call_miss, 1);
|
||||
}
|
||||
|
||||
// Get the receiver of the function from the stack; 1 ~ return address.
|
||||
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
|
||||
|
||||
@ -1233,22 +1270,25 @@ static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
// Check if the receiver is a global object of some sort.
|
||||
Label invoke, global;
|
||||
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver
|
||||
__ JumpIfSmi(rdx, &invoke);
|
||||
__ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx);
|
||||
__ j(equal, &global);
|
||||
__ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE);
|
||||
__ j(not_equal, &invoke);
|
||||
// This can happen only for regular CallIC but not KeyedCallIC.
|
||||
if (id == IC::kCallIC_Miss) {
|
||||
Label invoke, global;
|
||||
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver
|
||||
__ JumpIfSmi(rdx, &invoke);
|
||||
__ CmpObjectType(rdx, JS_GLOBAL_OBJECT_TYPE, rcx);
|
||||
__ j(equal, &global);
|
||||
__ CmpInstanceType(rcx, JS_BUILTINS_OBJECT_TYPE);
|
||||
__ j(not_equal, &invoke);
|
||||
|
||||
// Patch the receiver on the stack.
|
||||
__ bind(&global);
|
||||
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
|
||||
// Patch the receiver on the stack.
|
||||
__ bind(&global);
|
||||
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
|
||||
__ bind(&invoke);
|
||||
}
|
||||
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ bind(&invoke);
|
||||
__ InvokeFunction(rdi, actual, JUMP_FUNCTION);
|
||||
}
|
||||
|
||||
@ -1309,13 +1349,12 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
||||
}
|
||||
|
||||
|
||||
static void GenerateNormalHelper(MacroAssembler* masm,
|
||||
int argc,
|
||||
bool is_global_object,
|
||||
Label* miss) {
|
||||
static void GenerateFunctionTailCall(MacroAssembler* masm,
|
||||
int argc,
|
||||
Label* miss) {
|
||||
// ----------- S t a t e -------------
|
||||
// rcx : function name
|
||||
// rdx : receiver
|
||||
// rdi : function
|
||||
// rsp[0] : return address
|
||||
// rsp[8] : argument argc
|
||||
// rsp[16] : argument argc - 1
|
||||
@ -1323,21 +1362,11 @@ static void GenerateNormalHelper(MacroAssembler* masm,
|
||||
// rsp[argc * 8] : argument 1
|
||||
// rsp[(argc + 1) * 8] : argument 0 = receiver
|
||||
// -----------------------------------
|
||||
// Search dictionary - put result in register rdx.
|
||||
GenerateDictionaryLoad(
|
||||
masm, miss, rax, rdx, rbx, rcx, rdi, rdi, CHECK_DICTIONARY);
|
||||
|
||||
__ JumpIfSmi(rdi, miss);
|
||||
// Check that the value is a JavaScript function.
|
||||
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rdx);
|
||||
__ j(not_equal, miss);
|
||||
|
||||
// Patch the receiver with the global proxy if necessary.
|
||||
if (is_global_object) {
|
||||
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
|
||||
__ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx);
|
||||
}
|
||||
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ InvokeFunction(rdi, actual, JUMP_FUNCTION);
|
||||
@ -1355,56 +1384,18 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
|
||||
// rsp[argc * 8] : argument 1
|
||||
// rsp[(argc + 1) * 8] : argument 0 = receiver
|
||||
// -----------------------------------
|
||||
Label miss, global_object, non_global_object;
|
||||
Label miss;
|
||||
|
||||
// Get the receiver of the function from the stack.
|
||||
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ JumpIfSmi(rdx, &miss);
|
||||
GenerateDictionaryLoadReceiverCheck(masm, rdx, rax, rbx, &miss);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
// Because there are so many map checks and type checks, do not
|
||||
// use CmpObjectType, but load map and type into registers.
|
||||
__ movq(rbx, FieldOperand(rdx, HeapObject::kMapOffset));
|
||||
__ movb(rax, FieldOperand(rbx, Map::kInstanceTypeOffset));
|
||||
__ cmpb(rax, Immediate(FIRST_JS_OBJECT_TYPE));
|
||||
__ j(below, &miss);
|
||||
// rax: elements
|
||||
// Search the dictionary placing the result in rdi.
|
||||
GenerateDictionaryLoad(masm, &miss, rax, rcx, rbx, rdi, rdi);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
// Check for access to global object.
|
||||
__ cmpb(rax, Immediate(JS_GLOBAL_OBJECT_TYPE));
|
||||
__ j(equal, &global_object);
|
||||
__ cmpb(rax, Immediate(JS_BUILTINS_OBJECT_TYPE));
|
||||
__ j(not_equal, &non_global_object);
|
||||
|
||||
// Accessing global object: Load and invoke.
|
||||
__ bind(&global_object);
|
||||
// Check that the global object does not require access checks.
|
||||
__ movb(rbx, FieldOperand(rbx, Map::kBitFieldOffset));
|
||||
__ testb(rbx, Immediate(1 << Map::kIsAccessCheckNeeded));
|
||||
__ j(not_equal, &miss);
|
||||
GenerateNormalHelper(masm, argc, true, &miss);
|
||||
|
||||
// Accessing non-global object: Check for access to global proxy.
|
||||
Label global_proxy, invoke;
|
||||
__ bind(&non_global_object);
|
||||
__ cmpb(rax, Immediate(JS_GLOBAL_PROXY_TYPE));
|
||||
__ j(equal, &global_proxy);
|
||||
// Check that the non-global, non-global-proxy object does not
|
||||
// require access checks.
|
||||
__ movb(rbx, FieldOperand(rbx, Map::kBitFieldOffset));
|
||||
__ testb(rbx, Immediate(1 << Map::kIsAccessCheckNeeded));
|
||||
__ j(not_equal, &miss);
|
||||
__ bind(&invoke);
|
||||
GenerateNormalHelper(masm, argc, false, &miss);
|
||||
|
||||
// Global object proxy access: Check access rights.
|
||||
__ bind(&global_proxy);
|
||||
__ CheckAccessGlobalProxy(rdx, rax, &miss);
|
||||
__ jmp(&invoke);
|
||||
GenerateFunctionTailCall(masm, argc, &miss);
|
||||
|
||||
__ bind(&miss);
|
||||
}
|
||||
@ -1498,7 +1489,8 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// Now the key is known to be a smi. This place is also jumped to from below
|
||||
// where a numeric string is converted to a smi.
|
||||
|
||||
GenerateKeyedLoadReceiverCheck(masm, rdx, rax, &slow_call);
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, rdx, rax, Map::kHasIndexedInterceptor, &slow_call);
|
||||
|
||||
GenerateFastArrayLoad(
|
||||
masm, rdx, rcx, rax, rbx, rdi, &check_number_dictionary, &slow_load);
|
||||
@ -1508,14 +1500,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// receiver in rdx is not used after this point.
|
||||
// rcx: key
|
||||
// rdi: function
|
||||
|
||||
// Check that the value in edi is a JavaScript function.
|
||||
__ JumpIfSmi(rdi, &slow_call);
|
||||
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
|
||||
__ j(not_equal, &slow_call);
|
||||
// Invoke the function.
|
||||
ParameterCount actual(argc);
|
||||
__ InvokeFunction(rdi, actual, JUMP_FUNCTION);
|
||||
GenerateFunctionTailCall(masm, argc, &slow_call);
|
||||
|
||||
__ bind(&check_number_dictionary);
|
||||
// eax: elements
|
||||
@ -1523,6 +1508,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// Check whether the elements is a number dictionary.
|
||||
__ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
|
||||
Heap::kHashTableMapRootIndex);
|
||||
__ j(not_equal, &slow_load);
|
||||
__ SmiToInteger32(rbx, rcx);
|
||||
// ebx: untagged index
|
||||
GenerateNumberDictionaryLoad(masm, &slow_load, rax, rcx, rbx, r9, rdi, rdi);
|
||||
@ -1550,15 +1536,15 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
||||
// If the receiver is a regular JS object with slow properties then do
|
||||
// a quick inline probe of the receiver's dictionary.
|
||||
// Otherwise do the monomorphic cache probe.
|
||||
GenerateKeyedLoadReceiverCheck(masm, rdx, rax, &lookup_monomorphic_cache);
|
||||
GenerateKeyedLoadReceiverCheck(
|
||||
masm, rdx, rax, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
|
||||
|
||||
__ movq(rbx, FieldOperand(rdx, JSObject::kPropertiesOffset));
|
||||
__ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
|
||||
Heap::kHashTableMapRootIndex);
|
||||
__ j(not_equal, &lookup_monomorphic_cache);
|
||||
|
||||
GenerateDictionaryLoad(
|
||||
masm, &slow_load, rbx, rdx, rax, rcx, rdi, rdi, DICTIONARY_CHECK_DONE);
|
||||
GenerateDictionaryLoad(masm, &slow_load, rbx, rcx, rax, rdi, rdi);
|
||||
__ IncrementCounter(&Counters::keyed_call_generic_lookup_dict, 1);
|
||||
__ jmp(&do_call);
|
||||
|
||||
@ -1620,6 +1606,8 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// -- rsp[0] : return address
|
||||
// -----------------------------------
|
||||
|
||||
__ IncrementCounter(&Counters::load_miss, 1);
|
||||
|
||||
__ pop(rbx);
|
||||
__ push(rax); // receiver
|
||||
__ push(rcx); // name
|
||||
@ -1683,38 +1671,15 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
// -- rcx : name
|
||||
// -- rsp[0] : return address
|
||||
// -----------------------------------
|
||||
Label miss, probe, global;
|
||||
Label miss;
|
||||
|
||||
// Check that the receiver isn't a smi.
|
||||
__ JumpIfSmi(rax, &miss);
|
||||
|
||||
// Check that the receiver is a valid JS object.
|
||||
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx);
|
||||
__ j(below, &miss);
|
||||
|
||||
// If this assert fails, we have to check upper bound too.
|
||||
ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
||||
|
||||
// Check for access to global object (unlikely).
|
||||
__ CmpInstanceType(rbx, JS_GLOBAL_PROXY_TYPE);
|
||||
__ j(equal, &global);
|
||||
|
||||
// Check for non-global object that requires access check.
|
||||
__ testl(FieldOperand(rbx, Map::kBitFieldOffset),
|
||||
Immediate(1 << Map::kIsAccessCheckNeeded));
|
||||
__ j(not_zero, &miss);
|
||||
GenerateDictionaryLoadReceiverCheck(masm, rax, rdx, rbx, &miss);
|
||||
|
||||
// rdx: elements
|
||||
// Search the dictionary placing the result in rax.
|
||||
__ bind(&probe);
|
||||
GenerateDictionaryLoad(masm, &miss, rdx, rax, rbx,
|
||||
rcx, rdi, rax, CHECK_DICTIONARY);
|
||||
GenerateDictionaryLoad(masm, &miss, rdx, rcx, rbx, rdi, rax);
|
||||
__ ret(0);
|
||||
|
||||
// Global object access: Check access rights.
|
||||
__ bind(&global);
|
||||
__ CheckAccessGlobalProxy(rax, rdx, &miss);
|
||||
__ jmp(&probe);
|
||||
|
||||
// Cache miss: Jump to runtime.
|
||||
__ bind(&miss);
|
||||
GenerateMiss(masm);
|
||||
|
Loading…
Reference in New Issue
Block a user