| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #if V8_TARGET_ARCH_ARM | 5 #if V8_TARGET_ARCH_ARM |
| 6 | 6 |
| 7 #include "src/codegen.h" | 7 #include "src/codegen.h" |
| 8 #include "src/ic/ic.h" | 8 #include "src/ic/ic.h" |
| 9 #include "src/ic/ic-compiler.h" | 9 #include "src/ic/ic-compiler.h" |
| 10 #include "src/ic/stub-cache.h" | 10 #include "src/ic/stub-cache.h" |
| 11 | 11 |
| 12 namespace v8 { | 12 namespace v8 { |
| 13 namespace internal { | 13 namespace internal { |
| 14 | 14 |
| 15 | 15 |
| 16 // ---------------------------------------------------------------------------- | 16 // ---------------------------------------------------------------------------- |
| 17 // Static IC stub generators. | 17 // Static IC stub generators. |
| 18 // | 18 // |
| 19 | 19 |
| 20 #define __ ACCESS_MASM(masm) | 20 #define __ ACCESS_MASM(masm) |
| 21 | 21 |
| 22 | |
| 23 static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type, | |
| 24 Label* global_object) { | |
| 25 // Register usage: | |
| 26 // type: holds the receiver instance type on entry. | |
| 27 __ cmp(type, Operand(JS_GLOBAL_OBJECT_TYPE)); | |
| 28 __ b(eq, global_object); | |
| 29 __ cmp(type, Operand(JS_GLOBAL_PROXY_TYPE)); | |
| 30 __ b(eq, global_object); | |
| 31 } | |
| 32 | |
| 33 | |
| 34 // Helper function used from LoadIC GenerateNormal. | 22 // Helper function used from LoadIC GenerateNormal. |
| 35 // | 23 // |
| 36 // elements: Property dictionary. It is not clobbered if a jump to the miss | 24 // elements: Property dictionary. It is not clobbered if a jump to the miss |
| 37 // label is done. | 25 // label is done. |
| 38 // name: Property name. It is not clobbered if a jump to the miss label is | 26 // name: Property name. It is not clobbered if a jump to the miss label is |
| 39 // done | 27 // done |
| 40 // result: Register for the result. It is only updated if a jump to the miss | 28 // result: Register for the result. It is only updated if a jump to the miss |
| 41 // label is not done. Can be the same as elements or name clobbering | 29 // label is not done. Can be the same as elements or name clobbering |
| 42 // one of these in the case of not jumping to the miss label. | 30 // one of these in the case of not jumping to the miss label. |
| 43 // The two scratch registers need to be different from elements, name and | 31 // The two scratch registers need to be different from elements, name and |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 const int kValueOffset = kElementsStartOffset + kPointerSize; | 107 const int kValueOffset = kElementsStartOffset + kPointerSize; |
| 120 __ add(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag)); | 108 __ add(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag)); |
| 121 __ str(value, MemOperand(scratch2)); | 109 __ str(value, MemOperand(scratch2)); |
| 122 | 110 |
| 123 // Update the write barrier. Make sure not to clobber the value. | 111 // Update the write barrier. Make sure not to clobber the value. |
| 124 __ mov(scratch1, value); | 112 __ mov(scratch1, value); |
| 125 __ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved, | 113 __ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved, |
| 126 kDontSaveFPRegs); | 114 kDontSaveFPRegs); |
| 127 } | 115 } |
| 128 | 116 |
| 129 | |
| 130 // Checks the receiver for special cases (value type, slow case bits). | |
| 131 // Falls through for regular JS object. | |
| 132 static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm, | |
| 133 Register receiver, Register map, | |
| 134 Register scratch, | |
| 135 int interceptor_bit, Label* slow) { | |
| 136 // Check that the object isn't a smi. | |
| 137 __ JumpIfSmi(receiver, slow); | |
| 138 // Get the map of the receiver. | |
| 139 __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); | |
| 140 // Check bit field. | |
| 141 __ ldrb(scratch, FieldMemOperand(map, Map::kBitFieldOffset)); | |
| 142 __ tst(scratch, | |
| 143 Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit))); | |
| 144 __ b(ne, slow); | |
| 145 // Check that the object is some kind of JS object EXCEPT JS Value type. | |
| 146 // In the case that the object is a value-wrapper object, | |
| 147 // we enter the runtime system to make sure that indexing into string | |
| 148 // objects work as intended. | |
| 149 DCHECK(JS_OBJECT_TYPE > JS_VALUE_TYPE); | |
| 150 __ ldrb(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset)); | |
| 151 __ cmp(scratch, Operand(JS_OBJECT_TYPE)); | |
| 152 __ b(lt, slow); | |
| 153 } | |
| 154 | |
| 155 | |
| 156 // Loads an indexed element from a fast case array. | |
| 157 static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver, | |
| 158 Register key, Register elements, | |
| 159 Register scratch1, Register scratch2, | |
| 160 Register result, Label* slow) { | |
| 161 // Register use: | |
| 162 // | |
| 163 // receiver - holds the receiver on entry. | |
| 164 // Unchanged unless 'result' is the same register. | |
| 165 // | |
| 166 // key - holds the smi key on entry. | |
| 167 // Unchanged unless 'result' is the same register. | |
| 168 // | |
| 169 // result - holds the result on exit if the load succeeded. | |
| 170 // Allowed to be the the same as 'receiver' or 'key'. | |
| 171 // Unchanged on bailout so 'receiver' and 'key' can be safely | |
| 172 // used by further computation. | |
| 173 // | |
| 174 // Scratch registers: | |
| 175 // | |
| 176 // elements - holds the elements of the receiver and its prototypes. | |
| 177 // | |
| 178 // scratch1 - used to hold elements length, bit fields, base addresses. | |
| 179 // | |
| 180 // scratch2 - used to hold maps, prototypes, and the loaded value. | |
| 181 Label check_prototypes, check_next_prototype; | |
| 182 Label done, in_bounds, absent; | |
| 183 | |
| 184 __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset)); | |
| 185 __ AssertFastElements(elements); | |
| 186 | |
| 187 // Check that the key (index) is within bounds. | |
| 188 __ ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset)); | |
| 189 __ cmp(key, Operand(scratch1)); | |
| 190 __ b(lo, &in_bounds); | |
| 191 // Out-of-bounds. Check the prototype chain to see if we can just return | |
| 192 // 'undefined'. | |
| 193 __ cmp(key, Operand(0)); | |
| 194 __ b(lt, slow); // Negative keys can't take the fast OOB path. | |
| 195 __ bind(&check_prototypes); | |
| 196 __ ldr(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset)); | |
| 197 __ bind(&check_next_prototype); | |
| 198 __ ldr(scratch2, FieldMemOperand(scratch2, Map::kPrototypeOffset)); | |
| 199 // scratch2: current prototype | |
| 200 __ CompareRoot(scratch2, Heap::kNullValueRootIndex); | |
| 201 __ b(eq, &absent); | |
| 202 __ ldr(elements, FieldMemOperand(scratch2, JSObject::kElementsOffset)); | |
| 203 __ ldr(scratch2, FieldMemOperand(scratch2, HeapObject::kMapOffset)); | |
| 204 // elements: elements of current prototype | |
| 205 // scratch2: map of current prototype | |
| 206 __ CompareInstanceType(scratch2, scratch1, JS_OBJECT_TYPE); | |
| 207 __ b(lo, slow); | |
| 208 __ ldrb(scratch1, FieldMemOperand(scratch2, Map::kBitFieldOffset)); | |
| 209 __ tst(scratch1, Operand((1 << Map::kIsAccessCheckNeeded) | | |
| 210 (1 << Map::kHasIndexedInterceptor))); | |
| 211 __ b(ne, slow); | |
| 212 __ CompareRoot(elements, Heap::kEmptyFixedArrayRootIndex); | |
| 213 __ b(ne, slow); | |
| 214 __ jmp(&check_next_prototype); | |
| 215 | |
| 216 __ bind(&absent); | |
| 217 __ LoadRoot(result, Heap::kUndefinedValueRootIndex); | |
| 218 __ jmp(&done); | |
| 219 | |
| 220 __ bind(&in_bounds); | |
| 221 // Fast case: Do the load. | |
| 222 __ add(scratch1, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); | |
| 223 __ ldr(scratch2, MemOperand::PointerAddressFromSmiKey(scratch1, key)); | |
| 224 __ CompareRoot(scratch2, Heap::kTheHoleValueRootIndex); | |
| 225 // In case the loaded value is the_hole we have to check the prototype chain. | |
| 226 __ b(eq, &check_prototypes); | |
| 227 __ mov(result, scratch2); | |
| 228 __ bind(&done); | |
| 229 } | |
| 230 | |
| 231 | |
| 232 // Checks whether a key is an array index string or a unique name. | |
| 233 // Falls through if a key is a unique name. | |
| 234 static void GenerateKeyNameCheck(MacroAssembler* masm, Register key, | |
| 235 Register map, Register hash, | |
| 236 Label* index_string, Label* not_unique) { | |
| 237 // The key is not a smi. | |
| 238 Label unique; | |
| 239 // Is it a name? | |
| 240 __ CompareObjectType(key, map, hash, LAST_UNIQUE_NAME_TYPE); | |
| 241 __ b(hi, not_unique); | |
| 242 STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE); | |
| 243 __ b(eq, &unique); | |
| 244 | |
| 245 // Is the string an array index, with cached numeric value? | |
| 246 __ ldr(hash, FieldMemOperand(key, Name::kHashFieldOffset)); | |
| 247 __ tst(hash, Operand(Name::kContainsCachedArrayIndexMask)); | |
| 248 __ b(eq, index_string); | |
| 249 | |
| 250 // Is the string internalized? We know it's a string, so a single | |
| 251 // bit test is enough. | |
| 252 // map: key map | |
| 253 __ ldrb(hash, FieldMemOperand(map, Map::kInstanceTypeOffset)); | |
| 254 STATIC_ASSERT(kInternalizedTag == 0); | |
| 255 __ tst(hash, Operand(kIsNotInternalizedMask)); | |
| 256 __ b(ne, not_unique); | |
| 257 | |
| 258 __ bind(&unique); | |
| 259 } | |
| 260 | |
| 261 void LoadIC::GenerateNormal(MacroAssembler* masm) { | 117 void LoadIC::GenerateNormal(MacroAssembler* masm) { |
| 262 Register dictionary = r0; | 118 Register dictionary = r0; |
| 263 DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister())); | 119 DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister())); |
| 264 DCHECK(!dictionary.is(LoadDescriptor::NameRegister())); | 120 DCHECK(!dictionary.is(LoadDescriptor::NameRegister())); |
| 265 | 121 |
| 266 Label slow; | 122 Label slow; |
| 267 | 123 |
| 268 __ ldr(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(), | 124 __ ldr(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(), |
| 269 JSObject::kPropertiesOffset)); | 125 JSObject::kPropertiesOffset)); |
| 270 GenerateDictionaryLoad(masm, &slow, dictionary, | 126 GenerateDictionaryLoad(masm, &slow, dictionary, |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { | 189 void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { |
| 334 // The return address is in lr. | 190 // The return address is in lr. |
| 335 | 191 |
| 336 __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister()); | 192 __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister()); |
| 337 | 193 |
| 338 // Perform tail call to the entry. | 194 // Perform tail call to the entry. |
| 339 // Do tail-call to runtime routine. | 195 // Do tail-call to runtime routine. |
| 340 __ TailCallRuntime(Runtime::kKeyedGetProperty); | 196 __ TailCallRuntime(Runtime::kKeyedGetProperty); |
| 341 } | 197 } |
| 342 | 198 |
| 343 void KeyedLoadIC::GenerateMegamorphic(MacroAssembler* masm) { | |
| 344 // The return address is in lr. | |
| 345 Label slow, check_name, index_smi, index_name, property_array_property; | |
| 346 Label probe_dictionary, check_number_dictionary; | |
| 347 | |
| 348 Register key = LoadDescriptor::NameRegister(); | |
| 349 Register receiver = LoadDescriptor::ReceiverRegister(); | |
| 350 DCHECK(key.is(r2)); | |
| 351 DCHECK(receiver.is(r1)); | |
| 352 | |
| 353 Isolate* isolate = masm->isolate(); | |
| 354 | |
| 355 // Check that the key is a smi. | |
| 356 __ JumpIfNotSmi(key, &check_name); | |
| 357 __ bind(&index_smi); | |
| 358 // Now the key is known to be a smi. This place is also jumped to from below | |
| 359 // where a numeric string is converted to a smi. | |
| 360 | |
| 361 GenerateKeyedLoadReceiverCheck(masm, receiver, r0, r3, | |
| 362 Map::kHasIndexedInterceptor, &slow); | |
| 363 | |
| 364 // Check the receiver's map to see if it has fast elements. | |
| 365 __ CheckFastElements(r0, r3, &check_number_dictionary); | |
| 366 | |
| 367 GenerateFastArrayLoad(masm, receiver, key, r0, r3, r4, r0, &slow); | |
| 368 __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_smi(), 1, r4, | |
| 369 r3); | |
| 370 __ Ret(); | |
| 371 | |
| 372 __ bind(&check_number_dictionary); | |
| 373 __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset)); | |
| 374 __ ldr(r3, FieldMemOperand(r4, JSObject::kMapOffset)); | |
| 375 | |
| 376 // Check whether the elements is a number dictionary. | |
| 377 // r3: elements map | |
| 378 // r4: elements | |
| 379 __ LoadRoot(ip, Heap::kHashTableMapRootIndex); | |
| 380 __ cmp(r3, ip); | |
| 381 __ b(ne, &slow); | |
| 382 __ SmiUntag(r0, key); | |
| 383 __ LoadFromNumberDictionary(&slow, r4, key, r0, r0, r3, r5); | |
| 384 __ Ret(); | |
| 385 | |
| 386 // Slow case, key and receiver still in r2 and r1. | |
| 387 __ bind(&slow); | |
| 388 __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_slow(), 1, r4, | |
| 389 r3); | |
| 390 GenerateRuntimeGetProperty(masm); | |
| 391 | |
| 392 __ bind(&check_name); | |
| 393 GenerateKeyNameCheck(masm, key, r0, r3, &index_name, &slow); | |
| 394 | |
| 395 GenerateKeyedLoadReceiverCheck(masm, receiver, r0, r3, | |
| 396 Map::kHasNamedInterceptor, &slow); | |
| 397 | |
| 398 // If the receiver is a fast-case object, check the stub cache. Otherwise | |
| 399 // probe the dictionary. | |
| 400 __ ldr(r3, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); | |
| 401 __ ldr(r4, FieldMemOperand(r3, HeapObject::kMapOffset)); | |
| 402 __ LoadRoot(ip, Heap::kHashTableMapRootIndex); | |
| 403 __ cmp(r4, ip); | |
| 404 __ b(eq, &probe_dictionary); | |
| 405 | |
| 406 // The handlers in the stub cache expect a vector and slot. Since we won't | |
| 407 // change the IC from any downstream misses, a dummy vector can be used. | |
| 408 Register vector = LoadWithVectorDescriptor::VectorRegister(); | |
| 409 Register slot = LoadWithVectorDescriptor::SlotRegister(); | |
| 410 DCHECK(!AreAliased(vector, slot, r4, r5, r6, r9)); | |
| 411 Handle<TypeFeedbackVector> dummy_vector = | |
| 412 TypeFeedbackVector::DummyVector(masm->isolate()); | |
| 413 int slot_index = dummy_vector->GetIndex( | |
| 414 FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedLoadICSlot)); | |
| 415 __ LoadRoot(vector, Heap::kDummyVectorRootIndex); | |
| 416 __ mov(slot, Operand(Smi::FromInt(slot_index))); | |
| 417 | |
| 418 masm->isolate()->load_stub_cache()->GenerateProbe(masm, receiver, key, r4, r5, | |
| 419 r6, r9); | |
| 420 // Cache miss. | |
| 421 GenerateMiss(masm); | |
| 422 | |
| 423 // Do a quick inline probe of the receiver's dictionary, if it | |
| 424 // exists. | |
| 425 __ bind(&probe_dictionary); | |
| 426 // r3: elements | |
| 427 __ ldr(r0, FieldMemOperand(receiver, HeapObject::kMapOffset)); | |
| 428 __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset)); | |
| 429 GenerateGlobalInstanceTypeCheck(masm, r0, &slow); | |
| 430 // Load the property to r0. | |
| 431 GenerateDictionaryLoad(masm, &slow, r3, key, r0, r5, r4); | |
| 432 __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_symbol(), 1, | |
| 433 r4, r3); | |
| 434 __ Ret(); | |
| 435 | |
| 436 __ bind(&index_name); | |
| 437 __ IndexFromHash(r3, key); | |
| 438 // Now jump to the place where smi keys are handled. | |
| 439 __ jmp(&index_smi); | |
| 440 } | |
| 441 | |
| 442 | |
| 443 static void StoreIC_PushArgs(MacroAssembler* masm) { | 199 static void StoreIC_PushArgs(MacroAssembler* masm) { |
| 444 __ Push(StoreWithVectorDescriptor::ValueRegister(), | 200 __ Push(StoreWithVectorDescriptor::ValueRegister(), |
| 445 StoreWithVectorDescriptor::SlotRegister(), | 201 StoreWithVectorDescriptor::SlotRegister(), |
| 446 StoreWithVectorDescriptor::VectorRegister(), | 202 StoreWithVectorDescriptor::VectorRegister(), |
| 447 StoreWithVectorDescriptor::ReceiverRegister(), | 203 StoreWithVectorDescriptor::ReceiverRegister(), |
| 448 StoreWithVectorDescriptor::NameRegister()); | 204 StoreWithVectorDescriptor::NameRegister()); |
| 449 } | 205 } |
| 450 | 206 |
| 451 | 207 |
| 452 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { | 208 void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { |
| (...skipping 411 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 864 patcher.EmitCondition(ne); | 620 patcher.EmitCondition(ne); |
| 865 } else { | 621 } else { |
| 866 DCHECK(Assembler::GetCondition(branch_instr) == ne); | 622 DCHECK(Assembler::GetCondition(branch_instr) == ne); |
| 867 patcher.EmitCondition(eq); | 623 patcher.EmitCondition(eq); |
| 868 } | 624 } |
| 869 } | 625 } |
| 870 } // namespace internal | 626 } // namespace internal |
| 871 } // namespace v8 | 627 } // namespace v8 |
| 872 | 628 |
| 873 #endif // V8_TARGET_ARCH_ARM | 629 #endif // V8_TARGET_ARCH_ARM |
| OLD | NEW |