handler-compiler-arm64.cc 28.6 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#if V8_TARGET_ARCH_ARM64

#include "src/ic/call-optimization.h"
#include "src/ic/handler-compiler.h"
9
#include "src/ic/ic.h"
10
#include "src/isolate-inl.h"
11 12 13 14 15 16

namespace v8 {
namespace internal {

#define __ ACCESS_MASM(masm)

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
void PropertyHandlerCompiler::PushVectorAndSlot(Register vector,
                                                Register slot) {
  MacroAssembler* masm = this->masm();
  __ Push(vector);
  __ Push(slot);
}


void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) {
  MacroAssembler* masm = this->masm();
  __ Pop(slot);
  __ Pop(vector);
}


void PropertyHandlerCompiler::DiscardVectorAndSlot() {
  MacroAssembler* masm = this->masm();
  // Remove vector and slot.
  __ Drop(2);
}

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
    MacroAssembler* masm, Label* miss_label, Register receiver,
    Handle<Name> name, Register scratch0, Register scratch1) {
  DCHECK(!AreAliased(receiver, scratch0, scratch1));
  DCHECK(name->IsUniqueName());
  Counters* counters = masm->isolate()->counters();
  __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
  __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);

  Label done;

  const int kInterceptorOrAccessCheckNeededMask =
      (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);

  // Bail out if the receiver has a named interceptor or requires access checks.
  Register map = scratch1;
  __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
  __ Ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
  __ Tst(scratch0, kInterceptorOrAccessCheckNeededMask);
  __ B(ne, miss_label);

  // Check that receiver is a JSObject.
  __ Ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
62
  __ Cmp(scratch0, FIRST_JS_RECEIVER_TYPE);
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
  __ B(lt, miss_label);

  // Load properties array.
  Register properties = scratch0;
  __ Ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
  // Check that the properties array is a dictionary.
  __ Ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset));
  __ JumpIfNotRoot(map, Heap::kHashTableMapRootIndex, miss_label);

  NameDictionaryLookupStub::GenerateNegativeLookup(
      masm, miss_label, &done, receiver, properties, name, scratch1);
  __ Bind(&done);
  __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
}


void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype(
80
    MacroAssembler* masm, int index, Register result, Label* miss) {
81
  __ LoadNativeContextSlot(index, result);
82
  // Load its initial map. The global functions all have initial maps.
83 84
  __ Ldr(result,
         FieldMemOperand(result, JSFunction::kPrototypeOrInitialMapOffset));
85
  // Load the prototype from the initial map.
86
  __ Ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
}


void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype(
    MacroAssembler* masm, Register receiver, Register scratch1,
    Register scratch2, Label* miss_label) {
  __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
  // TryGetFunctionPrototype can't put the result directly in x0 because the
  // 3 inputs registers can't alias and we call this function from
  // LoadIC::GenerateFunctionPrototype, where receiver is x0. So we explicitly
  // move the result in x0.
  __ Mov(x0, scratch1);
  __ Ret();
}


// Generate code to check that a global property cell is empty. Create
// the property cell at compilation time if no cell exists for the
// property.
void PropertyHandlerCompiler::GenerateCheckPropertyCell(
    MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
    Register scratch, Label* miss) {
109
  Handle<PropertyCell> cell = JSGlobalObject::EnsurePropertyCell(global, name);
110
  DCHECK(cell->value()->IsTheHole());
111 112
  Handle<WeakCell> weak_cell = masm->isolate()->factory()->NewWeakCell(cell);
  __ LoadWeakValue(scratch, weak_cell, miss);
113
  __ Ldr(scratch, FieldMemOperand(scratch, PropertyCell::kValueOffset));
114 115 116 117 118 119 120 121
  __ JumpIfNotRoot(scratch, Heap::kTheHoleValueRootIndex, miss);
}


static void PushInterceptorArguments(MacroAssembler* masm, Register receiver,
                                     Register holder, Register name,
                                     Handle<JSObject> holder_obj) {
  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
122 123 124 125 126
  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);

  __ Push(name, receiver, holder);
127 128 129 130 131
}


