handler-compiler-x87.cc revision 8389745919cae02139ddc085a63c00d024269cf2
1// Copyright 2012 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#if V8_TARGET_ARCH_X87
6
7#include "src/ic/handler-compiler.h"
8
9#include "src/field-type.h"
10#include "src/ic/call-optimization.h"
11#include "src/ic/ic.h"
12#include "src/isolate-inl.h"
13
14namespace v8 {
15namespace internal {
16
17#define __ ACCESS_MASM(masm)
18
19
20void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
21    MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
22    int accessor_index, int expected_arguments, Register scratch) {
23  {
24    FrameScope scope(masm, StackFrame::INTERNAL);
25
26    if (accessor_index >= 0) {
27      DCHECK(!holder.is(scratch));
28      DCHECK(!receiver.is(scratch));
29      // Call the JavaScript getter with the receiver on the stack.
30      if (map->IsJSGlobalObjectMap()) {
31        // Swap in the global receiver.
32        __ mov(scratch,
33               FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
34        receiver = scratch;
35      }
36      __ push(receiver);
37      ParameterCount actual(0);
38      ParameterCount expected(expected_arguments);
39      __ LoadAccessor(edi, holder, accessor_index, ACCESSOR_GETTER);
40      __ InvokeFunction(edi, expected, actual, CALL_FUNCTION,
41                        CheckDebugStepCallWrapper());
42    } else {
43      // If we generate a global code snippet for deoptimization only, remember
44      // the place to continue after deoptimization.
45      masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
46    }
47
48    // Restore context register.
49    __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
50  }
51  __ ret(0);
52}
53
54
55void PropertyHandlerCompiler::PushVectorAndSlot(Register vector,
56                                                Register slot) {
57  MacroAssembler* masm = this->masm();
58  __ push(vector);
59  __ push(slot);
60}
61
62
63void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) {
64  MacroAssembler* masm = this->masm();
65  __ pop(slot);
66  __ pop(vector);
67}
68
69
70void PropertyHandlerCompiler::DiscardVectorAndSlot() {
71  MacroAssembler* masm = this->masm();
72  // Remove vector and slot.
73  __ add(esp, Immediate(2 * kPointerSize));
74}
75
76
77void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
78    MacroAssembler* masm, Label* miss_label, Register receiver,
79    Handle<Name> name, Register scratch0, Register scratch1) {
80  DCHECK(name->IsUniqueName());
81  DCHECK(!receiver.is(scratch0));
82  Counters* counters = masm->isolate()->counters();
83  __ IncrementCounter(counters->negative_lookups(), 1);
84  __ IncrementCounter(counters->negative_lookups_miss(), 1);
85
86  __ mov(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
87
88  const int kInterceptorOrAccessCheckNeededMask =
89      (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
90
91  // Bail out if the receiver has a named interceptor or requires access checks.
92  __ test_b(FieldOperand(scratch0, Map::kBitFieldOffset),
93            kInterceptorOrAccessCheckNeededMask);
94  __ j(not_zero, miss_label);
95
96  // Check that receiver is a JSObject.
97  __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE);
98  __ j(below, miss_label);
99
100  // Load properties array.
101  Register properties = scratch0;
102  __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
103
104  // Check that the properties array is a dictionary.
105  __ cmp(FieldOperand(properties, HeapObject::kMapOffset),
106         Immediate(masm->isolate()->factory()->hash_table_map()));
107  __ j(not_equal, miss_label);
108
109  Label done;
110  NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done,
111                                                   properties, name, scratch1);
112  __ bind(&done);
113  __ DecrementCounter(counters->negative_lookups_miss(), 1);
114}
115
116
117void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype(
118    MacroAssembler* masm, int index, Register result, Label* miss) {
119  __ LoadGlobalFunction(index, result);
120  // Load its initial map. The global functions all have initial maps.
121  __ mov(result,
122         FieldOperand(result, JSFunction::kPrototypeOrInitialMapOffset));
123  // Load the prototype from the initial map.
124  __ mov(result, FieldOperand(result, Map::kPrototypeOffset));
125}
126
127
128void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype(
129    MacroAssembler* masm, Register receiver, Register scratch1,
130    Register scratch2, Label* miss_label) {
131  // TODO(mvstanton): This isn't used on ia32. Move all the other
132  // platform implementations into a code stub so this method can be removed.
133  UNREACHABLE();
134}
135
136
137// Generate call to api function.
138// This function uses push() to generate smaller, faster code than
139// the version above. It is an optimization that should will be removed
140// when api call ICs are generated in hydrogen.
141void PropertyHandlerCompiler::GenerateApiAccessorCall(
142    MacroAssembler* masm, const CallOptimization& optimization,
143    Handle<Map> receiver_map, Register receiver, Register scratch,
144    bool is_store, Register store_parameter, Register accessor_holder,
145    int accessor_index) {
146  DCHECK(!accessor_holder.is(scratch));
147  // Copy return value.
148  __ pop(scratch);
149  // receiver
150  __ push(receiver);
151  // Write the arguments to stack frame.
152  if (is_store) {
153    DCHECK(!receiver.is(store_parameter));
154    DCHECK(!scratch.is(store_parameter));
155    __ push(store_parameter);
156  }
157  __ push(scratch);
158  // Stack now matches JSFunction abi.
159  DCHECK(optimization.is_simple_api_call());
160
161  // Abi for CallApiFunctionStub.
162  Register callee = edi;
163  Register data = ebx;
164  Register holder = ecx;
165  Register api_function_address = edx;
166  scratch = no_reg;
167
168  // Put callee in place.
169  __ LoadAccessor(callee, accessor_holder, accessor_index,
170                  is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER);
171
172  // Put holder in place.
173  CallOptimization::HolderLookup holder_lookup;
174  int holder_depth = 0;
175  optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup,
176                                          &holder_depth);
177  switch (holder_lookup) {
178    case CallOptimization::kHolderIsReceiver:
179      __ Move(holder, receiver);
180      break;
181    case CallOptimization::kHolderFound:
182      __ mov(holder, FieldOperand(receiver, HeapObject::kMapOffset));
183      __ mov(holder, FieldOperand(holder, Map::kPrototypeOffset));
184      for (int i = 1; i < holder_depth; i++) {
185        __ mov(holder, FieldOperand(holder, HeapObject::kMapOffset));
186        __ mov(holder, FieldOperand(holder, Map::kPrototypeOffset));
187      }
188      break;
189    case CallOptimization::kHolderNotFound:
190      UNREACHABLE();
191      break;
192  }
193
194  Isolate* isolate = masm->isolate();
195  Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
196  bool call_data_undefined = false;
197  // Put call data in place.
198  if (api_call_info->data()->IsUndefined()) {
199    call_data_undefined = true;
200    __ mov(data, Immediate(isolate->factory()->undefined_value()));
201  } else {
202    if (optimization.is_constant_call()) {
203      __ mov(data, FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset));
204      __ mov(data, FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset));
205      __ mov(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset));
206    } else {
207      __ mov(data, FieldOperand(callee, FunctionTemplateInfo::kCallCodeOffset));
208    }
209    __ mov(data, FieldOperand(data, CallHandlerInfo::kDataOffset));
210  }
211
212  if (api_call_info->fast_handler()->IsCode()) {
213    // Just tail call into the code.
214    __ Jump(handle(Code::cast(api_call_info->fast_handler())),
215            RelocInfo::CODE_TARGET);
216    return;
217  }
218  // Put api_function_address in place.
219  Address function_address = v8::ToCData<Address>(api_call_info->callback());
220  __ mov(api_function_address, Immediate(function_address));
221
222  // Jump to stub.
223  CallApiAccessorStub stub(isolate, is_store, call_data_undefined,
224                           !optimization.is_constant_call());
225  __ TailCallStub(&stub);
226}
227
228
229// Generate code to check that a global property cell is empty. Create
230// the property cell at compilation time if no cell exists for the
231// property.
232void PropertyHandlerCompiler::GenerateCheckPropertyCell(
233    MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
234    Register scratch, Label* miss) {
235  Handle<PropertyCell> cell = JSGlobalObject::EnsurePropertyCell(global, name);
236  DCHECK(cell->value()->IsTheHole());
237  Factory* factory = masm->isolate()->factory();
238  Handle<WeakCell> weak_cell = factory->NewWeakCell(cell);
239  __ LoadWeakValue(scratch, weak_cell, miss);
240  __ cmp(FieldOperand(scratch, PropertyCell::kValueOffset),
241         Immediate(factory->the_hole_value()));
242  __ j(not_equal, miss);
243}
244
245
246void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
247    MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
248    int accessor_index, int expected_arguments, Register scratch) {
249  // ----------- S t a t e -------------
250  //  -- esp[0] : return address
251  // -----------------------------------
252  {
253    FrameScope scope(masm, StackFrame::INTERNAL);
254
255    // Save value register, so we can restore it later.
256    __ push(value());
257
258    if (accessor_index >= 0) {
259      DCHECK(!holder.is(scratch));
260      DCHECK(!receiver.is(scratch));
261      DCHECK(!value().is(scratch));
262      // Call the JavaScript setter with receiver and value on the stack.
263      if (map->IsJSGlobalObjectMap()) {
264        __ mov(scratch,
265               FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
266        receiver = scratch;
267      }
268      __ push(receiver);
269      __ push(value());
270      ParameterCount actual(1);
271      ParameterCount expected(expected_arguments);
272      __ LoadAccessor(edi, holder, accessor_index, ACCESSOR_SETTER);
273      __ InvokeFunction(edi, expected, actual, CALL_FUNCTION,
274                        CheckDebugStepCallWrapper());
275    } else {
276      // If we generate a global code snippet for deoptimization only, remember
277      // the place to continue after deoptimization.
278      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
279    }
280
281    // We have to return the passed value, not the return value of the setter.
282    __ pop(eax);
283
284    // Restore context register.
285    __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
286  }
287  __ ret(0);
288}
289
290
291static void PushInterceptorArguments(MacroAssembler* masm, Register receiver,
292                                     Register holder, Register name,
293                                     Handle<JSObject> holder_obj) {
294  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
295  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
296  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
297  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
298  __ push(name);
299  __ push(receiver);
300  __ push(holder);
301}
302
303
304static void CompileCallLoadPropertyWithInterceptor(
305    MacroAssembler* masm, Register receiver, Register holder, Register name,
306    Handle<JSObject> holder_obj, Runtime::FunctionId id) {
307  DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
308         Runtime::FunctionForId(id)->nargs);
309  PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
310  __ CallRuntime(id);
311}
312
313
314static void StoreIC_PushArgs(MacroAssembler* masm) {
315  Register receiver = StoreDescriptor::ReceiverRegister();
316  Register name = StoreDescriptor::NameRegister();
317  Register value = StoreDescriptor::ValueRegister();
318  Register slot = VectorStoreICDescriptor::SlotRegister();
319  Register vector = VectorStoreICDescriptor::VectorRegister();
320
321  __ xchg(receiver, Operand(esp, 0));
322  __ push(name);
323  __ push(value);
324  __ push(slot);
325  __ push(vector);
326  __ push(receiver);  // which contains the return address.
327}
328
329
330void NamedStoreHandlerCompiler::GenerateSlow(MacroAssembler* masm) {
331  // Return address is on the stack.
332  StoreIC_PushArgs(masm);
333
334  // Do tail-call to runtime routine.
335  __ TailCallRuntime(Runtime::kStoreIC_Slow);
336}
337
338
339void ElementHandlerCompiler::GenerateStoreSlow(MacroAssembler* masm) {
340  // Return address is on the stack.
341  StoreIC_PushArgs(masm);
342
343  // Do tail-call to runtime routine.
344  __ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
345}
346
347
348#undef __
349#define __ ACCESS_MASM(masm())
350
351
352void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
353                                                    Handle<Name> name) {
354  if (!label->is_unused()) {
355    __ bind(label);
356    __ mov(this->name(), Immediate(name));
357  }
358}
359
360
361void NamedStoreHandlerCompiler::GenerateRestoreName(Handle<Name> name) {
362  __ mov(this->name(), Immediate(name));
363}
364
365
366void NamedStoreHandlerCompiler::RearrangeVectorAndSlot(
367    Register current_map, Register destination_map) {
368  DCHECK(destination_map.is(StoreTransitionHelper::MapRegister()));
369  DCHECK(current_map.is(StoreTransitionHelper::VectorRegister()));
370  ExternalReference virtual_slot =
371      ExternalReference::virtual_slot_register(isolate());
372  __ mov(destination_map, current_map);
373  __ pop(current_map);
374  __ mov(Operand::StaticVariable(virtual_slot), current_map);
375  __ pop(current_map);  // put vector in place.
376}
377
378
379void NamedStoreHandlerCompiler::GenerateRestoreMap(Handle<Map> transition,
380                                                   Register map_reg,
381                                                   Register scratch,
382                                                   Label* miss) {
383  Handle<WeakCell> cell = Map::WeakCellForMap(transition);
384  DCHECK(!map_reg.is(scratch));
385  __ LoadWeakValue(map_reg, cell, miss);
386  if (transition->CanBeDeprecated()) {
387    __ mov(scratch, FieldOperand(map_reg, Map::kBitField3Offset));
388    __ and_(scratch, Immediate(Map::Deprecated::kMask));
389    __ j(not_zero, miss);
390  }
391}
392
393
394void NamedStoreHandlerCompiler::GenerateConstantCheck(Register map_reg,
395                                                      int descriptor,
396                                                      Register value_reg,
397                                                      Register scratch,
398                                                      Label* miss_label) {
399  DCHECK(!map_reg.is(scratch));
400  DCHECK(!map_reg.is(value_reg));
401  DCHECK(!value_reg.is(scratch));
402  __ LoadInstanceDescriptors(map_reg, scratch);
403  __ mov(scratch,
404         FieldOperand(scratch, DescriptorArray::GetValueOffset(descriptor)));
405  __ cmp(value_reg, scratch);
406  __ j(not_equal, miss_label);
407}
408
409void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(FieldType* field_type,
410                                                        Register value_reg,
411                                                        Label* miss_label) {
412  Register map_reg = scratch1();
413  Register scratch = scratch2();
414  DCHECK(!value_reg.is(map_reg));
415  DCHECK(!value_reg.is(scratch));
416  __ JumpIfSmi(value_reg, miss_label);
417  if (field_type->IsClass()) {
418    __ mov(map_reg, FieldOperand(value_reg, HeapObject::kMapOffset));
419    __ CmpWeakValue(map_reg, Map::WeakCellForMap(field_type->AsClass()),
420                    scratch);
421    __ j(not_equal, miss_label);
422  }
423}
424
425
426Register PropertyHandlerCompiler::CheckPrototypes(
427    Register object_reg, Register holder_reg, Register scratch1,
428    Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check,
429    ReturnHolder return_what) {
430  Handle<Map> receiver_map = map();
431
432  // Make sure there's no overlap between holder and object registers.
433  DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
434  DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
435         !scratch2.is(scratch1));
436
437  if (FLAG_eliminate_prototype_chain_checks) {
438    Handle<Cell> validity_cell =
439        Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
440    if (!validity_cell.is_null()) {
441      DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid),
442                validity_cell->value());
443      // Operand::ForCell(...) points to the cell's payload!
444      __ cmp(Operand::ForCell(validity_cell),
445             Immediate(Smi::FromInt(Map::kPrototypeChainValid)));
446      __ j(not_equal, miss);
447    }
448
449    // The prototype chain of primitives (and their JSValue wrappers) depends
450    // on the native context, which can't be guarded by validity cells.
451    // |object_reg| holds the native context specific prototype in this case;
452    // we need to check its map.
453    if (check == CHECK_ALL_MAPS) {
454      __ mov(scratch1, FieldOperand(object_reg, HeapObject::kMapOffset));
455      Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map);
456      __ CmpWeakValue(scratch1, cell, scratch2);
457      __ j(not_equal, miss);
458    }
459  }
460
461  // Keep track of the current object in register reg.
462  Register reg = object_reg;
463  int depth = 0;
464
465  Handle<JSObject> current = Handle<JSObject>::null();
466  if (receiver_map->IsJSGlobalObjectMap()) {
467    current = isolate()->global_object();
468  }
469
470  // Check access rights to the global object.  This has to happen after
471  // the map check so that we know that the object is actually a global
472  // object.
473  // This allows us to install generated handlers for accesses to the
474  // global proxy (as opposed to using slow ICs). See corresponding code
475  // in LookupForRead().
476  if (receiver_map->IsJSGlobalProxyMap()) {
477    __ CheckAccessGlobalProxy(reg, scratch1, scratch2, miss);
478  }
479
480  Handle<JSObject> prototype = Handle<JSObject>::null();
481  Handle<Map> current_map = receiver_map;
482  Handle<Map> holder_map(holder()->map());
483  // Traverse the prototype chain and check the maps in the prototype chain for
484  // fast and global objects or do negative lookup for normal objects.
485  while (!current_map.is_identical_to(holder_map)) {
486    ++depth;
487
488    // Only global objects and objects that do not require access
489    // checks are allowed in stubs.
490    DCHECK(current_map->IsJSGlobalProxyMap() ||
491           !current_map->is_access_check_needed());
492
493    prototype = handle(JSObject::cast(current_map->prototype()));
494    if (current_map->is_dictionary_map() &&
495        !current_map->IsJSGlobalObjectMap()) {
496      DCHECK(!current_map->IsJSGlobalProxyMap());  // Proxy maps are fast.
497      if (!name->IsUniqueName()) {
498        DCHECK(name->IsString());
499        name = factory()->InternalizeString(Handle<String>::cast(name));
500      }
501      DCHECK(current.is_null() ||
502             current->property_dictionary()->FindEntry(name) ==
503                 NameDictionary::kNotFound);
504
505      if (FLAG_eliminate_prototype_chain_checks && depth > 1) {
506        // TODO(jkummerow): Cache and re-use weak cell.
507        __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
508      }
509      GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
510                                       scratch2);
511
512      if (!FLAG_eliminate_prototype_chain_checks) {
513        __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
514        __ mov(holder_reg, FieldOperand(scratch1, Map::kPrototypeOffset));
515      }
516    } else {
517      Register map_reg = scratch1;
518      if (!FLAG_eliminate_prototype_chain_checks) {
519        __ mov(map_reg, FieldOperand(reg, HeapObject::kMapOffset));
520      }
521      if (current_map->IsJSGlobalObjectMap()) {
522        GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
523                                  name, scratch2, miss);
524      } else if (!FLAG_eliminate_prototype_chain_checks &&
525                 (depth != 1 || check == CHECK_ALL_MAPS)) {
526        Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
527        __ CmpWeakValue(map_reg, cell, scratch2);
528        __ j(not_equal, miss);
529      }
530      if (!FLAG_eliminate_prototype_chain_checks) {
531        __ mov(holder_reg, FieldOperand(map_reg, Map::kPrototypeOffset));
532      }
533    }
534
535    reg = holder_reg;  // From now on the object will be in holder_reg.
536    // Go to the next object in the prototype chain.
537    current = prototype;
538    current_map = handle(current->map());
539  }
540
541  DCHECK(!current_map->IsJSGlobalProxyMap());
542
543  // Log the check depth.
544  LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
545
546  if (!FLAG_eliminate_prototype_chain_checks &&
547      (depth != 0 || check == CHECK_ALL_MAPS)) {
548    // Check the holder map.
549    __ mov(scratch1, FieldOperand(reg, HeapObject::kMapOffset));
550    Handle<WeakCell> cell = Map::WeakCellForMap(current_map);
551    __ CmpWeakValue(scratch1, cell, scratch2);
552    __ j(not_equal, miss);
553  }
554
555  bool return_holder = return_what == RETURN_HOLDER;
556  if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) {
557    __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss);
558  }
559
560  // Return the register containing the holder.
561  return return_holder ? reg : no_reg;
562}
563
564
565void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
566  if (!miss->is_unused()) {
567    Label success;
568    __ jmp(&success);
569    __ bind(miss);
570    if (IC::ICUseVector(kind())) {
571      DCHECK(kind() == Code::LOAD_IC);
572      PopVectorAndSlot();
573    }
574    TailCallBuiltin(masm(), MissBuiltin(kind()));
575    __ bind(&success);
576  }
577}
578
579
580void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
581  if (!miss->is_unused()) {
582    Label success;
583    __ jmp(&success);
584    GenerateRestoreName(miss, name);
585    if (IC::ICUseVector(kind())) PopVectorAndSlot();
586    TailCallBuiltin(masm(), MissBuiltin(kind()));
587    __ bind(&success);
588  }
589}
590
591
592void NamedLoadHandlerCompiler::GenerateLoadCallback(
593    Register reg, Handle<AccessorInfo> callback) {
594  DCHECK(!AreAliased(scratch2(), scratch3(), receiver()));
595  DCHECK(!AreAliased(scratch2(), scratch3(), reg));
596
597  // Insert additional parameters into the stack frame above return address.
598  __ pop(scratch3());  // Get return address to place it below.
599
600  // Build v8::PropertyCallbackInfo::args_ array on the stack and push property
601  // name below the exit frame to make GC aware of them.
602  STATIC_ASSERT(PropertyCallbackArguments::kShouldThrowOnErrorIndex == 0);
603  STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 1);
604  STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 2);
605  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 3);
606  STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 4);
607  STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 5);
608  STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 6);
609  STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 7);
610
611  __ push(receiver());  // receiver
612  // Push data from AccessorInfo.
613  Handle<Object> data(callback->data(), isolate());
614  if (data->IsUndefined() || data->IsSmi()) {
615    __ push(Immediate(data));
616  } else {
617    Handle<WeakCell> cell =
618        isolate()->factory()->NewWeakCell(Handle<HeapObject>::cast(data));
619    // The callback is alive if this instruction is executed,
620    // so the weak cell is not cleared and points to data.
621    __ GetWeakValue(scratch2(), cell);
622    __ push(scratch2());
623  }
624  __ push(Immediate(isolate()->factory()->undefined_value()));  // ReturnValue
625  // ReturnValue default value
626  __ push(Immediate(isolate()->factory()->undefined_value()));
627  __ push(Immediate(reinterpret_cast<int>(isolate())));
628  __ push(reg);  // holder
629  __ push(Immediate(Smi::FromInt(0)));  // should_throw_on_error -> false
630
631  __ push(name());  // name
632  __ push(scratch3());  // Restore return address.
633
634  // Abi for CallApiGetter
635  Register getter_address = ApiGetterDescriptor::function_address();
636  Address function_address = v8::ToCData<Address>(callback->getter());
637  __ mov(getter_address, Immediate(function_address));
638
639  CallApiGetterStub stub(isolate());
640  __ TailCallStub(&stub);
641}
642
643
644void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) {
645  // Return the constant value.
646  __ LoadObject(eax, value);
647  __ ret(0);
648}
649
650
651void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
652    LookupIterator* it, Register holder_reg) {
653  DCHECK(holder()->HasNamedInterceptor());
654  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
655
656  // Compile the interceptor call, followed by inline code to load the
657  // property from further up the prototype chain if the call fails.
658  // Check that the maps haven't changed.
659  DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
660
661  // Preserve the receiver register explicitly whenever it is different from the
662  // holder and it is needed should the interceptor return without any result.
663  // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
664  // case might cause a miss during the prototype check.
665  bool must_perform_prototype_check =
666      !holder().is_identical_to(it->GetHolder<JSObject>());
667  bool must_preserve_receiver_reg =
668      !receiver().is(holder_reg) &&
669      (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
670
671  // Save necessary data before invoking an interceptor.
672  // Requires a frame to make GC aware of pushed pointers.
673  {
674    FrameScope frame_scope(masm(), StackFrame::INTERNAL);
675
676    if (must_preserve_receiver_reg) {
677      __ push(receiver());
678    }
679    __ push(holder_reg);
680    __ push(this->name());
681    InterceptorVectorSlotPush(holder_reg);
682    // Invoke an interceptor.  Note: map checks from receiver to
683    // interceptor's holder has been compiled before (see a caller
684    // of this method.)
685    CompileCallLoadPropertyWithInterceptor(
686        masm(), receiver(), holder_reg, this->name(), holder(),
687        Runtime::kLoadPropertyWithInterceptorOnly);
688
689    // Check if interceptor provided a value for property.  If it's
690    // the case, return immediately.
691    Label interceptor_failed;
692    __ cmp(eax, factory()->no_interceptor_result_sentinel());
693    __ j(equal, &interceptor_failed);
694    frame_scope.GenerateLeaveFrame();
695    __ ret(0);
696
697    // Clobber registers when generating debug-code to provoke errors.
698    __ bind(&interceptor_failed);
699    if (FLAG_debug_code) {
700      __ mov(receiver(), Immediate(bit_cast<int32_t>(kZapValue)));
701      __ mov(holder_reg, Immediate(bit_cast<int32_t>(kZapValue)));
702      __ mov(this->name(), Immediate(bit_cast<int32_t>(kZapValue)));
703    }
704
705    InterceptorVectorSlotPop(holder_reg);
706    __ pop(this->name());
707    __ pop(holder_reg);
708    if (must_preserve_receiver_reg) {
709      __ pop(receiver());
710    }
711
712    // Leave the internal frame.
713  }
714
715  GenerateLoadPostInterceptor(it, holder_reg);
716}
717
718
719void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
720  DCHECK(holder()->HasNamedInterceptor());
721  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined());
722  // Call the runtime system to load the interceptor.
723  __ pop(scratch2());  // save old return address
724  PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
725                           holder());
726  __ push(scratch2());  // restore old return address
727
728  __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
729}
730
731
732Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
733    Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback,
734    LanguageMode language_mode) {
735  Register holder_reg = Frontend(name);
736
737  __ pop(scratch1());  // remove the return address
738  __ push(receiver());
739  __ push(holder_reg);
740  // If the callback cannot leak, then push the callback directly,
741  // otherwise wrap it in a weak cell.
742  if (callback->data()->IsUndefined() || callback->data()->IsSmi()) {
743    __ Push(callback);
744  } else {
745    Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
746    __ Push(cell);
747  }
748  __ Push(name);
749  __ push(value());
750  __ push(Immediate(Smi::FromInt(language_mode)));
751  __ push(scratch1());  // restore return address
752
753  // Do tail-call to the runtime system.
754  __ TailCallRuntime(Runtime::kStoreCallbackProperty);
755
756  // Return the generated code.
757  return GetCode(kind(), Code::FAST, name);
758}
759
760
761Handle<Code> NamedStoreHandlerCompiler::CompileStoreInterceptor(
762    Handle<Name> name) {
763  __ pop(scratch1());  // remove the return address
764  __ push(receiver());
765  __ push(this->name());
766  __ push(value());
767  __ push(scratch1());  // restore return address
768
769  // Do tail-call to the runtime system.
770  __ TailCallRuntime(Runtime::kStorePropertyWithInterceptor);
771
772  // Return the generated code.
773  return GetCode(kind(), Code::FAST, name);
774}
775
776
777Register NamedStoreHandlerCompiler::value() {
778  return StoreDescriptor::ValueRegister();
779}
780
781
782Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
783    Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) {
784  Label miss;
785  if (IC::ICUseVector(kind())) {
786    PushVectorAndSlot();
787  }
788  FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
789  // Get the value from the cell.
790  Register result = StoreDescriptor::ValueRegister();
791  Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
792  __ LoadWeakValue(result, weak_cell, &miss);
793  __ mov(result, FieldOperand(result, PropertyCell::kValueOffset));
794
795  // Check for deleted property if property can actually be deleted.
796  if (is_configurable) {
797    __ cmp(result, factory()->the_hole_value());
798    __ j(equal, &miss);
799  } else if (FLAG_debug_code) {
800    __ cmp(result, factory()->the_hole_value());
801    __ Check(not_equal, kDontDeleteCellsCannotContainTheHole);
802  }
803
804  Counters* counters = isolate()->counters();
805  __ IncrementCounter(counters->ic_named_load_global_stub(), 1);
806  // The code above already loads the result into the return register.
807  if (IC::ICUseVector(kind())) {
808    DiscardVectorAndSlot();
809  }
810  __ ret(0);
811
812  FrontendFooter(name, &miss);
813
814  // Return the generated code.
815  return GetCode(kind(), Code::NORMAL, name);
816}
817
818
819#undef __
820}  // namespace internal
821}  // namespace v8
822
823#endif  // V8_TARGET_ARCH_X87
824