ic-arm.cc 33.5 KB
Newer Older
1
// Copyright 2012 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#if V8_TARGET_ARCH_ARM
6

7
#include "src/codegen.h"
8
#include "src/ic/ic.h"
9
#include "src/ic/ic-compiler.h"
10
#include "src/ic/stub-cache.h"
11

12 13
namespace v8 {
namespace internal {
14 15 16 17 18 19


// ----------------------------------------------------------------------------
// Static IC stub generators.
//

20
#define __ ACCESS_MASM(masm)
21

22

23
static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm, Register type,
24 25 26 27 28 29 30 31 32 33
                                            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_GLOBAL_PROXY_TYPE));
  __ b(eq, global_object);
}


verwaest@chromium.org's avatar
verwaest@chromium.org committed
34
// Helper function used from LoadIC GenerateNormal.
35 36 37 38 39 40 41 42 43 44 45 46
//
// 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 elements or name clobbering
//           one of these in the case of not jumping to the miss label.
// 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.
47 48 49
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
                                   Register elements, Register name,
                                   Register result, Register scratch1,
50 51 52 53 54 55 56 57
                                   Register scratch2) {
  // Main use of the scratch registers.
  // scratch1: Used as temporary and to hold the capacity of the property
  //           dictionary.
  // scratch2: Used as temporary.
  Label done;

  // Probe the dictionary.
58 59
  NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
                                                   name, scratch1, scratch2);
60 61 62

  // If probing finds an entry check that the value is a normal
  // property.
63
  __ bind(&done);  // scratch2 == elements + 4 * index
64 65
  const int kElementsStartOffset =
      NameDictionary::kHeaderSize +
66
      NameDictionary::kElementsStartIndex * kPointerSize;
67 68
  const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
  __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
69
  __ tst(scratch1, Operand(PropertyDetails::TypeField::kMask << kSmiTagSize));
70
  __ b(ne, miss);
71 72

  // Get the value at the masked, scaled index and return.
73
  __ ldr(result,
74
         FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
75 76 77
}


78 79 80 81 82 83 84 85 86 87 88
// Helper function used from StoreIC::GenerateNormal.
//
// 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
// value:    The value to store.
// 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.
89 90 91
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
                                    Register elements, Register name,
                                    Register value, Register scratch1,
92 93 94 95 96 97 98 99
                                    Register scratch2) {
  // Main use of the scratch registers.
  // scratch1: Used as temporary and to hold the capacity of the property
  //           dictionary.
  // scratch2: Used as temporary.
  Label done;

  // Probe the dictionary.
100 101
  NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
                                                   name, scratch1, scratch2);
102 103 104 105

  // If probing finds an entry in the dictionary check that the value
  // is a normal property that is not read only.
  __ bind(&done);  // scratch2 == elements + 4 * index
106 107
  const int kElementsStartOffset =
      NameDictionary::kHeaderSize +
108
      NameDictionary::kElementsStartIndex * kPointerSize;
109
  const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
110 111
  const int kTypeAndReadOnlyMask =
      (PropertyDetails::TypeField::kMask |
112 113
       PropertyDetails::AttributesField::encode(READ_ONLY))
      << kSmiTagSize;
114 115 116 117 118 119 120 121 122 123 124
  __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
  __ tst(scratch1, Operand(kTypeAndReadOnlyMask));
  __ b(ne, miss);

  // Store the value at the masked, scaled index and return.
  const int kValueOffset = kElementsStartOffset + kPointerSize;
  __ add(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
  __ str(value, MemOperand(scratch2));

  // Update the write barrier. Make sure not to clobber the value.
  __ mov(scratch1, value);
125 126
  __ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved,
                 kDontSaveFPRegs);
127 128 129
}