static void CompileCallLoadPropertyWithInterceptor(
    MacroAssembler* masm, Register receiver, Register holder, Register name,
132
    Handle<JSObject> holder_obj, Runtime::FunctionId id) {
133 134
  PushInterceptorArguments(masm, receiver, holder, name, holder_obj);

135
  __ CallRuntime(id, NamedLoadHandlerCompiler::kInterceptorArgsLength);
136 137 138 139
}


// Generate call to api function.
140
void PropertyHandlerCompiler::GenerateApiAccessorCall(
141 142
    MacroAssembler* masm, const CallOptimization& optimization,
    Handle<Map> receiver_map, Register receiver, Register scratch,
143 144 145
    bool is_store, Register store_parameter, Register accessor_holder,
    int accessor_index) {
  DCHECK(!AreAliased(accessor_holder, scratch));
146 147 148 149 150
  DCHECK(!AreAliased(receiver, scratch));

  MacroAssembler::PushPopQueue queue(masm);
  queue.Queue(receiver);
  // Write the arguments to the stack frame.
151 152 153 154
  if (is_store) {
    DCHECK(!receiver.is(store_parameter));
    DCHECK(!scratch.is(store_parameter));
    queue.Queue(store_parameter);
155 156 157 158 159 160 161
  }
  queue.PushQueued();

  DCHECK(optimization.is_simple_api_call());

  // Abi for CallApiFunctionStub.
  Register callee = x0;
162
  Register data = x4;
163 164 165
  Register holder = x2;
  Register api_function_address = x1;

166 167 168 169
  // Put callee in place.
  __ LoadAccessor(callee, accessor_holder, accessor_index,
                  is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER);

170 171
  // Put holder in place.
  CallOptimization::HolderLookup holder_lookup;
172 173 174
  int holder_depth = 0;
  optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup,
                                          &holder_depth);
175 176 177 178 179
  switch (holder_lookup) {
    case CallOptimization::kHolderIsReceiver:
      __ Mov(holder, receiver);
      break;
    case CallOptimization::kHolderFound:
180 181 182 183 184 185
      __ Ldr(holder, FieldMemOperand(receiver, HeapObject::kMapOffset));
      __ Ldr(holder, FieldMemOperand(holder, Map::kPrototypeOffset));
      for (int i = 1; i < holder_depth; i++) {
        __ Ldr(holder, FieldMemOperand(holder, HeapObject::kMapOffset));
        __ Ldr(holder, FieldMemOperand(holder, Map::kPrototypeOffset));
      }
186 187 188 189 190 191 192 193 194
      break;
    case CallOptimization::kHolderNotFound:
      UNREACHABLE();
      break;
  }

  Isolate* isolate = masm->isolate();
  Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
  bool call_data_undefined = false;
195 196
  // Put call data in place.
  if (api_call_info->data()->IsUndefined()) {
197
    call_data_undefined = true;
198
    __ LoadRoot(data, Heap::kUndefinedValueRootIndex);
199
  } else {
200 201 202 203 204 205
    __ Ldr(data,
           FieldMemOperand(callee, JSFunction::kSharedFunctionInfoOffset));
    __ Ldr(data,
           FieldMemOperand(data, SharedFunctionInfo::kFunctionDataOffset));
    __ Ldr(data, FieldMemOperand(data, FunctionTemplateInfo::kCallCodeOffset));
    __ Ldr(data, FieldMemOperand(data, CallHandlerInfo::kDataOffset));
206 207
  }

208 209 210 211 212 213 214
  if (api_call_info->fast_handler()->IsCode()) {
    // Just tail call into the fast handler if present.
    __ Jump(handle(Code::cast(api_call_info->fast_handler())),
            RelocInfo::CODE_TARGET);
    return;
  }

215 216 217 218 219 220 221 222
  // Put api_function_address in place.
  Address function_address = v8::ToCData<Address>(api_call_info->callback());
  ApiFunction fun(function_address);
  ExternalReference ref = ExternalReference(
      &fun, ExternalReference::DIRECT_API_CALL, masm->isolate());
  __ Mov(api_function_address, ref);

  // Jump to stub.
223
  CallApiAccessorStub stub(isolate, is_store, call_data_undefined);
224 225 226 227 228
  __ TailCallStub(&stub);
}


