1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/v8.h"
6
7#if V8_TARGET_ARCH_ARM64
8
9#include "src/ic/call-optimization.h"
10#include "src/ic/handler-compiler.h"
11#include "src/ic/ic.h"
12
13namespace v8 {
14namespace internal {
15
16#define __ ACCESS_MASM(masm)
17
18
19void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
20    MacroAssembler* masm, Label* miss_label, Register receiver,
21    Handle<Name> name, Register scratch0, Register scratch1) {
22  DCHECK(!AreAliased(receiver, scratch0, scratch1));
23  DCHECK(name->IsUniqueName());
24  Counters* counters = masm->isolate()->counters();
25  __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
26  __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
27
28  Label done;
29
30  const int kInterceptorOrAccessCheckNeededMask =
31      (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
32
33  // Bail out if the receiver has a named interceptor or requires access checks.
34  Register map = scratch1;
35  __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
36  __ Ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
37  __ Tst(scratch0, kInterceptorOrAccessCheckNeededMask);
38  __ B(ne, miss_label);
39
40  // Check that receiver is a JSObject.
41  __ Ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
42  __ Cmp(scratch0, FIRST_SPEC_OBJECT_TYPE);
43  __ B(lt, miss_label);
44
45  // Load properties array.
46  Register properties = scratch0;
47  __ Ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
48  // Check that the properties array is a dictionary.
49  __ Ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset));
50  __ JumpIfNotRoot(map, Heap::kHashTableMapRootIndex, miss_label);
51
52  NameDictionaryLookupStub::GenerateNegativeLookup(
53      masm, miss_label, &done, receiver, properties, name, scratch1);
54  __ Bind(&done);
55  __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
56}
57
58
59void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype(
60    MacroAssembler* masm, int index, Register prototype, Label* miss) {
61  Isolate* isolate = masm->isolate();
62  // Get the global function with the given index.
63  Handle<JSFunction> function(
64      JSFunction::cast(isolate->native_context()->get(index)));
65
66  // Check we're still in the same context.
67  Register scratch = prototype;
68  __ Ldr(scratch, GlobalObjectMemOperand());
69  __ Ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset));
70  __ Ldr(scratch, ContextMemOperand(scratch, index));
71  __ Cmp(scratch, Operand(function));
72  __ B(ne, miss);
73
74  // Load its initial map. The global functions all have initial maps.
75  __ Mov(prototype, Operand(Handle<Map>(function->initial_map())));
76  // Load the prototype from the initial map.
77  __ Ldr(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
78}
79
80
81void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype(
82    MacroAssembler* masm, Register receiver, Register scratch1,
83    Register scratch2, Label* miss_label) {
84  __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
85  // TryGetFunctionPrototype can't put the result directly in x0 because the
86  // 3 inputs registers can't alias and we call this function from
87  // LoadIC::GenerateFunctionPrototype, where receiver is x0. So we explicitly
88  // move the result in x0.
89  __ Mov(x0, scratch1);
90  __ Ret();
91}
92
93
94// Generate code to check that a global property cell is empty. Create
95// the property cell at compilation time if no cell exists for the
96// property.
97void PropertyHandlerCompiler::GenerateCheckPropertyCell(
98    MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
99    Register scratch, Label* miss) {
100  Handle<Cell> cell = JSGlobalObject::EnsurePropertyCell(global, name);
101  DCHECK(cell->value()->IsTheHole());
102  __ Mov(scratch, Operand(cell));
103  __ Ldr(scratch, FieldMemOperand(scratch, Cell::kValueOffset));
104  __ JumpIfNotRoot(scratch, Heap::kTheHoleValueRootIndex, miss);
105}
106
107
108static void PushInterceptorArguments(MacroAssembler* masm, Register receiver,
109                                     Register holder, Register name,
110                                     Handle<JSObject> holder_obj) {
111  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
112  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsInfoIndex == 1);
113  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 2);
114  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 3);
115  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 4);
116
117  __ Push(name);
118  Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor());
119  DCHECK(!masm->isolate()->heap()->InNewSpace(*interceptor));
120  Register scratch = name;
121  __ Mov(scratch, Operand(interceptor));
122  __ Push(scratch, receiver, holder);
123}
124
125
126static void CompileCallLoadPropertyWithInterceptor(
127    MacroAssembler* masm, Register receiver, Register holder, Register name,
128    Handle<JSObject> holder_obj, IC::UtilityId id) {
129  PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
130
131  __ CallExternalReference(ExternalReference(IC_Utility(id), masm->isolate()),
132                           NamedLoadHandlerCompiler::kInterceptorArgsLength);
133}
134
135
136// Generate call to api function.
137void PropertyHandlerCompiler::GenerateFastApiCall(
138    MacroAssembler* masm, const CallOptimization& optimization,
139    Handle<Map> receiver_map, Register receiver, Register scratch,
140    bool is_store, int argc, Register* values) {
141  DCHECK(!AreAliased(receiver, scratch));
142
143  MacroAssembler::PushPopQueue queue(masm);
144  queue.Queue(receiver);
145  // Write the arguments to the stack frame.
146  for (int i = 0; i < argc; i++) {
147    Register arg = values[argc - 1 - i];
148    DCHECK(!AreAliased(receiver, scratch, arg));
149    queue.Queue(arg);
150  }
151  queue.PushQueued();
152
153  DCHECK(optimization.is_simple_api_call());
154
155  // Abi for CallApiFunctionStub.
156  Register callee = x0;
157  Register call_data = x4;
158  Register holder = x2;
159  Register api_function_address = x1;
160
161  // Put holder in place.
162  CallOptimization::HolderLookup holder_lookup;
163  Handle<JSObject> api_holder =
164      optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup);
165  switch (holder_lookup) {
166    case CallOptimization::kHolderIsReceiver:
167      __ Mov(holder, receiver);
168      break;
169    case CallOptimization::kHolderFound:
170      __ LoadObject(holder, api_holder);
171      break;
172    case CallOptimization::kHolderNotFound:
173      UNREACHABLE();
174      break;
175  }
176
177  Isolate* isolate = masm->isolate();
178  Handle<JSFunction> function = optimization.constant_function();
179  Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
180  Handle<Object> call_data_obj(api_call_info->data(), isolate);
181
182  // Put callee in place.
183  __ LoadObject(callee, function);
184
185  bool call_data_undefined = false;
186  // Put call_data in place.
187  if (isolate->heap()->InNewSpace(*call_data_obj)) {
188    __ LoadObject(call_data, api_call_info);
189    __ Ldr(call_data, FieldMemOperand(call_data, CallHandlerInfo::kDataOffset));
190  } else if (call_data_obj->IsUndefined()) {
191    call_data_undefined = true;
192    __ LoadRoot(call_data, Heap::kUndefinedValueRootIndex);
193  } else {
194    __ LoadObject(call_data, call_data_obj);
195  }
196
197  // Put api_function_address in place.
198  Address function_address = v8::ToCData<Address>(api_call_info->callback());
199  ApiFunction fun(function_address);
200  ExternalReference ref = ExternalReference(
201      &fun, ExternalReference::DIRECT_API_CALL, masm->isolate());
202  __ Mov(api_function_address, ref);
203
204  // Jump to stub.
205  CallApiFunctionStub stub(isolate, is_store, call_data_undefined, argc);
206  __ TailCallStub(&stub);
207}
208
209
210void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
211    MacroAssembler* masm, Handle<HeapType> type, Register receiver,
212    Handle<JSFunction> setter) {
213  // ----------- S t a t e -------------
214  //  -- lr    : return address
215  // -----------------------------------
216  Label miss;
217
218  {
219    FrameScope scope(masm, StackFrame::INTERNAL);
220
221    // Save value register, so we can restore it later.
222    __ Push(value());
223
224    if (!setter.is_null()) {
225      // Call the JavaScript setter with receiver and value on the stack.
226      if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) {
227        // Swap in the global receiver.
228        __ Ldr(receiver,
229               FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
230      }
231      __ Push(receiver, value());
232      ParameterCount actual(1);
233      ParameterCount expected(setter);
234      __ InvokeFunction(setter, expected, actual, CALL_FUNCTION,
235                        NullCallWrapper());
236    } else {
237      // If we generate a global code snippet for deoptimization only, remember
238      // the place to continue after deoptimization.
239      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
240    }
241
242    // We have to return the passed value, not the return value of the setter.
243    __ Pop(x0);
244
245    // Restore context register.
246    __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
247  }
248  __ Ret();
249}
250
251
252void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
253    MacroAssembler* masm, Handle<HeapType> type, Register receiver,
254    Handle<JSFunction> getter) {
255  {
256    FrameScope scope(masm, StackFrame::INTERNAL);
257
258    if (!getter.is_null()) {
259      // Call the JavaScript getter with the receiver on the stack.
260      if (IC::TypeToMap(*type, masm->isolate())->IsJSGlobalObjectMap()) {
261        // Swap in the global receiver.
262        __ Ldr(receiver,
263               FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
264      }
265      __ Push(receiver);
266      ParameterCount actual(0);
267      ParameterCount expected(getter);
268      __ InvokeFunction(getter, expected, actual, CALL_FUNCTION,
269                        NullCallWrapper());
270    } else {
271      // If we generate a global code snippet for deoptimization only, remember
272      // the place to continue after deoptimization.
273      masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
274    }
275
276    // Restore context register.
277    __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
278  }
279  __ Ret();
280}
281
282
283void NamedStoreHandlerCompiler::GenerateSlow(MacroAssembler* masm) {
284  // Push receiver, name and value for runtime call.
285  __ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
286          StoreDescriptor::ValueRegister());
287
288  // The slow case calls into the runtime to complete the store without causing
289  // an IC miss that would otherwise cause a transition to the generic stub.
290  ExternalReference ref =
291      ExternalReference(IC_Utility(IC::kStoreIC_Slow), masm->isolate());
292  __ TailCallExternalReference(ref, 3, 1);
293}
294
295
296void ElementHandlerCompiler::GenerateStoreSlow(MacroAssembler* masm) {
297  ASM_LOCATION("ElementHandlerCompiler::GenerateStoreSlow");
298
299  // Push receiver, key and value for runtime call.
300  __ Push(StoreDescriptor::ReceiverRegister(), StoreDescriptor::NameRegister(),
301          StoreDescriptor::ValueRegister());
302
303  // The slow case calls into the runtime to complete the store without causing
304  // an IC miss that would otherwise cause a transition to the generic stub.
305  ExternalReference ref =
306      ExternalReference(IC_Utility(IC::kKeyedStoreIC_Slow), masm->isolate());
307  __ TailCallExternalReference(ref, 3, 1);
308}
309
310
311#undef __
312#define __ ACCESS_MASM(masm())
313
314
315Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
316    Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) {
317  Label miss;
318  FrontendHeader(receiver(), name, &miss);
319
320  // Get the value from the cell.
321  Register result = StoreDescriptor::ValueRegister();
322  __ Mov(result, Operand(cell));
323  __ Ldr(result, FieldMemOperand(result, Cell::kValueOffset));
324
325  // Check for deleted property if property can actually be deleted.
326  if (is_configurable) {
327    __ JumpIfRoot(result, Heap::kTheHoleValueRootIndex, &miss);
328  }
329
330  Counters* counters = isolate()->counters();
331  __ IncrementCounter(counters->named_load_global_stub(), 1, x1, x3);
332  __ Ret();
333
334  FrontendFooter(name, &miss);
335
336  // Return the generated code.
337  return GetCode(kind(), Code::NORMAL, name);
338}
339
340
341Handle<Code> NamedStoreHandlerCompiler::CompileStoreInterceptor(
342    Handle<Name> name) {
343  Label miss;
344
345  ASM_LOCATION("NamedStoreHandlerCompiler::CompileStoreInterceptor");
346
347  __ Push(receiver(), this->name(), value());
348
349  // Do tail-call to the runtime system.
350  ExternalReference store_ic_property = ExternalReference(
351      IC_Utility(IC::kStorePropertyWithInterceptor), isolate());
352  __ TailCallExternalReference(store_ic_property, 3, 1);
353
354  // Return the generated code.
355  return GetCode(kind(), Code::FAST, name);
356}
357
358
359Register NamedStoreHandlerCompiler::value() {
360  return StoreDescriptor::ValueRegister();
361}
362
363
364void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
365                                                    Handle<Name> name) {
366  if (!label->is_unused()) {
367    __ Bind(label);
368    __ Mov(this->name(), Operand(name));
369  }
370}
371
372
373// Generate StoreTransition code, value is passed in x0 register.
374// When leaving generated code after success, the receiver_reg and storage_reg
375// may be clobbered. Upon branch to miss_label, the receiver and name registers
376// have their original values.
377void NamedStoreHandlerCompiler::GenerateStoreTransition(
378    Handle<Map> transition, Handle<Name> name, Register receiver_reg,
379    Register storage_reg, Register value_reg, Register scratch1,
380    Register scratch2, Register scratch3, Label* miss_label, Label* slow) {
381  Label exit;
382
383  DCHECK(!AreAliased(receiver_reg, storage_reg, value_reg, scratch1, scratch2,
384                     scratch3));
385
386  // We don't need scratch3.
387  scratch3 = NoReg;
388
389  int descriptor = transition->LastAdded();
390  DescriptorArray* descriptors = transition->instance_descriptors();
391  PropertyDetails details = descriptors->GetDetails(descriptor);
392  Representation representation = details.representation();
393  DCHECK(!representation.IsNone());
394
395  if (details.type() == CONSTANT) {
396    Handle<Object> constant(descriptors->GetValue(descriptor), isolate());
397    __ LoadObject(scratch1, constant);
398    __ Cmp(value_reg, scratch1);
399    __ B(ne, miss_label);
400  } else if (representation.IsSmi()) {
401    __ JumpIfNotSmi(value_reg, miss_label);
402  } else if (representation.IsHeapObject()) {
403    __ JumpIfSmi(value_reg, miss_label);
404    HeapType* field_type = descriptors->GetFieldType(descriptor);
405    HeapType::Iterator<Map> it = field_type->Classes();
406    if (!it.Done()) {
407      __ Ldr(scratch1, FieldMemOperand(value_reg, HeapObject::kMapOffset));
408      Label do_store;
409      while (true) {
410        __ CompareMap(scratch1, it.Current());
411        it.Advance();
412        if (it.Done()) {
413          __ B(ne, miss_label);
414          break;
415        }
416        __ B(eq, &do_store);
417      }
418      __ Bind(&do_store);
419    }
420  } else if (representation.IsDouble()) {
421    UseScratchRegisterScope temps(masm());
422    DoubleRegister temp_double = temps.AcquireD();
423    __ SmiUntagToDouble(temp_double, value_reg, kSpeculativeUntag);
424
425    Label do_store;
426    __ JumpIfSmi(value_reg, &do_store);
427
428    __ CheckMap(value_reg, scratch1, Heap::kHeapNumberMapRootIndex, miss_label,
429                DONT_DO_SMI_CHECK);
430    __ Ldr(temp_double, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
431
432    __ Bind(&do_store);
433    __ AllocateHeapNumber(storage_reg, slow, scratch1, scratch2, temp_double,
434                          NoReg, MUTABLE);
435  }
436
437  // Stub never generated for objects that require access checks.
438  DCHECK(!transition->is_access_check_needed());
439
440  // Perform map transition for the receiver if necessary.
441  if (details.type() == FIELD &&
442      Map::cast(transition->GetBackPointer())->unused_property_fields() == 0) {
443    // The properties must be extended before we can store the value.
444    // We jump to a runtime call that extends the properties array.
445    __ Mov(scratch1, Operand(transition));
446    __ Push(receiver_reg, scratch1, value_reg);
447    __ TailCallExternalReference(
448        ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
449                          isolate()),
450        3, 1);
451    return;
452  }
453
454  // Update the map of the object.
455  __ Mov(scratch1, Operand(transition));
456  __ Str(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset));
457
458  // Update the write barrier for the map field.
459  __ RecordWriteField(receiver_reg, HeapObject::kMapOffset, scratch1, scratch2,
460                      kLRHasNotBeenSaved, kDontSaveFPRegs, OMIT_REMEMBERED_SET,
461                      OMIT_SMI_CHECK);
462
463  if (details.type() == CONSTANT) {
464    DCHECK(value_reg.is(x0));
465    __ Ret();
466    return;
467  }
468
469  int index = transition->instance_descriptors()->GetFieldIndex(
470      transition->LastAdded());
471
472  // Adjust for the number of properties stored in the object. Even in the
473  // face of a transition we can use the old map here because the size of the
474  // object and the number of in-object properties is not going to change.
475  index -= transition->inobject_properties();
476
477  // TODO(verwaest): Share this code as a code stub.
478  SmiCheck smi_check =
479      representation.IsTagged() ? INLINE_SMI_CHECK : OMIT_SMI_CHECK;
480  Register prop_reg = representation.IsDouble() ? storage_reg : value_reg;
481  if (index < 0) {
482    // Set the property straight into the object.
483    int offset = transition->instance_size() + (index * kPointerSize);
484    __ Str(prop_reg, FieldMemOperand(receiver_reg, offset));
485
486    if (!representation.IsSmi()) {
487      // Update the write barrier for the array address.
488      if (!representation.IsDouble()) {
489        __ Mov(storage_reg, value_reg);
490      }
491      __ RecordWriteField(receiver_reg, offset, storage_reg, scratch1,
492                          kLRHasNotBeenSaved, kDontSaveFPRegs,
493                          EMIT_REMEMBERED_SET, smi_check);
494    }
495  } else {
496    // Write to the properties array.
497    int offset = index * kPointerSize + FixedArray::kHeaderSize;
498    // Get the properties array
499    __ Ldr(scratch1,
500           FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
501    __ Str(prop_reg, FieldMemOperand(scratch1, offset));
502
503    if (!representation.IsSmi()) {
504      // Update the write barrier for the array address.
505      if (!representation.IsDouble()) {
506        __ Mov(storage_reg, value_reg);
507      }
508      __ RecordWriteField(scratch1, offset, storage_reg, receiver_reg,
509                          kLRHasNotBeenSaved, kDontSaveFPRegs,
510                          EMIT_REMEMBERED_SET, smi_check);
511    }
512  }
513
514  __ Bind(&exit);
515  // Return the value (register x0).
516  DCHECK(value_reg.is(x0));
517  __ Ret();
518}
519
520
521void NamedStoreHandlerCompiler::GenerateStoreField(LookupIterator* lookup,
522                                                   Register value_reg,
523                                                   Label* miss_label) {
524  DCHECK(lookup->representation().IsHeapObject());
525  __ JumpIfSmi(value_reg, miss_label);
526  HeapType::Iterator<Map> it = lookup->GetFieldType()->Classes();
527  __ Ldr(scratch1(), FieldMemOperand(value_reg, HeapObject::kMapOffset));
528  Label do_store;
529  while (true) {
530    __ CompareMap(scratch1(), it.Current());
531    it.Advance();
532    if (it.Done()) {
533      __ B(ne, miss_label);
534      break;
535    }
536    __ B(eq, &do_store);
537  }
538  __ Bind(&do_store);
539
540  StoreFieldStub stub(isolate(), lookup->GetFieldIndex(),
541                      lookup->representation());
542  GenerateTailCall(masm(), stub.GetCode());
543}
544
545
546Register PropertyHandlerCompiler::CheckPrototypes(
547    Register object_reg, Register holder_reg, Register scratch1,
548    Register scratch2, Handle<Name> name, Label* miss,
549    PrototypeCheckType check) {
550  Handle<Map> receiver_map(IC::TypeToMap(*type(), isolate()));
551
552  // object_reg and holder_reg registers can alias.
553  DCHECK(!AreAliased(object_reg, scratch1, scratch2));
554  DCHECK(!AreAliased(holder_reg, scratch1, scratch2));
555
556  // Keep track of the current object in register reg.
557  Register reg = object_reg;
558  int depth = 0;
559
560  Handle<JSObject> current = Handle<JSObject>::null();
561  if (type()->IsConstant()) {
562    current = Handle<JSObject>::cast(type()->AsConstant()->Value());
563  }
564  Handle<JSObject> prototype = Handle<JSObject>::null();
565  Handle<Map> current_map = receiver_map;
566  Handle<Map> holder_map(holder()->map());
567  // Traverse the prototype chain and check the maps in the prototype chain for
568  // fast and global objects or do negative lookup for normal objects.
569  while (!current_map.is_identical_to(holder_map)) {
570    ++depth;
571
572    // Only global objects and objects that do not require access
573    // checks are allowed in stubs.
574    DCHECK(current_map->IsJSGlobalProxyMap() ||
575           !current_map->is_access_check_needed());
576
577    prototype = handle(JSObject::cast(current_map->prototype()));
578    if (current_map->is_dictionary_map() &&
579        !current_map->IsJSGlobalObjectMap()) {
580      DCHECK(!current_map->IsJSGlobalProxyMap());  // Proxy maps are fast.
581      if (!name->IsUniqueName()) {
582        DCHECK(name->IsString());
583        name = factory()->InternalizeString(Handle<String>::cast(name));
584      }
585      DCHECK(current.is_null() || (current->property_dictionary()->FindEntry(
586                                       name) == NameDictionary::kNotFound));
587
588      GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
589                                       scratch2);
590
591      __ Ldr(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
592      reg = holder_reg;  // From now on the object will be in holder_reg.
593      __ Ldr(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
594    } else {
595      // Two possible reasons for loading the prototype from the map:
596      // (1) Can't store references to new space in code.
597      // (2) Handler is shared for all receivers with the same prototype
598      //     map (but not necessarily the same prototype instance).
599      bool load_prototype_from_map =
600          heap()->InNewSpace(*prototype) || depth == 1;
601      Register map_reg = scratch1;
602      __ Ldr(map_reg, FieldMemOperand(reg, HeapObject::kMapOffset));
603
604      if (depth != 1 || check == CHECK_ALL_MAPS) {
605        __ CheckMap(map_reg, current_map, miss, DONT_DO_SMI_CHECK);
606      }
607
608      // Check access rights to the global object.  This has to happen after
609      // the map check so that we know that the object is actually a global
610      // object.
611      // This allows us to install generated handlers for accesses to the
612      // global proxy (as opposed to using slow ICs). See corresponding code
613      // in LookupForRead().
614      if (current_map->IsJSGlobalProxyMap()) {
615        UseScratchRegisterScope temps(masm());
616        __ CheckAccessGlobalProxy(reg, scratch2, temps.AcquireX(), miss);
617      } else if (current_map->IsJSGlobalObjectMap()) {
618        GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
619                                  name, scratch2, miss);
620      }
621
622      reg = holder_reg;  // From now on the object will be in holder_reg.
623
624      if (load_prototype_from_map) {
625        __ Ldr(reg, FieldMemOperand(map_reg, Map::kPrototypeOffset));
626      } else {
627        __ Mov(reg, Operand(prototype));
628      }
629    }
630
631    // Go to the next object in the prototype chain.
632    current = prototype;
633    current_map = handle(current->map());
634  }
635
636  // Log the check depth.
637  LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
638
639  // Check the holder map.
640  if (depth != 0 || check == CHECK_ALL_MAPS) {
641    // Check the holder map.
642    __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK);
643  }
644
645  // Perform security check for access to the global object.
646  DCHECK(current_map->IsJSGlobalProxyMap() ||
647         !current_map->is_access_check_needed());
648  if (current_map->IsJSGlobalProxyMap()) {
649    __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss);
650  }
651
652  // Return the register containing the holder.
653  return reg;
654}
655
656
657void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
658  if (!miss->is_unused()) {
659    Label success;
660    __ B(&success);
661
662    __ Bind(miss);
663    TailCallBuiltin(masm(), MissBuiltin(kind()));
664
665    __ Bind(&success);
666  }
667}
668
669
670void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
671  if (!miss->is_unused()) {
672    Label success;
673    __ B(&success);
674
675    GenerateRestoreName(miss, name);
676    TailCallBuiltin(masm(), MissBuiltin(kind()));
677
678    __ Bind(&success);
679  }
680}
681
682
683void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) {
684  // Return the constant value.
685  __ LoadObject(x0, value);
686  __ Ret();
687}
688
689
690void NamedLoadHandlerCompiler::GenerateLoadCallback(
691    Register reg, Handle<ExecutableAccessorInfo> callback) {
692  DCHECK(!AreAliased(scratch2(), scratch3(), scratch4(), reg));
693
694  // Build ExecutableAccessorInfo::args_ list on the stack and push property
695  // name below the exit frame to make GC aware of them and store pointers to
696  // them.
697  STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0);
698  STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1);
699  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2);
700  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3);
701  STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4);
702  STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5);
703  STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6);
704
705  __ Push(receiver());
706
707  if (heap()->InNewSpace(callback->data())) {
708    __ Mov(scratch3(), Operand(callback));
709    __ Ldr(scratch3(),
710           FieldMemOperand(scratch3(), ExecutableAccessorInfo::kDataOffset));
711  } else {
712    __ Mov(scratch3(), Operand(Handle<Object>(callback->data(), isolate())));
713  }
714  __ LoadRoot(scratch4(), Heap::kUndefinedValueRootIndex);
715  __ Mov(scratch2(), Operand(ExternalReference::isolate_address(isolate())));
716  __ Push(scratch3(), scratch4(), scratch4(), scratch2(), reg, name());
717
718  Register args_addr = scratch2();
719  __ Add(args_addr, __ StackPointer(), kPointerSize);
720
721  // Stack at this point:
722  //              sp[40] callback data
723  //              sp[32] undefined
724  //              sp[24] undefined
725  //              sp[16] isolate
726  // args_addr -> sp[8]  reg
727  //              sp[0]  name
728
729  // Abi for CallApiGetter.
730  Register getter_address_reg = x2;
731
732  // Set up the call.
733  Address getter_address = v8::ToCData<Address>(callback->getter());
734  ApiFunction fun(getter_address);
735  ExternalReference::Type type = ExternalReference::DIRECT_GETTER_CALL;
736  ExternalReference ref = ExternalReference(&fun, type, isolate());
737  __ Mov(getter_address_reg, ref);
738
739  CallApiGetterStub stub(isolate());
740  __ TailCallStub(&stub);
741}
742
743
744void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
745    LookupIterator* it, Register holder_reg) {
746  DCHECK(!AreAliased(receiver(), this->name(), scratch1(), scratch2(),
747                     scratch3()));
748  DCHECK(holder()->HasNamedInterceptor());
749  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
750
751  // Compile the interceptor call, followed by inline code to load the
752  // property from further up the prototype chain if the call fails.
753  // Check that the maps haven't changed.
754  DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
755
756  // Preserve the receiver register explicitly whenever it is different from the
757  // holder and it is needed should the interceptor return without any result.
758  // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
759  // case might cause a miss during the prototype check.
760  bool must_perform_prototype_check =
761      !holder().is_identical_to(it->GetHolder<JSObject>());
762  bool must_preserve_receiver_reg =
763      !receiver().is(holder_reg) &&
764      (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
765
766  // Save necessary data before invoking an interceptor.
767  // Requires a frame to make GC aware of pushed pointers.
768  {
769    FrameScope frame_scope(masm(), StackFrame::INTERNAL);
770    if (must_preserve_receiver_reg) {
771      __ Push(receiver(), holder_reg, this->name());
772    } else {
773      __ Push(holder_reg, this->name());
774    }
775    // Invoke an interceptor.  Note: map checks from receiver to
776    // interceptor's holder has been compiled before (see a caller
777    // of this method.)
778    CompileCallLoadPropertyWithInterceptor(
779        masm(), receiver(), holder_reg, this->name(), holder(),
780        IC::kLoadPropertyWithInterceptorOnly);
781
782    // Check if interceptor provided a value for property.  If it's
783    // the case, return immediately.
784    Label interceptor_failed;
785    __ JumpIfRoot(x0, Heap::kNoInterceptorResultSentinelRootIndex,
786                  &interceptor_failed);
787    frame_scope.GenerateLeaveFrame();
788    __ Ret();
789
790    __ Bind(&interceptor_failed);
791    if (must_preserve_receiver_reg) {
792      __ Pop(this->name(), holder_reg, receiver());
793    } else {
794      __ Pop(this->name(), holder_reg);
795    }
796    // Leave the internal frame.
797  }
798
799  GenerateLoadPostInterceptor(it, holder_reg);
800}
801
802
803void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
804  // Call the runtime system to load the interceptor.
805  DCHECK(holder()->HasNamedInterceptor());
806  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
807  PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
808                           holder());
809
810  ExternalReference ref = ExternalReference(
811      IC_Utility(IC::kLoadPropertyWithInterceptor), isolate());
812  __ TailCallExternalReference(
813      ref, NamedLoadHandlerCompiler::kInterceptorArgsLength, 1);
814}
815
816
817Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
818    Handle<JSObject> object, Handle<Name> name,
819    Handle<ExecutableAccessorInfo> callback) {
820  ASM_LOCATION("NamedStoreHandlerCompiler::CompileStoreCallback");
821  Register holder_reg = Frontend(receiver(), name);
822
823  // Stub never generated for non-global objects that require access checks.
824  DCHECK(holder()->IsJSGlobalProxy() || !holder()->IsAccessCheckNeeded());
825
826  // receiver() and holder_reg can alias.
827  DCHECK(!AreAliased(receiver(), scratch1(), scratch2(), value()));
828  DCHECK(!AreAliased(holder_reg, scratch1(), scratch2(), value()));
829  __ Mov(scratch1(), Operand(callback));
830  __ Mov(scratch2(), Operand(name));
831  __ Push(receiver(), holder_reg, scratch1(), scratch2(), value());
832
833  // Do tail-call to the runtime system.
834  ExternalReference store_callback_property =
835      ExternalReference(IC_Utility(IC::kStoreCallbackProperty), isolate());
836  __ TailCallExternalReference(store_callback_property, 5, 1);
837
838  // Return the generated code.
839  return GetCode(kind(), Code::FAST, name);
840}
841
842
843#undef __
844}
845}  // namespace v8::internal
846
847#endif  // V8_TARGET_ARCH_IA32
848