130 131 132
// Checks the receiver for special cases (value type, slow case bits).
// Falls through for regular JS object.
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 142
  __ ldrb(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
  __ tst(scratch,
143
         Operand((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)));
144
  __ b(ne, slow);
145 146 147 148
  // 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
  // objects work as intended.
149
  DCHECK(JS_OBJECT_TYPE > JS_VALUE_TYPE);
150 151
  __ ldrb(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
  __ cmp(scratch, Operand(JS_OBJECT_TYPE));
152 153 154 155 156
  __ b(lt, slow);
}


// Loads an indexed element from a fast case array.
157 158 159
static void GenerateFastArrayLoad(MacroAssembler* masm, Register receiver,
                                  Register key, Register elements,
                                  Register scratch1, Register scratch2,
160
                                  Register result, Label* slow) {
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
  // Register use:
  //
  // receiver - holds the receiver on entry.
  //            Unchanged unless 'result' is the same register.
  //
  // key      - holds the smi key on entry.
  //            Unchanged unless 'result' is the same register.
  //
  // result   - holds the result on exit if the load succeeded.
  //            Allowed to be the the same as 'receiver' or 'key'.
  //            Unchanged on bailout so 'receiver' and 'key' can be safely
  //            used by further computation.
  //
  // Scratch registers:
  //
176 177 178
  // elements - holds the elements of the receiver and its prototypes.
  //
  // scratch1 - used to hold elements length, bit fields, base addresses.
179
  //
180 181
  // scratch2 - used to hold maps, prototypes, and the loaded value.
  Label check_prototypes, check_next_prototype;
182
  Label done, in_bounds, absent;
183 184

  __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
185 186
  __ AssertFastElements(elements);

187 188 189
  // Check that the key (index) is within bounds.
  __ ldr(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
  __ cmp(key, Operand(scratch1));
190 191 192 193 194 195 196 197 198 199 200
  __ b(lo, &in_bounds);
  // Out-of-bounds. Check the prototype chain to see if we can just return
  // 'undefined'.
  __ cmp(key, Operand(0));
  __ b(lt, slow);  // Negative keys can't take the fast OOB path.
  __ bind(&check_prototypes);
  __ ldr(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
  __ bind(&check_next_prototype);
  __ ldr(scratch2, FieldMemOperand(scratch2, Map::kPrototypeOffset));
  // scratch2: current prototype
  __ CompareRoot(scratch2, Heap::kNullValueRootIndex);
201
  __ b(eq, &absent);
202 203 204 205 206 207 208 209 210 211 212 213 214 215
  __ ldr(elements, FieldMemOperand(scratch2, JSObject::kElementsOffset));
  __ ldr(scratch2, FieldMemOperand(scratch2, HeapObject::kMapOffset));
  // elements: elements of current prototype
  // scratch2: map of current prototype
  __ CompareInstanceType(scratch2, scratch1, JS_OBJECT_TYPE);
  __ b(lo, slow);
  __ ldrb(scratch1, FieldMemOperand(scratch2, Map::kBitFieldOffset));
  __ tst(scratch1, Operand((1 << Map::kIsAccessCheckNeeded) |
                           (1 << Map::kHasIndexedInterceptor)));
  __ b(ne, slow);
  __ CompareRoot(elements, Heap::kEmptyFixedArrayRootIndex);
  __ b(ne, slow);
  __ jmp(&check_next_prototype);

216
  __ bind(&absent);
217 218
  __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
  __ jmp(&done);
219 220

  __ bind(&in_bounds);
221 222
  // Fast case: Do the load.
  __ add(scratch1, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
223
  __ ldr(scratch2, MemOperand::PointerAddressFromSmiKey(scratch1, key));
224 225 226
  __ CompareRoot(scratch2, Heap::kTheHoleValueRootIndex);
  // In case the loaded value is the_hole we have to check the prototype chain.
  __ b(eq, &check_prototypes);
227
  __ mov(result, scratch2);
228
  __ bind(&done);
229 230 231
}


232 233
// Checks whether a key is an array index string or a unique name.
// Falls through if a key is a unique name.
234 235 236
static void GenerateKeyNameCheck(MacroAssembler* masm, Register key,
                                 Register map, Register hash,
                                 Label* index_string, Label* not_unique) {
237
  // The key is not a smi.
238 239 240 241 242 243
  Label unique;
  // Is it a name?
  __ CompareObjectType(key, map, hash, LAST_UNIQUE_NAME_TYPE);
  __ b(hi, not_unique);
  STATIC_ASSERT(LAST_UNIQUE_NAME_TYPE == FIRST_NONSTRING_TYPE);
  __ b(eq, &unique);
244 245

  // Is the string an array index, with cached numeric value?
246 247
  __ ldr(hash, FieldMemOperand(key, Name::kHashFieldOffset));
  __ tst(hash, Operand(Name::kContainsCachedArrayIndexMask));
248 249
  __ b(eq, index_string);

250 251
  // Is the string internalized? We know it's a string, so a single
  // bit test is enough.
252 253
  // map: key map
  __ ldrb(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
254 255 256
  STATIC_ASSERT(kInternalizedTag == 0);
  __ tst(hash, Operand(kIsNotInternalizedMask));
  __ b(ne, not_unique);
257 258

  __ bind(&unique);
259 260
}

261
void LoadIC::GenerateNormal(MacroAssembler* masm) {
262
  Register dictionary = r0;
263 264
  DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
  DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
265

266
  Label slow;
267

268
  __ ldr(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
269 270
                                     JSObject::kPropertiesOffset));
  GenerateDictionaryLoad(masm, &slow, dictionary,
271
                         LoadDescriptor::NameRegister(), r0, r3, r4);
272 273
  __ Ret();

274 275
  // Dictionary load failed, go slow (but don't miss).
  __ bind(&slow);
276
  GenerateRuntimeGetProperty(masm);
277 278 279
}


280 281 282 283
// A register that isn't one of the parameters to the load ic.
static const Register LoadIC_TempRegister() { return r3; }


284 285 286
static void LoadIC_PushArgs(MacroAssembler* masm) {
  Register receiver = LoadDescriptor::ReceiverRegister();
  Register name = LoadDescriptor::NameRegister();
287 288
  Register slot = LoadDescriptor::SlotRegister();
  Register vector = LoadWithVectorDescriptor::VectorRegister();
289 290

  __ Push(receiver, name, slot, vector);
291 292 293
}


294
void LoadIC::GenerateMiss(MacroAssembler* masm) {
295
  // The return address is in lr.
296
  Isolate* isolate = masm->isolate();
297

298 299
  DCHECK(!AreAliased(r4, r5, LoadWithVectorDescriptor::SlotRegister(),
                     LoadWithVectorDescriptor::VectorRegister()));
cbruni's avatar
cbruni committed
300
  __ IncrementCounter(isolate->counters()->ic_load_miss(), 1, r4, r5);
301

302
  LoadIC_PushArgs(masm);
303

304
  // Perform tail call to the entry.
305
  __ TailCallRuntime(Runtime::kLoadIC_Miss);
306 307
}

308
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
309
  // The return address is in lr.
310

311 312
  __ mov(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
  __ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
313

314
  // Do tail-call to runtime routine.
315
  __ TailCallRuntime(Runtime::kGetProperty);
316 317 318
}


319
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
320
  // The return address is in lr.
321
  Isolate* isolate = masm->isolate();
322

323 324
  DCHECK(!AreAliased(r4, r5, LoadWithVectorDescriptor::SlotRegister(),
                     LoadWithVectorDescriptor::VectorRegister()));
cbruni's avatar
cbruni committed
325
  __ IncrementCounter(isolate->counters()->ic_keyed_load_miss(), 1, r4, r5);
326

327
  LoadIC_PushArgs(masm);
328

danno@chromium.org's avatar
danno@chromium.org committed
329
  // Perform tail call to the entry.
330
  __ TailCallRuntime(Runtime::kKeyedLoadIC_Miss);
331 332
}

333
void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
334
  // The return address is in lr.
335

336
  __ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister());
337

338 339
  // Perform tail call to the entry.
  // Do tail-call to runtime routine.
340
  __ TailCallRuntime(Runtime::kKeyedGetProperty);
341 342
}

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 349
  Register key = LoadDescriptor::NameRegister();
  Register receiver = LoadDescriptor::ReceiverRegister();
350 351
  DCHECK(key.is(r2));
  DCHECK(receiver.is(r1));
352

353 354
  Isolate* isolate = masm->isolate();

355
  // Check that the key is a smi.
356
  __ JumpIfNotSmi(key, &check_name);
357 358 359
  __ 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.
360

361 362
  GenerateKeyedLoadReceiverCheck(masm, receiver, r0, r3,
                                 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);
cbruni's avatar
cbruni committed
368 369
  __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_smi(), 1, r4,
                      r3);