void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
229 230
    MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
    int accessor_index, int expected_arguments, Register scratch) {
231 232 233 234 235 236 237 238 239 240
  // ----------- S t a t e -------------
  //  -- lr    : return address
  // -----------------------------------
  Label miss;
  {
    FrameScope scope(masm, StackFrame::INTERNAL);

    // Save value register, so we can restore it later.
    __ Push(value());

241
    if (accessor_index >= 0) {
242 243 244
      DCHECK(!AreAliased(holder, scratch));
      DCHECK(!AreAliased(receiver, scratch));
      DCHECK(!AreAliased(value(), scratch));
245
      // Call the JavaScript setter with receiver and value on the stack.
246
      if (map->IsJSGlobalObjectMap()) {
247
        // Swap in the global receiver.
248
        __ Ldr(scratch,
249
               FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
250
        receiver = scratch;
251 252 253
      }
      __ Push(receiver, value());
      ParameterCount actual(1);
254
      ParameterCount expected(expected_arguments);
255
      __ LoadAccessor(x1, holder, accessor_index, ACCESSOR_SETTER);
256 257
      __ InvokeFunction(x1, expected, actual, CALL_FUNCTION,
                        CheckDebugStepCallWrapper());
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
    } else {
      // If we generate a global code snippet for deoptimization only, remember
      // the place to continue after deoptimization.
      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
    }

    // We have to return the passed value, not the return value of the setter.
    __ Pop(x0);

    // Restore context register.
    __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
  }
  __ Ret();
}


void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
275 276
    MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
    int accessor_index, int expected_arguments, Register scratch) {
277 278 279
  {
    FrameScope scope(masm, StackFrame::INTERNAL);

280
    if (accessor_index >= 0) {
281 282
      DCHECK(!AreAliased(holder, scratch));
      DCHECK(!AreAliased(receiver, scratch));
283
      // Call the JavaScript getter with the receiver on the stack.
284
      if (map->IsJSGlobalObjectMap()) {
285
        // Swap in the global receiver.
286
        __ Ldr(scratch,
287
               FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
288
        receiver = scratch;
289 290 291
      }
      __ Push(receiver);
      ParameterCount actual(0);
292
      ParameterCount expected(expected_arguments);
293
      __ LoadAccessor(x1, holder, accessor_index, ACCESSOR_GETTER);
294 295
      __ InvokeFunction(x1, expected, actual, CALL_FUNCTION,
                        CheckDebugStepCallWrapper());
296 297 298 299 300 301 302 303 304 305 306 307 308
    } else {
      // If we generate a global code snippet for deoptimization only, remember
      // the place to continue after deoptimization.
      masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
    }

    // Restore context register.
    __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
  }
  __ Ret();
}


309
static void StoreIC_PushArgs(MacroAssembler* masm) {
310 311 312 313
  __ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
          StoreDescriptor::ValueRegister(),
          VectorStoreICDescriptor::SlotRegister(),
          VectorStoreICDescriptor::VectorRegister());
314 315 316
}


317
void NamedStoreHandlerCompiler::GenerateSlow(MacroAssembler* masm) {
318
  StoreIC_PushArgs(masm);
319 320 321

  // The slow case calls into the runtime to complete the store without causing
  // an IC miss that would otherwise cause a transition to the generic stub.
322
  __ TailCallRuntime(Runtime::kStoreIC_Slow, 5, 1);
323 324 325 326 327
}


void ElementHandlerCompiler::GenerateStoreSlow(MacroAssembler* masm) {
  ASM_LOCATION("ElementHandlerCompiler::GenerateStoreSlow");
328
  StoreIC_PushArgs(masm);
329 330 331

  // The slow case calls into the runtime to complete the store without causing
  // an IC miss that would otherwise cause a transition to the generic stub.
332
  __ TailCallRuntime(Runtime::kKeyedStoreIC_Slow, 5, 1);
333 334 335
}


