| Index: src/arm/ic-arm.cc
|
| ===================================================================
|
| --- src/arm/ic-arm.cc (revision 4772)
|
| +++ src/arm/ic-arm.cc (working copy)
|
| @@ -48,59 +48,70 @@
|
| #define __ ACCESS_MASM(masm)
|
|
|
| // Helper function used from LoadIC/CallIC GenerateNormal.
|
| +// receiver: Receiver. 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
|
| +// 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
|
| +// result.
|
| static void GenerateDictionaryLoad(MacroAssembler* masm,
|
| Label* miss,
|
| - Register t0,
|
| - Register t1) {
|
| - // Register use:
|
| - //
|
| - // t0 - used to hold the property dictionary.
|
| - //
|
| - // t1 - initially the receiver.
|
| - // - holds the result on exit.
|
| - //
|
| - // r3 - used as temporary and to hold the capacity of the property
|
| - // dictionary.
|
| - //
|
| - // r2 - initially holds the name of the property and is unchanged.
|
| - // r4 - used to hold the index into the property dictionary.
|
| + Register receiver,
|
| + Register name,
|
| + Register result,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Register scratch3,
|
| + DictionaryCheck check_dictionary) {
|
| + // 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
|
| + // dictionary.
|
| + // scratch3: Used as temporary.
|
|
|
| Label done;
|
|
|
| // Check for the absence of an interceptor.
|
| - // Load the map into t0.
|
| - __ ldr(t0, FieldMemOperand(t1, JSObject::kMapOffset));
|
| + // Load the map into scratch1.
|
| + __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kMapOffset));
|
|
|
| // Bail out if the receiver has a named interceptor.
|
| - __ ldrb(r3, FieldMemOperand(t0, Map::kBitFieldOffset));
|
| - __ tst(r3, Operand(1 << Map::kHasNamedInterceptor));
|
| + __ 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(r3, FieldMemOperand(t0, Map::kInstanceTypeOffset));
|
| - __ cmp(r3, Operand(JS_GLOBAL_PROXY_TYPE));
|
| + __ 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(r3, Operand(JS_GLOBAL_OBJECT_TYPE));
|
| + __ cmp(scratch2, Operand(JS_GLOBAL_OBJECT_TYPE));
|
| __ b(eq, miss);
|
| - __ cmp(r3, Operand(JS_BUILTINS_OBJECT_TYPE));
|
| + __ 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.
|
| - __ ldr(t0, FieldMemOperand(t1, JSObject::kPropertiesOffset));
|
| - __ ldr(r3, FieldMemOperand(t0, HeapObject::kMapOffset));
|
| - __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
|
| - __ cmp(r3, ip);
|
| - __ b(ne, miss);
|
| + 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(r3, FieldMemOperand(t0, kCapacityOffset));
|
| - __ mov(r3, Operand(r3, ASR, kSmiTagSize)); // convert smi to int
|
| - __ sub(r3, r3, Operand(1));
|
| + __ ldr(scratch2, FieldMemOperand(scratch1, kCapacityOffset));
|
| + __ mov(scratch2, Operand(scratch2, ASR, kSmiTagSize)); // convert smi to int
|
| + __ sub(scratch2, scratch2, Operand(1));
|
|
|
| const int kElementsStartOffset = StringDictionary::kHeaderSize +
|
| StringDictionary::kElementsStartIndex * kPointerSize;
|
| @@ -111,26 +122,27 @@
|
| static const int kProbes = 4;
|
| for (int i = 0; i < kProbes; i++) {
|
| // Compute the masked index: (hash + i + i * i) & mask.
|
| - __ ldr(r4, FieldMemOperand(r2, String::kHashFieldOffset));
|
| + __ ldr(scratch3, 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(r4, r4, Operand(
|
| + __ add(scratch3, scratch3, Operand(
|
| StringDictionary::GetProbeOffset(i) << String::kHashShift));
|
| }
|
| - __ and_(r4, r3, Operand(r4, LSR, String::kHashShift));
|
| + __ and_(scratch3, scratch2, Operand(scratch3, LSR, String::kHashShift));
|
|
|
| // Scale the index by multiplying by the element size.
|
| ASSERT(StringDictionary::kEntrySize == 3);
|
| - __ add(r4, r4, Operand(r4, LSL, 1)); // r4 = r4 * 3
|
| + // scratch3 = scratch3 * 3.
|
| + __ add(scratch3, scratch3, Operand(scratch3, LSL, 1));
|
|
|
| // Check if the key is identical to the name.
|
| - __ add(r4, t0, Operand(r4, LSL, 2));
|
| - __ ldr(ip, FieldMemOperand(r4, kElementsStartOffset));
|
| - __ cmp(r2, Operand(ip));
|
| + __ add(scratch3, scratch1, Operand(scratch3, LSL, 2));
|
| + __ ldr(ip, FieldMemOperand(scratch3, kElementsStartOffset));
|
| + __ cmp(name, Operand(ip));
|
| if (i != kProbes - 1) {
|
| __ b(eq, &done);
|
| } else {
|
| @@ -139,13 +151,15 @@
|
| }
|
|
|
| // Check that the value is a normal property.
|
| - __ bind(&done); // r4 == t0 + 4*index
|
| - __ ldr(r3, FieldMemOperand(r4, kElementsStartOffset + 2 * kPointerSize));
|
| - __ tst(r3, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
|
| + __ bind(&done); // scratch3 == scratch1 + 4 * index
|
| + __ ldr(scratch2,
|
| + FieldMemOperand(scratch3, kElementsStartOffset + 2 * kPointerSize));
|
| + __ tst(scratch2, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
|
| __ b(ne, miss);
|
|
|
| // Get the value at the masked, scaled index and return.
|
| - __ ldr(t1, FieldMemOperand(r4, kElementsStartOffset + 1 * kPointerSize));
|
| + __ ldr(result,
|
| + FieldMemOperand(scratch3, kElementsStartOffset + 1 * kPointerSize));
|
| }
|
|
|
|
|
| @@ -353,7 +367,7 @@
|
| Label* miss,
|
| Register scratch) {
|
| // Search dictionary - put result in register r1.
|
| - GenerateDictionaryLoad(masm, miss, r0, r1);
|
| + GenerateDictionaryLoad(masm, miss, r1, r2, r1, r0, r3, r4, CHECK_DICTIONARY);
|
|
|
| // Check that the value isn't a smi.
|
| __ tst(r1, Operand(kSmiTagMask));
|
| @@ -533,7 +547,7 @@
|
| __ b(ne, &miss);
|
|
|
| __ bind(&probe);
|
| - GenerateDictionaryLoad(masm, &miss, r1, r0);
|
| + GenerateDictionaryLoad(masm, &miss, r0, r2, r0, r1, r3, r4, CHECK_DICTIONARY);
|
| __ Ret();
|
|
|
| // Global object access: Check access rights.
|
| @@ -714,7 +728,7 @@
|
|
|
| __ Push(r1, r0);
|
|
|
| - __ TailCallRuntime(Runtime::kGetProperty, 2, 1);
|
| + __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
|
| }
|
|
|
|
|
| @@ -724,7 +738,8 @@
|
| // -- r0 : key
|
| // -- r1 : receiver
|
| // -----------------------------------
|
| - Label slow, fast, check_pixel_array, check_number_dictionary;
|
| + Label slow, check_string, index_smi, index_string;
|
| + Label check_pixel_array, probe_dictionary, check_number_dictionary;
|
|
|
| Register key = r0;
|
| Register receiver = r1;
|
| @@ -747,8 +762,10 @@
|
| __ b(lt, &slow);
|
|
|
| // Check that the key is a smi.
|
| - __ BranchOnNotSmi(key, &slow);
|
| - // Get the elements array of the object.
|
| + __ 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.
|
| __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
| // Check that the object is in fast mode (not dictionary).
|
| __ ldr(r3, FieldMemOperand(r4, HeapObject::kMapOffset));
|
| @@ -770,6 +787,7 @@
|
| // to ensure the prototype chain is searched.
|
| __ b(eq, &slow);
|
| __ mov(r0, r2);
|
| + __ IncrementCounter(&Counters::keyed_load_generic_smi, 1, r2, r3);
|
| __ Ret();
|
|
|
| // Check whether the elements is a pixel array.
|
| @@ -805,6 +823,107 @@
|
| __ bind(&slow);
|
| __ IncrementCounter(&Counters::keyed_load_generic_slow, 1, r2, r3);
|
| GenerateRuntimeGetProperty(masm);
|
| +
|
| + __ bind(&check_string);
|
| + // The key is not a smi.
|
| + // Is it a string?
|
| + // r0: key
|
| + // r1: receiver
|
| + __ CompareObjectType(r0, r2, r3, FIRST_NONSTRING_TYPE);
|
| + __ b(ge, &slow);
|
| +
|
| + // Is the string an array index, with cached numeric value?
|
| + __ ldr(r3, FieldMemOperand(r0, String::kHashFieldOffset));
|
| + __ tst(r3, Operand(String::kIsArrayIndexMask));
|
| + __ b(ne, &index_string);
|
| +
|
| + // Is the string a symbol?
|
| + // r2: key map
|
| + __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset));
|
| + ASSERT(kSymbolTag != 0);
|
| + __ tst(r3, Operand(kIsSymbolMask));
|
| + __ b(eq, &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));
|
| + __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
|
| + __ cmp(r3, ip);
|
| + __ b(eq, &probe_dictionary);
|
| +
|
| + // Load the map of the receiver, compute the keyed lookup cache hash
|
| + // based on 32 bits of the map pointer and the string hash.
|
| + __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| + __ mov(r3, Operand(r2, ASR, KeyedLookupCache::kMapHashShift));
|
| + __ ldr(r4, FieldMemOperand(r0, String::kHashFieldOffset));
|
| + __ eor(r3, r3, Operand(r4, ASR, String::kHashShift));
|
| + __ and_(r3, r3, Operand(KeyedLookupCache::kCapacityMask));
|
| +
|
| + // Load the key (consisting of map and symbol) from the cache and
|
| + // check for match.
|
| + ExternalReference cache_keys = ExternalReference::keyed_lookup_cache_keys();
|
| + __ mov(r4, Operand(cache_keys));
|
| + __ add(r4, r4, Operand(r3, LSL, kPointerSizeLog2 + 1));
|
| + __ ldr(r5, MemOperand(r4, kPointerSize, PostIndex)); // Move r4 to symbol.
|
| + __ cmp(r2, r5);
|
| + __ b(ne, &slow);
|
| + __ ldr(r5, MemOperand(r4));
|
| + __ cmp(r0, r5);
|
| + __ b(ne, &slow);
|
| +
|
| + // Get field offset and check that it is an in-object property.
|
| + // r0 : key
|
| + // r1 : receiver
|
| + // r2 : receiver's map
|
| + // r3 : lookup cache index
|
| + ExternalReference cache_field_offsets
|
| + = ExternalReference::keyed_lookup_cache_field_offsets();
|
| + __ mov(r4, Operand(cache_field_offsets));
|
| + __ ldr(r5, MemOperand(r4, r3, LSL, kPointerSizeLog2));
|
| + __ ldrb(r6, FieldMemOperand(r2, Map::kInObjectPropertiesOffset));
|
| + __ cmp(r5, r6);
|
| + __ b(ge, &slow);
|
| +
|
| + // Load in-object property.
|
| + __ sub(r5, r5, r6); // Index from end of object.
|
| + __ ldrb(r6, FieldMemOperand(r2, Map::kInstanceSizeOffset));
|
| + __ add(r6, r6, r5); // Index from start of object.
|
| + __ sub(r1, r1, Operand(kHeapObjectTag)); // Remove the heap tag.
|
| + __ ldr(r0, MemOperand(r1, r6, LSL, kPointerSizeLog2));
|
| + __ IncrementCounter(&Counters::keyed_load_generic_lookup_cache, 1, r2, r3);
|
| + __ Ret();
|
| +
|
| + // Do a quick inline probe of the receiver's dictionary, if it
|
| + // exists.
|
| + __ bind(&probe_dictionary);
|
| + // Load the property to r0.
|
| + GenerateDictionaryLoad(
|
| + masm, &slow, r1, r0, r0, r2, r3, r4, DICTIONARY_CHECK_DONE);
|
| + __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1, r2, r3);
|
| + __ Ret();
|
| +
|
| + __ b(&slow);
|
| + // If the hash field contains an array index pick it out. The assert checks
|
| + // that the constants for the maximum number of digits for an array index
|
| + // cached in the hash field and the number of bits reserved for it does not
|
| + // conflict.
|
| + ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
|
| + (1 << String::kArrayIndexValueBits));
|
| + __ bind(&index_string);
|
| + // r0: key (string)
|
| + // r1: receiver
|
| + // r3: hash field
|
| + // We want the smi-tagged index in r0. kArrayIndexValueMask has zeros in
|
| + // the low kHashShift bits.
|
| + ASSERT(String::kHashShift >= kSmiTagSize);
|
| + __ and_(r3, r3, Operand(String::kArrayIndexValueMask));
|
| + // Here we actually clobber the key (r0) which will be used if calling into
|
| + // runtime later. However as the new key is the numeric value of a string key
|
| + // there is no difference in using either key.
|
| + __ mov(r0, Operand(r3, ASR, String::kHashShift - kSmiTagSize));
|
| + // Now jump to the place where smi keys are handled.
|
| + __ jmp(&index_smi);
|
| }
|
|
|
|
|
|
|