370
  __ Ret();
371

372
  __ bind(&check_number_dictionary);
373 374 375
  __ ldr(r4, FieldMemOperand(receiver, JSObject::kElementsOffset));
  __ ldr(r3, FieldMemOperand(r4, JSObject::kMapOffset));

376
  // Check whether the elements is a number dictionary.
377 378
  // r3: elements map
  // r4: elements
379 380 381
  __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
  __ cmp(r3, ip);
  __ b(ne, &slow);
382 383
  __ SmiUntag(r0, key);
  __ LoadFromNumberDictionary(&slow, r4, key, r0, r0, r3, r5);
384 385
  __ Ret();

386
  // Slow case, key and receiver still in r2 and r1.
387
  __ bind(&slow);
cbruni's avatar
cbruni committed
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 396
  GenerateKeyedLoadReceiverCheck(masm, receiver, r0, r3,
                                 Map::kHasNamedInterceptor, &slow);
397

398 399
  // If the receiver is a fast-case object, check the stub cache. Otherwise
  // 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 405
  __ b(eq, &probe_dictionary);

406 407
  // The handlers in the stub cache expect a vector and slot. Since we won't
  // change the IC from any downstream misses, a dummy vector can be used.
408 409
  Register vector = LoadWithVectorDescriptor::VectorRegister();
  Register slot = LoadWithVectorDescriptor::SlotRegister();