336 337 338 339 340 341 342
#undef __
#define __ ACCESS_MASM(masm())


Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
    Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) {
  Label miss;
343 344 345
  if (IC::ICUseVector(kind())) {
    PushVectorAndSlot();
  }
346
  FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
347 348

  // Get the value from the cell.
349
  Register result = StoreDescriptor::ValueRegister();
350 351
  Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
  __ LoadWeakValue(result, weak_cell, &miss);
352
  __ Ldr(result, FieldMemOperand(result, PropertyCell::kValueOffset));
353 354 355 356 357 358 359 360

  // Check for deleted property if property can actually be deleted.
  if (is_configurable) {
    __ JumpIfRoot(result, Heap::kTheHoleValueRootIndex, &miss);
  }

  Counters* counters = isolate()->counters();
  __ IncrementCounter(counters->named_load_global_stub(), 1, x1, x3);
361 362 363
  if (IC::ICUseVector(kind())) {
    DiscardVectorAndSlot();
  }
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
  __ Ret();

  FrontendFooter(name, &miss);

  // Return the generated code.
  return GetCode(kind(), Code::NORMAL, name);
}


Handle<Code> NamedStoreHandlerCompiler::CompileStoreInterceptor(
    Handle<Name> name) {
  Label miss;

  ASM_LOCATION("NamedStoreHandlerCompiler::CompileStoreInterceptor");

  __ Push(receiver(), this->name(), value());

  // Do tail-call to the runtime system.
382
  __ TailCallRuntime(Runtime::kStorePropertyWithInterceptor, 3, 1);
383 384 385 386 387 388

  // Return the generated code.
  return GetCode(kind(), Code::FAST, name);
}


389
Register NamedStoreHandlerCompiler::value() {
390
  return StoreDescriptor::ValueRegister();
391
}
392 393 394 395 396 397 398 399 400 401 402


void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
                                                    Handle<Name> name) {
  if (!label->is_unused()) {
    __ Bind(label);
    __ Mov(this->name(), Operand(name));
  }
}


403
void NamedStoreHandlerCompiler::GenerateRestoreName(Handle<Name> name) {
404
  __ Mov(this->name(), Operand(name));
405 406 407
}


408 409
void NamedStoreHandlerCompiler::RearrangeVectorAndSlot(
    Register current_map, Register destination_map) {
410 411 412 413
  DCHECK(false);  // Not implemented.
}


414
void NamedStoreHandlerCompiler::GenerateRestoreMap(Handle<Map> transition,
415
                                                   Register map_reg,
416 417 418 419 420 421 422 423 424
                                                   Register scratch,
                                                   Label* miss) {
  Handle<WeakCell> cell = Map::WeakCellForMap(transition);
  DCHECK(!map_reg.is(scratch));
  __ LoadWeakValue(map_reg, cell, miss);
  if (transition->CanBeDeprecated()) {
    __ Ldrsw(scratch, FieldMemOperand(map_reg, Map::kBitField3Offset));
    __ TestAndBranchIfAnySet(scratch, Map::Deprecated::kMask, miss);
  }
425
}
426 427


428 429
void NamedStoreHandlerCompiler::GenerateConstantCheck(Register map_reg,
                                                      int descriptor,
430
                                                      Register value_reg,
431
                                                      Register scratch,
432
                                                      Label* miss_label) {
433 434 435 436 437 438 439
  DCHECK(!map_reg.is(scratch));
  DCHECK(!map_reg.is(value_reg));
  DCHECK(!value_reg.is(scratch));
  __ LoadInstanceDescriptors(map_reg, scratch);
  __ Ldr(scratch,
         FieldMemOperand(scratch, DescriptorArray::GetValueOffset(descriptor)));
  __ Cmp(value_reg, scratch);
440
  __ B(ne, miss_label);
441 442 443
}


444 445 446
void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type,
                                                        Register value_reg,
                                                        Label* miss_label) {
447 448 449 450
  Register map_reg = scratch1();
  Register scratch = scratch2();
  DCHECK(!value_reg.is(map_reg));
  DCHECK(!value_reg.is(scratch));
451
  __ JumpIfSmi(value_reg, miss_label);
452 453
  HeapType::Iterator<Map> it = field_type->Classes();
  if (!it.Done()) {
454
    __ Ldr(map_reg, FieldMemOperand(value_reg, HeapObject::kMapOffset));
455 456
    Label do_store;
    while (true) {
457
      __ CmpWeakValue(map_reg, Map::WeakCellForMap(it.Current()), scratch);
458 459 460 461 462 463
      it.Advance();
      if (it.Done()) {
        __ B(ne, miss_label);
        break;
      }
      __ B(eq, &do_store);
464
    }
465
    __ Bind(&do_store);
466 467 468 469 470 471
  }
}