410
  DCHECK(!AreAliased(vector, slot, r4, r5, r6, r9));
411 412 413
  Handle<TypeFeedbackVector> dummy_vector =
      TypeFeedbackVector::DummyVector(masm->isolate());
  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 419
  Code::Flags flags =
      Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(Code::LOAD_IC));
420 421
  masm->isolate()->stub_cache()->GenerateProbe(masm, Code::KEYED_LOAD_IC, flags,
                                               receiver, key, r4, r5, r6, r9);
422 423
  // Cache miss.
  GenerateMiss(masm);
424

425 426 427
  // Do a quick inline probe of the receiver's dictionary, if it
  // exists.
  __ bind(&probe_dictionary);
428
  // r3: elements
429 430 431
  __ ldr(r0, FieldMemOperand(receiver, HeapObject::kMapOffset));
  __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
  GenerateGlobalInstanceTypeCheck(masm, r0, &slow);
432
  // Load the property to r0.
433
  GenerateDictionaryLoad(masm, &slow, r3, key, r0, r5, r4);
cbruni's avatar
cbruni committed
434 435
  __ IncrementCounter(isolate->counters()->ic_keyed_load_generic_symbol(), 1,
                      r4, r3);
436 437
  __ Ret();

438
  __ bind(&index_name);
439
  __ IndexFromHash(r3, key);
440 441
  // Now jump to the place where smi keys are handled.
  __ jmp(&index_smi);
442 443
}

444

445
static void StoreIC_PushArgs(MacroAssembler* masm) {
446 447 448 449
  __ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
          StoreDescriptor::ValueRegister(),
          VectorStoreICDescriptor::SlotRegister(),
          VectorStoreICDescriptor::VectorRegister());
450 451 452
}


453
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
454
  StoreIC_PushArgs(masm);
danno@chromium.org's avatar
danno@chromium.org committed
455

456
  __ TailCallRuntime(Runtime::kKeyedStoreIC_Miss);
457 458 459
}


460
static void KeyedStoreGenerateMegamorphicHelper(
461 462 463 464
    MacroAssembler* masm, Label* fast_object, Label* fast_double, Label* slow,
    KeyedStoreCheckMap check_map, KeyedStoreIncrementLength increment_length,
    Register value, Register key, Register receiver, Register receiver_map,
    Register elements_map, Register elements) {
465 466 467 468 469 470
  Label transition_smi_elements;
  Label finish_object_store, non_double_value, transition_double_elements;
  Label fast_double_without_map_check;

  // Fast case: Do the store, could be either Object or double.
  __ bind(fast_object);
471
  Register scratch = r4;
472
  Register address = r5;
473 474 475
  DCHECK(!AreAliased(value, key, receiver, receiver_map, elements_map, elements,
                     scratch, address));

476 477 478 479 480 481
  if (check_map == kCheckMap) {
    __ ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
    __ cmp(elements_map,
           Operand(masm->isolate()->factory()->fixed_array_map()));
    __ b(ne, fast_double);
  }
482 483 484 485 486 487

  // HOLECHECK: guards "A[i] = V"
  // We have to go to the runtime if the current value is the hole because
  // there may be a callback on the element
  Label holecheck_passed1;
  __ add(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
488 489
  __ ldr(scratch, MemOperand::PointerAddressFromSmiKey(address, key, PreIndex));
  __ cmp(scratch, Operand(masm->isolate()->factory()->the_hole_value()));
490
  __ b(ne, &holecheck_passed1);
491
  __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, scratch, slow);
492 493 494

  __ bind(&holecheck_passed1);

495 496 497 498 499 500
  // Smi stores don't require further checks.
  Label non_smi_value;
  __ JumpIfNotSmi(value, &non_smi_value);

  if (increment_length == kIncrementLength) {
    // Add 1 to receiver->length.
501 502
    __ add(scratch, key, Operand(Smi::FromInt(1)));
    __ str(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
503 504 505
  }
  // It's irrelevant whether array is smi-only or not when writing a smi.
  __ add(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
506
  __ str(value, MemOperand::PointerAddressFromSmiKey(address, key));
507 508 509 510
  __ Ret();

  __ bind(&non_smi_value);
  // Escape to elements kind transition case.
511
  __ CheckFastObjectElements(receiver_map, scratch, &transition_smi_elements);
512 513 514 515 516

  // Fast elements array, store the value to the elements backing store.
  __ bind(&finish_object_store);
  if (increment_length == kIncrementLength) {
    // Add 1 to receiver->length.
517 518
    __ add(scratch, key, Operand(Smi::FromInt(1)));
    __ str(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
519 520
  }
  __ add(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
521
  __ add(address, address, Operand::PointerOffsetFromSmiKey(key));
522 523
  __ str(value, MemOperand(address));
  // Update write barrier for the elements array address.
524 525
  __ mov(scratch, value);  // Preserve the value which is returned.
  __ RecordWrite(elements, address, scratch, kLRHasNotBeenSaved,
526
                 kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
527 528 529 530 531 532 533 534 535
  __ Ret();

  __ bind(fast_double);
  if (check_map == kCheckMap) {
    // Check for fast double array case. If this fails, call through to the
    // runtime.
    __ CompareRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex);
    __ b(ne, slow);
  }
536 537 538 539 540

  // HOLECHECK: guards "A[i] double hole?"
  // We have to see if the double version of the hole is present. If so
  // go to the runtime.
  __ add(address, elements,
541 542
         Operand((FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32)) -
                 kHeapObjectTag));
543 544
  __ ldr(scratch, MemOperand(address, key, LSL, kPointerSizeLog2, PreIndex));
  __ cmp(scratch, Operand(kHoleNanUpper32));
545
  __ b(ne, &fast_double_without_map_check);
546
  __ JumpIfDictionaryInPrototypeChain(receiver, elements_map, scratch, slow);
547

548
  __ bind(&fast_double_without_map_check);
549
  __ StoreNumberToDoubleElements(value, key, elements, scratch, d0,
550 551 552
                                 &transition_double_elements);
  if (increment_length == kIncrementLength) {
    // Add 1 to receiver->length.
553 554
    __ add(scratch, key, Operand(Smi::FromInt(1)));
    __ str(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
555 556 557 558 559
  }
  __ Ret();

  __ bind(&transition_smi_elements);
  // Transition the array appropriately depending on the value type.
560 561
  __ ldr(scratch, FieldMemOperand(value, HeapObject::kMapOffset));
  __ CompareRoot(scratch, Heap::kHeapNumberMapRootIndex);
562 563 564 565
  __ b(ne, &non_double_value);

  // Value is a double. Transition FAST_SMI_ELEMENTS ->
  // FAST_DOUBLE_ELEMENTS and complete the store.
566
  __ LoadTransitionedArrayMapConditional(
567
      FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS, receiver_map, scratch, slow);
568 569 570 571
  AllocationSiteMode mode =
      AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_DOUBLE_ELEMENTS);
  ElementsTransitionGenerator::GenerateSmiToDouble(masm, receiver, key, value,
                                                   receiver_map, mode, slow);
572 573 574 575 576
  __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
  __ jmp(&fast_double_without_map_check);

  __ bind(&non_double_value);
  // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
577
  __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, FAST_ELEMENTS,
578
                                         receiver_map, scratch, slow);
579
  mode = AllocationSite::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
580 581
  ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
      masm, receiver, key, value, receiver_map, mode, slow);
582 583 584 585 586 587 588
  __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
  __ jmp(&finish_object_store);

  __ bind(&transition_double_elements);
  // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
  // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
  // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
589
  __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS,
590
                                         receiver_map, scratch, slow);
591
  mode = AllocationSite::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
592 593
  ElementsTransitionGenerator::GenerateDoubleToObject(
      masm, receiver, key, value, receiver_map, mode, slow);
594 595 596 597 598
  __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
  __ jmp(&finish_object_store);
}