Register PropertyHandlerCompiler::CheckPrototypes(
    Register object_reg, Register holder_reg, Register scratch1,
472 473
    Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
    ReturnHolder return_what) {
474
  Handle<Map> receiver_map = map();
475 476 477 478 479

  // object_reg and holder_reg registers can alias.
  DCHECK(!AreAliased(object_reg, scratch1, scratch2));
  DCHECK(!AreAliased(holder_reg, scratch1, scratch2));

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
  if (FLAG_eliminate_prototype_chain_checks) {
    Handle<Cell> validity_cell =
        Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
    if (!validity_cell.is_null()) {
      DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
                validity_cell->value());
      __ Mov(scratch1, Operand(validity_cell));
      __ Ldr(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset));
      __ Cmp(scratch1, Operand(Smi::FromInt(Map::kPrototypeChainValid)));
      __ B(ne, miss);
    }

    // The prototype chain of primitives (and their JSValue wrappers) depends
    // on the native context, which can't be guarded by validity cells.
    // |object_reg| holds the native context specific prototype in this case;
    // we need to check its map.
    if (check == CHECK_ALL_MAPS) {
      __ Ldr(scratch1, FieldMemOperand(object_reg, HeapObject::kMapOffset));
      Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
      __ CmpWeakValue(scratch1, cell, scratch2);
      __ B(ne, miss);
    }
  }

504 505 506 507 508
  // Keep track of the current object in register reg.
  Register reg = object_reg;
  int depth = 0;

  Handle<JSObject> current = Handle<JSObject>::null();
509 510
  if (receiver_map->IsJSGlobalObjectMap()) {
    current = isolate()->global_object();
511
  }
512 513 514 515 516 517 518 519 520 521 522 523

  // Check access rights to the global object.  This has to happen after
  // the map check so that we know that the object is actually a global
  // object.
  // This allows us to install generated handlers for accesses to the
  // global proxy (as opposed to using slow ICs). See corresponding code
  // in LookupForRead().
  if (receiver_map->IsJSGlobalProxyMap()) {
    UseScratchRegisterScope temps(masm());
    __ CheckAccessGlobalProxy(reg, scratch2, temps.AcquireX(), miss);
  }

524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
  Handle<JSObject> prototype = Handle<JSObject>::null();
  Handle<Map> current_map = receiver_map;
  Handle<Map> holder_map(holder()->map());
  // Traverse the prototype chain and check the maps in the prototype chain for
  // fast and global objects or do negative lookup for normal objects.
  while (!current_map.is_identical_to(holder_map)) {
    ++depth;

    // Only global objects and objects that do not require access
    // checks are allowed in stubs.
    DCHECK(current_map->IsJSGlobalProxyMap() ||
           !current_map->is_access_check_needed());

    prototype = handle(JSObject::cast(current_map->prototype()));
    if (current_map->is_dictionary_map() &&
        !current_map->IsJSGlobalObjectMap()) {
      DCHECK(!current_map->IsJSGlobalProxyMap());  // Proxy maps are fast.
      if (!name->IsUniqueName()) {
        DCHECK(name->IsString());
        name = factory()->InternalizeString(Handle<String>::cast(name));
      }
      DCHECK(current.is_null() || (current->property_dictionary()->FindEntry(
                                       name) == NameDictionary::kNotFound));

548 549 550 551
      if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
        // TODO(jkummerow): Cache and re-use weak cell.
        __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
      }
552 553 554
      GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
                                       scratch2);

555 556 557 558
      if (!FLAG_eliminate_prototype_chain_checks) {
        __ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
        __ Ldr(holder_reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
      }
559 560
    } else {
      Register map_reg = scratch1;
561 562 563
      if (!FLAG_eliminate_prototype_chain_checks) {
        __ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
      }
564
      if (current_map->IsJSGlobalObjectMap()) {
565 566
        GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
                                  name, scratch2, miss);
567 568
      } else if (!FLAG_eliminate_prototype_chain_checks &&
                 (depth != 1 || check == CHECK_ALL_MAPS)) {
569 570 571
        Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
        __ CmpWeakValue(map_reg, cell, scratch2);
        __ B(ne, miss);
572
      }