599
void KeyedStoreIC::GenerateMegamorphic(MacroAssembler* masm,
600
                                       LanguageMode language_mode) {
601 602
  // ---------- S t a t e --------------
  //  -- r0     : value
603 604
  //  -- r1     : key
  //  -- r2     : receiver
605
  //  -- lr     : return address
606
  // -----------------------------------
607 608
  Label slow, fast_object, fast_object_grow;
  Label fast_double, fast_double_grow;
609
  Label array, extra, check_if_double_array, maybe_name_key, miss;
610 611

  // Register usage.
612 613 614
  Register value = StoreDescriptor::ValueRegister();
  Register key = StoreDescriptor::NameRegister();
  Register receiver = StoreDescriptor::ReceiverRegister();
615 616 617
  DCHECK(receiver.is(r1));
  DCHECK(key.is(r2));
  DCHECK(value.is(r0));
618
  Register receiver_map = r3;
619
  Register elements_map = r6;
620
  Register elements = r9;  // Elements array of the receiver.
621
  // r4 and r5 are used as general scratch registers.
622

623
  // Check that the key is a smi.
624
  __ JumpIfNotSmi(key, &maybe_name_key);
625
  // Check that the object isn't a smi.
626
  __ JumpIfSmi(receiver, &slow);
627
  // Get the map of the object.
628
  __ ldr(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
629 630
  // Check that the receiver does not require access checks and is not observed.
  // The generic stub does not perform map checks or handle observed objects.
631
  __ ldrb(ip, FieldMemOperand(receiver_map, Map::kBitFieldOffset));
632
  __ tst(ip, Operand(1 << Map::kIsAccessCheckNeeded | 1 << Map::kIsObserved));
633
  __ b(ne, &slow);
634
  // Check if the object is a JS array or not.
635
  __ ldrb(r4, FieldMemOperand(receiver_map, Map::kInstanceTypeOffset));
636
  __ cmp(r4, Operand(JS_ARRAY_TYPE));
637
  __ b(eq, &array);
638 639 640 641 642 643
  // 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 objects works as intended.
  STATIC_ASSERT(JS_VALUE_TYPE < JS_OBJECT_TYPE);
  __ cmp(r4, Operand(JS_OBJECT_TYPE));
  __ b(lo, &slow);
644 645

  // Object case: Check key against length in the elements array.
646
  __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
647
  // Check array bounds. Both the key and the length of FixedArray are smis.
648
  __ ldr(ip, FieldMemOperand(elements, FixedArray::kLengthOffset));
649
  __ cmp(key, Operand(ip));
650
  __ b(lo, &fast_object);
651

652
  // Slow case, handle jump to runtime.
653
  __ bind(&slow);
654 655 656 657
  // Entry registers are intact.
  // r0: value.
  // r1: key.
  // r2: receiver.
658
  PropertyICCompiler::GenerateRuntimeSetProperty(masm, language_mode);
659 660 661 662 663 664
  // Never returns to here.

  __ bind(&maybe_name_key);
  __ ldr(r4, FieldMemOperand(key, HeapObject::kMapOffset));
  __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
  __ JumpIfNotUniqueNameInstanceType(r4, &slow);
665

666 667
  // We use register r8, because otherwise probing the megamorphic stub cache
  // would require pushing temporaries on the stack.
668 669
  // TODO(mvstanton): quit using register r8 when
  // FLAG_enable_embedded_constant_pool is turned on.
670 671 672 673 674 675 676 677 678 679 680 681 682 683
  DCHECK(!FLAG_enable_embedded_constant_pool);
  Register temporary2 = r8;
  // The handlers in the stub cache expect a vector and slot. Since we won't
  // change the IC from any downstream misses, a dummy vector can be used.
  Register vector = VectorStoreICDescriptor::VectorRegister();
  Register slot = VectorStoreICDescriptor::SlotRegister();

  DCHECK(!AreAliased(vector, slot, r5, temporary2, r6, r9));
  Handle<TypeFeedbackVector> dummy_vector =
      TypeFeedbackVector::DummyVector(masm->isolate());
  int slot_index = dummy_vector->GetIndex(
      FeedbackVectorSlot(TypeFeedbackVector::kDummyKeyedStoreICSlot));
  __ LoadRoot(vector, Heap::kDummyVectorRootIndex);
  __ mov(slot, Operand(Smi::FromInt(slot_index)));
684

685 686
  Code::Flags flags =
      Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(Code::STORE_IC));
687 688
  masm->isolate()->stub_cache()->GenerateProbe(
      masm, Code::STORE_IC, flags, receiver, key, r5, temporary2, r6, r9);
689
  // Cache miss.
690
  __ b(&miss);
691 692 693 694 695

  // Extra capacity case: Check if there is extra capacity to
  // perform the store and update the length. Used for adding one
  // element to the array by writing to array[array.length].
  __ bind(&extra);
696 697 698
  // Condition code from comparing key and array length is still available.
  __ b(ne, &slow);  // Only support writing to writing to array[array.length].
  // Check for room in the elements backing store.
699
  // Both the key and the length of FixedArray are smis.
700
  __ ldr(ip, FieldMemOperand(elements, FixedArray::kLengthOffset));
701
  __ cmp(key, Operand(ip));
702
  __ b(hs, &slow);
703
  __ ldr(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
704
  __ cmp(elements_map, Operand(masm->isolate()->factory()->fixed_array_map()));
705
  __ b(ne, &check_if_double_array);
706
  __ jmp(&fast_object_grow);
707 708 709 710 711

  __ bind(&check_if_double_array);
  __ cmp(elements_map,
         Operand(masm->isolate()->factory()->fixed_double_array_map()));
  __ b(ne, &slow);
712
  __ jmp(&fast_double_grow);
713 714

  // Array case: Get the length and the elements array from the JS
715 716
  // array. Check that the array is in fast mode (and writable); if it
  // is the length is always a smi.
717
  __ bind(&array);
718
  __ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
719

720 721 722
  // Check the key against the length in the array.
  __ ldr(ip, FieldMemOperand(receiver, JSArray::kLengthOffset));
  __ cmp(key, Operand(ip));
723 724
  __ b(hs, &extra);

725
  KeyedStoreGenerateMegamorphicHelper(
726 727
      masm, &fast_object, &fast_double, &slow, kCheckMap, kDontIncrementLength,
      value, key, receiver, receiver_map, elements_map, elements);
728 729 730 731
  KeyedStoreGenerateMegamorphicHelper(masm, &fast_object_grow,
                                      &fast_double_grow, &slow, kDontCheckMap,
                                      kIncrementLength, value, key, receiver,
                                      receiver_map, elements_map, elements);
732 733 734

  __ bind(&miss);
  GenerateMiss(masm);
735 736
}

737

738
void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
739 740
  Register receiver = StoreDescriptor::ReceiverRegister();
  Register name = StoreDescriptor::NameRegister();
741 742
  DCHECK(receiver.is(r1));
  DCHECK(name.is(r2));
743
  DCHECK(StoreDescriptor::ValueRegister().is(r0));
744 745

  // Get the receiver from the stack and probe the stub cache.
746 747
  Code::Flags flags =
      Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(Code::STORE_IC));
748

749 750
  masm->isolate()->stub_cache()->GenerateProbe(masm, Code::STORE_IC, flags,
                                               receiver, name, r3, r4, r5, r6);
751 752

  // Cache miss: Jump to runtime.
753
  GenerateMiss(masm);
754 755 756
}


757
void StoreIC::GenerateMiss(MacroAssembler* masm) {
758
  StoreIC_PushArgs(masm);
759

760
  // Perform tail call to the entry.
761
  __ TailCallRuntime(Runtime::kStoreIC_Miss);
762 763 764
}


765 766
void StoreIC::GenerateNormal(MacroAssembler* masm) {
  Label miss;
767 768 769
  Register receiver = StoreDescriptor::ReceiverRegister();
  Register name = StoreDescriptor::NameRegister();
  Register value = StoreDescriptor::ValueRegister();
770
  Register dictionary = r5;
771 772 773
  DCHECK(receiver.is(r1));
  DCHECK(name.is(r2));
  DCHECK(value.is(r0));
774 775
  DCHECK(VectorStoreICDescriptor::VectorRegister().is(r3));
  DCHECK(VectorStoreICDescriptor::SlotRegister().is(r4));
776

777
  __ ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
778

779
  GenerateDictionaryStore(masm, &miss, dictionary, name, value, r6, r9);
780
  Counters* counters = masm->isolate()->counters();
cbruni's avatar
cbruni committed
781
  __ IncrementCounter(counters->ic_store_normal_hit(), 1, r6, r9);
782 783 784
  __ Ret();

  __ bind(&miss);
cbruni's avatar
cbruni committed
785
  __ IncrementCounter(counters->ic_store_normal_miss(), 1, r6, r9);
786 787 788 789
  GenerateMiss(masm);
}