573 574 575
      if (!FLAG_eliminate_prototype_chain_checks) {
        __ Ldr(holder_reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
      }
576 577
    }

578
    reg = holder_reg;  // From now on the object will be in holder_reg.
579 580 581 582 583
    // Go to the next object in the prototype chain.
    current = prototype;
    current_map = handle(current->map());
  }

584 585
  DCHECK(!current_map->IsJSGlobalProxyMap());

586 587 588
  // Log the check depth.
  LOG(isolate(), IntEvent("check-maps-depth", depth + 1));

589 590
  if (!FLAG_eliminate_prototype_chain_checks &&
      (depth != 0 || check == CHECK_ALL_MAPS)) {
591
    // Check the holder map.
592 593 594 595
    __ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
    Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
    __ CmpWeakValue(scratch1, cell, scratch2);
    __ B(ne, miss);
596 597
  }

598 599 600 601 602
  bool return_holder = return_what == RETURN_HOLDER;
  if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
    __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
  }

603
  // Return the register containing the holder.
604
  return return_holder ? reg : no_reg;
605 606 607 608 609 610 611 612 613
}


void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
  if (!miss->is_unused()) {
    Label success;
    __ B(&success);

    __ Bind(miss);
614 615 616 617
    if (IC::ICUseVector(kind())) {
      DCHECK(kind() == Code::LOAD_IC);
      PopVectorAndSlot();
    }
618 619 620 621 622 623 624 625 626 627 628 629 630
    TailCallBuiltin(masm(), MissBuiltin(kind()));

    __ Bind(&success);
  }
}


void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
  if (!miss->is_unused()) {
    Label success;
    __ B(&success);

    GenerateRestoreName(miss, name);
631
    if (IC::ICUseVector(kind())) PopVectorAndSlot();
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
    TailCallBuiltin(masm(), MissBuiltin(kind()));

    __ Bind(&success);
  }
}


void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) {
  // Return the constant value.
  __ LoadObject(x0, value);
  __ Ret();
}


void NamedLoadHandlerCompiler::GenerateLoadCallback(
    Register reg, Handle<ExecutableAccessorInfo> callback) {
  DCHECK(!AreAliased(scratch2(), scratch3(), scratch4(), reg));

  // Build ExecutableAccessorInfo::args_ list on the stack and push property
  // name below the exit frame to make GC aware of them and store pointers to
  // them.
  STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0);
  STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1);
  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2);
  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3);
  STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4);
  STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5);
  STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6);

  __ Push(receiver());

663 664 665
  Handle<Object> data(callback->data(), isolate());
  if (data->IsUndefined() || data->IsSmi()) {
    __ Mov(scratch3(), Operand(data));
666
  } else {
667 668 669 670 671
    Handle<WeakCell> cell =
        isolate()->factory()->NewWeakCell(Handle<HeapObject>::cast(data));
    // The callback is alive if this instruction is executed,
    // so the weak cell is not cleared and points to data.
    __ GetWeakValue(scratch3(), cell);
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
  }
  __ LoadRoot(scratch4(), Heap::kUndefinedValueRootIndex);
  __ Mov(scratch2(), Operand(ExternalReference::isolate_address(isolate())));
  __ Push(scratch3(), scratch4(), scratch4(), scratch2(), reg, name());

  Register args_addr = scratch2();
  __ Add(args_addr, __ StackPointer(), kPointerSize);

  // Stack at this point:
  //              sp[40] callback data
  //              sp[32] undefined
  //              sp[24] undefined
  //              sp[16] isolate
  // args_addr -> sp[8]  reg
  //              sp[0]  name

  // Abi for CallApiGetter.
  Register getter_address_reg = x2;

  // Set up the call.
  Address getter_address = v8::ToCData<Address>(callback->getter());
  ApiFunction fun(getter_address);
  ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL;
  ExternalReference ref = ExternalReference(&fun, type, isolate());
  __ Mov(getter_address_reg, ref);

  CallApiGetterStub stub(isolate());
  __ TailCallStub(&stub);
}