790 791 792
#undef __


793 794 795 796 797 798 799 800
Condition CompareIC::ComputeCondition(Token::Value op) {
  switch (op) {
    case Token::EQ_STRICT:
    case Token::EQ:
      return eq;
    case Token::LT:
      return lt;
    case Token::GT:
801
      return gt;
802
    case Token::LTE:
803
      return le;
804 805 806 807
    case Token::GTE:
      return ge;
    default:
      UNREACHABLE();
808
      return kNoCondition;
809 810 811 812
  }
}


813 814 815 816
bool CompareIC::HasInlinedSmiCode(Address address) {
  // The address of the instruction following the call.
  Address cmp_instruction_address =
      Assembler::return_address_from_call_start(address);
817

818 819 820 821
  // If the instruction following the call is not a cmp rx, #yyy, nothing
  // was inlined.
  Instr instr = Assembler::instr_at(cmp_instruction_address);
  return Assembler::IsCmpImmediate(instr);
822 823
}

824

825 826
void PatchInlinedSmiCode(Isolate* isolate, Address address,
                         InlinedSmiCheck check) {
827
  Address cmp_instruction_address =
828
      Assembler::return_address_from_call_start(address);
829 830 831 832 833 834 835 836 837 838 839

  // If the instruction following the call is not a cmp rx, #yyy, nothing
  // was inlined.
  Instr instr = Assembler::instr_at(cmp_instruction_address);
  if (!Assembler::IsCmpImmediate(instr)) {
    return;
  }

  // The delta to the start of the map check instruction and the
  // condition code uses at the patched jump.
  int delta = Assembler::GetCmpImmediateRawImmediate(instr);
840
  delta += Assembler::GetCmpImmediateRegister(instr).code() * kOff12Mask;
841 842 843 844 845 846 847
  // If the delta is 0 the instruction is cmp r0, #0 which also signals that
  // nothing was inlined.
  if (delta == 0) {
    return;
  }

  if (FLAG_trace_ic) {
848 849
    PrintF("[  patching ic at %p, cmp=%p, delta=%d\n", address,
           cmp_instruction_address, delta);
850 851 852 853 854 855 856
  }

  Address patch_address =
      cmp_instruction_address - delta * Instruction::kInstrSize;
  Instr instr_at_patch = Assembler::instr_at(patch_address);
  Instr branch_instr =
      Assembler::instr_at(patch_address + Instruction::kInstrSize);
857 858 859 860 861 862 863 864
  // This is patching a conditional "jump if not smi/jump if smi" site.
  // Enabling by changing from
  //   cmp rx, rx
  //   b eq/ne, <target>
  // to
  //   tst rx, #kSmiTagMask
  //   b ne/eq, <target>
  // and vice-versa to be disabled again.
865
  CodePatcher patcher(isolate, patch_address, 2);
866 867
  Register reg = Assembler::GetRn(instr_at_patch);
  if (check == ENABLE_INLINED_SMI_CHECK) {
868 869
    DCHECK(Assembler::IsCmpRegister(instr_at_patch));
    DCHECK_EQ(Assembler::GetRn(instr_at_patch).code(),
870 871 872
              Assembler::GetRm(instr_at_patch).code());
    patcher.masm()->tst(reg, Operand(kSmiTagMask));
  } else {
873 874
    DCHECK(check == DISABLE_INLINED_SMI_CHECK);
    DCHECK(Assembler::IsTstImmediate(instr_at_patch));
875 876
    patcher.masm()->cmp(reg, reg);
  }
877
  DCHECK(Assembler::IsBranch(branch_instr));
878 879 880
  if (Assembler::GetCondition(branch_instr) == eq) {
    patcher.EmitCondition(ne);
  } else {
881
    DCHECK(Assembler::GetCondition(branch_instr) == ne);
882 883
    patcher.EmitCondition(eq);
  }
884
}
885 886
}  // namespace internal
}  // namespace v8
887 888

#endif  // V8_TARGET_ARCH_ARM