void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
    LookupIterator* it, Register holder_reg) {
  DCHECK(!AreAliased(receiver(), this->name(), scratch1(), scratch2(),
                     scratch3()));
  DCHECK(holder()->HasNamedInterceptor());
  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());

  // Compile the interceptor call, followed by inline code to load the
  // property from further up the prototype chain if the call fails.
  // Check that the maps haven't changed.
  DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));

  // Preserve the receiver register explicitly whenever it is different from the
  // holder and it is needed should the interceptor return without any result.
  // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
  // case might cause a miss during the prototype check.
  bool must_perform_prototype_check =
      !holder().is_identical_to(it->GetHolder<JSObject>());
  bool must_preserve_receiver_reg =
      !receiver().is(holder_reg) &&
723
      (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
724 725 726 727 728 729 730 731 732 733

  // Save necessary data before invoking an interceptor.
  // Requires a frame to make GC aware of pushed pointers.
  {
    FrameScope frame_scope(masm(), StackFrame::INTERNAL);
    if (must_preserve_receiver_reg) {
      __ Push(receiver(), holder_reg, this->name());
    } else {
      __ Push(holder_reg, this->name());
    }
734
    InterceptorVectorSlotPush(holder_reg);
735 736 737 738 739
    // Invoke an interceptor.  Note: map checks from receiver to
    // interceptor's holder has been compiled before (see a caller
    // of this method.)
    CompileCallLoadPropertyWithInterceptor(
        masm(), receiver(), holder_reg, this->name(), holder(),
740
        Runtime::kLoadPropertyWithInterceptorOnly);
741 742 743 744 745 746 747 748 749 750

    // Check if interceptor provided a value for property.  If it's
    // the case, return immediately.
    Label interceptor_failed;
    __ JumpIfRoot(x0, Heap::kNoInterceptorResultSentinelRootIndex,
                  &interceptor_failed);
    frame_scope.GenerateLeaveFrame();
    __ Ret();

    __ Bind(&interceptor_failed);
751
    InterceptorVectorSlotPop(holder_reg);
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
    if (must_preserve_receiver_reg) {
      __ Pop(this->name(), holder_reg, receiver());
    } else {
      __ Pop(this->name(), holder_reg);
    }
    // Leave the internal frame.
  }

  GenerateLoadPostInterceptor(it, holder_reg);
}


void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
  // Call the runtime system to load the interceptor.
  DCHECK(holder()->HasNamedInterceptor());
  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
  PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
                           holder());

771 772
  __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor,
                     NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
773 774 775 776
}


Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
777 778
    Handle<JSObject> object, Handle<Name> name,
    Handle<ExecutableAccessorInfo> callback) {
779
  ASM_LOCATION("NamedStoreHandlerCompiler::CompileStoreCallback");
780
  Register holder_reg = Frontend(name);
781 782 783 784 785 786 787

  // Stub never generated for non-global objects that require access checks.
  DCHECK(holder()->IsJSGlobalProxy() || !holder()->IsAccessCheckNeeded());

  // receiver() and holder_reg can alias.
  DCHECK(!AreAliased(receiver(), scratch1(), scratch2(), value()));
  DCHECK(!AreAliased(holder_reg, scratch1(), scratch2(), value()));
788 789 790 791 792 793 794 795
  // If the callback cannot leak, then push the callback directly,
  // otherwise wrap it in a weak cell.
  if (callback->data()->IsUndefined() || callback->data()->IsSmi()) {
    __ Mov(scratch1(), Operand(callback));
  } else {
    Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
    __ Mov(scratch1(), Operand(cell));
  }
796 797 798 799
  __ Mov(scratch2(), Operand(name));
  __ Push(receiver(), holder_reg, scratch1(), scratch2(), value());

  // Do tail-call to the runtime system.
800
  __ TailCallRuntime(Runtime::kStoreCallbackProperty, 5, 1);
801 802 803 804 805 806 807

  // Return the generated code.
  return GetCode(kind(), Code::FAST, name);
}


#undef __
808 809
}  // namespace internal
}  // namespace v8
810 811

#endif  // V8_TARGET_ARCH_IA32