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#if V8_TARGET_ARCH_ARM64
6
7#include "src/ic/handler-compiler.h"
8
9#include "src/api-arguments.h"
10#include "src/field-type.h"
11#include "src/ic/call-optimization.h"
12#include "src/ic/ic.h"
13#include "src/isolate-inl.h"
14
15namespace v8 {
16namespace internal {
17
18#define __ ACCESS_MASM(masm)
19
20void PropertyHandlerCompiler::PushVectorAndSlot(Register vector,
21                                                Register slot) {
22  MacroAssembler* masm = this->masm();
23  STATIC_ASSERT(LoadWithVectorDescriptor::kSlot <
24                LoadWithVectorDescriptor::kVector);
25  STATIC_ASSERT(StoreWithVectorDescriptor::kSlot <
26                StoreWithVectorDescriptor::kVector);
27  STATIC_ASSERT(StoreTransitionDescriptor::kSlot <
28                StoreTransitionDescriptor::kVector);
29  __ Push(slot);
30  __ Push(vector);
31}
32
33
34void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) {
35  MacroAssembler* masm = this->masm();
36  __ Pop(vector);
37  __ Pop(slot);
38}
39
40
41void PropertyHandlerCompiler::DiscardVectorAndSlot() {
42  MacroAssembler* masm = this->masm();
43  // Remove vector and slot.
44  __ Drop(2);
45}
46
47void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
48    MacroAssembler* masm, Label* miss_label, Register receiver,
49    Handle<Name> name, Register scratch0, Register scratch1) {
50  DCHECK(!AreAliased(receiver, scratch0, scratch1));
51  DCHECK(name->IsUniqueName());
52  Counters* counters = masm->isolate()->counters();
53  __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
54  __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
55
56  Label done;
57
58  const int kInterceptorOrAccessCheckNeededMask =
59      (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
60
61  // Bail out if the receiver has a named interceptor or requires access checks.
62  Register map = scratch1;
63  __ Ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
64  __ Ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
65  __ Tst(scratch0, kInterceptorOrAccessCheckNeededMask);
66  __ B(ne, miss_label);
67
68  // Check that receiver is a JSObject.
69  __ Ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
70  __ Cmp(scratch0, FIRST_JS_RECEIVER_TYPE);
71  __ B(lt, miss_label);
72
73  // Load properties array.
74  Register properties = scratch0;
75  __ Ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
76  // Check that the properties array is a dictionary.
77  __ Ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset));
78  __ JumpIfNotRoot(map, Heap::kHashTableMapRootIndex, miss_label);
79
80  NameDictionaryLookupStub::GenerateNegativeLookup(
81      masm, miss_label, &done, receiver, properties, name, scratch1);
82  __ Bind(&done);
83  __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
84}
85
86// Generate code to check that a global property cell is empty. Create
87// the property cell at compilation time if no cell exists for the
88// property.
89void PropertyHandlerCompiler::GenerateCheckPropertyCell(
90    MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
91    Register scratch, Label* miss) {
92  Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
93      global, name, PropertyCellType::kInvalidated);
94  Isolate* isolate = masm->isolate();
95  DCHECK(cell->value()->IsTheHole(isolate));
96  Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
97  __ LoadWeakValue(scratch, weak_cell, miss);
98  __ Ldr(scratch, FieldMemOperand(scratch, PropertyCell::kValueOffset));
99  __ JumpIfNotRoot(scratch, Heap::kTheHoleValueRootIndex, miss);
100}
101
102static void CompileCallLoadPropertyWithInterceptor(
103    MacroAssembler* masm, Register receiver, Register holder, Register name,
104    Handle<JSObject> holder_obj, Runtime::FunctionId id) {
105  DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
106         Runtime::FunctionForId(id)->nargs);
107
108  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
109  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
110  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
111  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
112  __ Push(name, receiver, holder);
113
114  __ CallRuntime(id);
115}
116
117
118// Generate call to api function.
119void PropertyHandlerCompiler::GenerateApiAccessorCall(
120    MacroAssembler* masm, const CallOptimization& optimization,
121    Handle<Map> receiver_map, Register receiver, Register scratch,
122    bool is_store, Register store_parameter, Register accessor_holder,
123    int accessor_index) {
124  DCHECK(!AreAliased(accessor_holder, scratch));
125  DCHECK(!AreAliased(receiver, scratch));
126
127  MacroAssembler::PushPopQueue queue(masm);
128  queue.Queue(receiver);
129  // Write the arguments to the stack frame.
130  if (is_store) {
131    DCHECK(!receiver.is(store_parameter));
132    DCHECK(!scratch.is(store_parameter));
133    queue.Queue(store_parameter);
134  }
135  queue.PushQueued();
136
137  DCHECK(optimization.is_simple_api_call());
138
139  // Abi for CallApiCallbackStub.
140  Register callee = x0;
141  Register data = x4;
142  Register holder = x2;
143  Register api_function_address = x1;
144
145  // Put callee in place.
146  __ LoadAccessor(callee, accessor_holder, accessor_index,
147                  is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER);
148
149  // Put holder in place.
150  CallOptimization::HolderLookup holder_lookup;
151  int holder_depth = 0;
152  optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup,
153                                          &holder_depth);
154  switch (holder_lookup) {
155    case CallOptimization::kHolderIsReceiver:
156      __ Mov(holder, receiver);
157      break;
158    case CallOptimization::kHolderFound:
159      __ Ldr(holder, FieldMemOperand(receiver, HeapObject::kMapOffset));
160      __ Ldr(holder, FieldMemOperand(holder, Map::kPrototypeOffset));
161      for (int i = 1; i < holder_depth; i++) {
162        __ Ldr(holder, FieldMemOperand(holder, HeapObject::kMapOffset));
163        __ Ldr(holder, FieldMemOperand(holder, Map::kPrototypeOffset));
164      }
165      break;
166    case CallOptimization::kHolderNotFound:
167      UNREACHABLE();
168      break;
169  }
170
171  Isolate* isolate = masm->isolate();
172  Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
173  bool call_data_undefined = false;
174  // Put call data in place.
175  if (api_call_info->data()->IsUndefined(isolate)) {
176    call_data_undefined = true;
177    __ LoadRoot(data, Heap::kUndefinedValueRootIndex);
178  } else {
179    if (optimization.is_constant_call()) {
180      __ Ldr(data,
181             FieldMemOperand(callee, JSFunction::kSharedFunctionInfoOffset));
182      __ Ldr(data,
183             FieldMemOperand(data, SharedFunctionInfo::kFunctionDataOffset));
184      __ Ldr(data,
185             FieldMemOperand(data, FunctionTemplateInfo::kCallCodeOffset));
186    } else {
187      __ Ldr(data,
188             FieldMemOperand(callee, FunctionTemplateInfo::kCallCodeOffset));
189    }
190    __ Ldr(data, FieldMemOperand(data, CallHandlerInfo::kDataOffset));
191  }
192
193  if (api_call_info->fast_handler()->IsCode()) {
194    // Just tail call into the fast handler if present.
195    __ Jump(handle(Code::cast(api_call_info->fast_handler())),
196            RelocInfo::CODE_TARGET);
197    return;
198  }
199
200  // Put api_function_address in place.
201  Address function_address = v8::ToCData<Address>(api_call_info->callback());
202  ApiFunction fun(function_address);
203  ExternalReference ref = ExternalReference(
204      &fun, ExternalReference::DIRECT_API_CALL, masm->isolate());
205  __ Mov(api_function_address, ref);
206
207  // Jump to stub.
208  CallApiCallbackStub stub(isolate, is_store, call_data_undefined,
209                           !optimization.is_constant_call());
210  __ TailCallStub(&stub);
211}
212
213
214void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
215    MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
216    int accessor_index, int expected_arguments, Register scratch) {
217  // ----------- S t a t e -------------
218  //  -- lr    : return address
219  // -----------------------------------
220  Label miss;
221  {
222    FrameScope scope(masm, StackFrame::INTERNAL);
223
224    // Save context register
225    __ Push(cp);
226    // Save value register, so we can restore it later.
227    __ Push(value());
228
229    if (accessor_index >= 0) {
230      DCHECK(!AreAliased(holder, scratch));
231      DCHECK(!AreAliased(receiver, scratch));
232      DCHECK(!AreAliased(value(), scratch));
233      // Call the JavaScript setter with receiver and value on the stack.
234      if (map->IsJSGlobalObjectMap()) {
235        // Swap in the global receiver.
236        __ Ldr(scratch,
237               FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
238        receiver = scratch;
239      }
240      __ Push(receiver, value());
241      __ LoadAccessor(x1, holder, accessor_index, ACCESSOR_SETTER);
242      __ Mov(x0, 1);
243      __ Call(masm->isolate()->builtins()->CallFunction(
244                  ConvertReceiverMode::kNotNullOrUndefined),
245              RelocInfo::CODE_TARGET);
246    } else {
247      // If we generate a global code snippet for deoptimization only, remember
248      // the place to continue after deoptimization.
249      masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
250    }
251
252    // We have to return the passed value, not the return value of the setter.
253    __ Pop(x0);
254
255    // Restore context register.
256    __ Pop(cp);
257  }
258  __ Ret();
259}
260
261
262void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
263    MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
264    int accessor_index, int expected_arguments, Register scratch) {
265  {
266    FrameScope scope(masm, StackFrame::INTERNAL);
267
268    // Save context register
269    __ Push(cp);
270
271    if (accessor_index >= 0) {
272      DCHECK(!AreAliased(holder, scratch));
273      DCHECK(!AreAliased(receiver, scratch));
274      // Call the JavaScript getter with the receiver on the stack.
275      if (map->IsJSGlobalObjectMap()) {
276        // Swap in the global receiver.
277        __ Ldr(scratch,
278               FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
279        receiver = scratch;
280      }
281      __ Push(receiver);
282      __ LoadAccessor(x1, holder, accessor_index, ACCESSOR_GETTER);
283      __ Mov(x0, 0);
284      __ Call(masm->isolate()->builtins()->CallFunction(
285                  ConvertReceiverMode::kNotNullOrUndefined),
286              RelocInfo::CODE_TARGET);
287    } else {
288      // If we generate a global code snippet for deoptimization only, remember
289      // the place to continue after deoptimization.
290      masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
291    }
292
293    // Restore context register.
294    __ Pop(cp);
295  }
296  __ Ret();
297}
298
299#undef __
300#define __ ACCESS_MASM(masm())
301
302
303Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
304    Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) {
305  Label miss;
306  if (IC::ICUseVector(kind())) {
307    PushVectorAndSlot();
308  }
309  FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
310
311  // Get the value from the cell.
312  Register result = StoreDescriptor::ValueRegister();
313  Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
314  __ LoadWeakValue(result, weak_cell, &miss);
315  __ Ldr(result, FieldMemOperand(result, PropertyCell::kValueOffset));
316
317  // Check for deleted property if property can actually be deleted.
318  if (is_configurable) {
319    __ JumpIfRoot(result, Heap::kTheHoleValueRootIndex, &miss);
320  }
321
322  Counters* counters = isolate()->counters();
323  __ IncrementCounter(counters->ic_named_load_global_stub(), 1, x1, x3);
324  if (IC::ICUseVector(kind())) {
325    DiscardVectorAndSlot();
326  }
327  __ Ret();
328
329  FrontendFooter(name, &miss);
330
331  // Return the generated code.
332  return GetCode(kind(), name);
333}
334
335
336Register NamedStoreHandlerCompiler::value() {
337  return StoreDescriptor::ValueRegister();
338}
339
340
341void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
342                                                    Handle<Name> name) {
343  if (!label->is_unused()) {
344    __ Bind(label);
345    __ Mov(this->name(), Operand(name));
346  }
347}
348
349void PropertyHandlerCompiler::GenerateAccessCheck(
350    Handle<WeakCell> native_context_cell, Register scratch1, Register scratch2,
351    Label* miss, bool compare_native_contexts_only) {
352  Label done;
353  // Load current native context.
354  __ Ldr(scratch1, NativeContextMemOperand());
355  // Load expected native context.
356  __ LoadWeakValue(scratch2, native_context_cell, miss);
357  __ Cmp(scratch1, scratch2);
358
359  if (!compare_native_contexts_only) {
360    __ B(eq, &done);
361
362    // Compare security tokens of current and expected native contexts.
363    __ Ldr(scratch1,
364           ContextMemOperand(scratch1, Context::SECURITY_TOKEN_INDEX));
365    __ Ldr(scratch2,
366           ContextMemOperand(scratch2, Context::SECURITY_TOKEN_INDEX));
367    __ Cmp(scratch1, scratch2);
368  }
369  __ B(ne, miss);
370
371  __ bind(&done);
372}
373
374Register PropertyHandlerCompiler::CheckPrototypes(
375    Register object_reg, Register holder_reg, Register scratch1,
376    Register scratch2, Handle<Name> name, Label* miss,
377    ReturnHolder return_what) {
378  Handle<Map> receiver_map = map();
379
380  // object_reg and holder_reg registers can alias.
381  DCHECK(!AreAliased(object_reg, scratch1, scratch2));
382  DCHECK(!AreAliased(holder_reg, scratch1, scratch2));
383
384  Handle<Cell> validity_cell =
385      Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
386  if (!validity_cell.is_null()) {
387    DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), validity_cell->value());
388    __ Mov(scratch1, Operand(validity_cell));
389    __ Ldr(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset));
390    // Compare scratch1 against Map::kPrototypeChainValid.
391    static_assert(Map::kPrototypeChainValid == 0,
392                  "Map::kPrototypeChainValid has unexpected value");
393    __ Cbnz(scratch1, miss);
394  }
395
396  // Keep track of the current object in register reg.
397  Register reg = object_reg;
398  int depth = 0;
399
400  Handle<JSObject> current = Handle<JSObject>::null();
401  if (receiver_map->IsJSGlobalObjectMap()) {
402    current = isolate()->global_object();
403  }
404
405  Handle<Map> current_map(receiver_map->GetPrototypeChainRootMap(isolate()),
406                          isolate());
407  Handle<Map> holder_map(holder()->map());
408  // Traverse the prototype chain and check the maps in the prototype chain for
409  // fast and global objects or do negative lookup for normal objects.
410  while (!current_map.is_identical_to(holder_map)) {
411    ++depth;
412
413    if (current_map->IsJSGlobalObjectMap()) {
414      GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
415                                name, scratch2, miss);
416    } else if (current_map->is_dictionary_map()) {
417      DCHECK(!current_map->IsJSGlobalProxyMap());  // Proxy maps are fast.
418      DCHECK(name->IsUniqueName());
419      DCHECK(current.is_null() || (current->property_dictionary()->FindEntry(
420                                       name) == NameDictionary::kNotFound));
421
422      if (depth > 1) {
423        Handle<WeakCell> weak_cell =
424            Map::GetOrCreatePrototypeWeakCell(current, isolate());
425        __ LoadWeakValue(reg, weak_cell, miss);
426      }
427      GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
428                                       scratch2);
429    }
430
431    reg = holder_reg;  // From now on the object will be in holder_reg.
432    // Go to the next object in the prototype chain.
433    current = handle(JSObject::cast(current_map->prototype()));
434    current_map = handle(current->map());
435  }
436
437  DCHECK(!current_map->IsJSGlobalProxyMap());
438
439  // Log the check depth.
440  LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
441
442  bool return_holder = return_what == RETURN_HOLDER;
443  if (return_holder && depth != 0) {
444    Handle<WeakCell> weak_cell =
445        Map::GetOrCreatePrototypeWeakCell(current, isolate());
446    __ LoadWeakValue(reg, weak_cell, miss);
447  }
448
449  // Return the register containing the holder.
450  return return_holder ? reg : no_reg;
451}
452
453
454void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
455  if (!miss->is_unused()) {
456    Label success;
457    __ B(&success);
458
459    __ Bind(miss);
460    if (IC::ICUseVector(kind())) {
461      DCHECK(kind() == Code::LOAD_IC);
462      PopVectorAndSlot();
463    }
464    TailCallBuiltin(masm(), MissBuiltin(kind()));
465
466    __ Bind(&success);
467  }
468}
469
470
471void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
472  if (!miss->is_unused()) {
473    Label success;
474    __ B(&success);
475
476    GenerateRestoreName(miss, name);
477    if (IC::ICUseVector(kind())) PopVectorAndSlot();
478    TailCallBuiltin(masm(), MissBuiltin(kind()));
479
480    __ Bind(&success);
481  }
482}
483
484void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
485    LookupIterator* it, Register holder_reg) {
486  DCHECK(!AreAliased(receiver(), this->name(), scratch1(), scratch2(),
487                     scratch3()));
488  DCHECK(holder()->HasNamedInterceptor());
489  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
490
491  // Compile the interceptor call, followed by inline code to load the
492  // property from further up the prototype chain if the call fails.
493  // Check that the maps haven't changed.
494  DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
495
496  // Preserve the receiver register explicitly whenever it is different from the
497  // holder and it is needed should the interceptor return without any result.
498  // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
499  // case might cause a miss during the prototype check.
500  bool must_perform_prototype_check =
501      !holder().is_identical_to(it->GetHolder<JSObject>());
502  bool must_preserve_receiver_reg =
503      !receiver().is(holder_reg) &&
504      (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
505
506  // Save necessary data before invoking an interceptor.
507  // Requires a frame to make GC aware of pushed pointers.
508  {
509    FrameScope frame_scope(masm(), StackFrame::INTERNAL);
510    if (must_preserve_receiver_reg) {
511      __ Push(receiver(), holder_reg, this->name());
512    } else {
513      __ Push(holder_reg, this->name());
514    }
515    InterceptorVectorSlotPush(holder_reg);
516    // Invoke an interceptor.  Note: map checks from receiver to
517    // interceptor's holder has been compiled before (see a caller
518    // of this method.)
519    CompileCallLoadPropertyWithInterceptor(
520        masm(), receiver(), holder_reg, this->name(), holder(),
521        Runtime::kLoadPropertyWithInterceptorOnly);
522
523    // Check if interceptor provided a value for property.  If it's
524    // the case, return immediately.
525    Label interceptor_failed;
526    __ JumpIfRoot(x0, Heap::kNoInterceptorResultSentinelRootIndex,
527                  &interceptor_failed);
528    frame_scope.GenerateLeaveFrame();
529    __ Ret();
530
531    __ Bind(&interceptor_failed);
532    InterceptorVectorSlotPop(holder_reg);
533    if (must_preserve_receiver_reg) {
534      __ Pop(this->name(), holder_reg, receiver());
535    } else {
536      __ Pop(this->name(), holder_reg);
537    }
538    // Leave the internal frame.
539  }
540
541  GenerateLoadPostInterceptor(it, holder_reg);
542}
543
544
545void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
546  // Call the runtime system to load the interceptor.
547  DCHECK(holder()->HasNamedInterceptor());
548  DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
549
550  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
551  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
552  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
553  STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
554  __ Push(name(), receiver(), holder_reg);
555  // See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details.
556  if (holder_reg.is(receiver())) {
557    __ Push(slot(), vector());
558  } else {
559    __ Push(scratch3(), scratch2());  // slot, vector
560  }
561
562  __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
563}
564
565void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
566  STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
567}
568
569Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
570    Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback,
571    LanguageMode language_mode) {
572  ASM_LOCATION("NamedStoreHandlerCompiler::CompileStoreCallback");
573  Register holder_reg = Frontend(name);
574
575  // Stub never generated for non-global objects that require access checks.
576  DCHECK(holder()->IsJSGlobalProxy() || !holder()->IsAccessCheckNeeded());
577
578  // receiver() and holder_reg can alias.
579  DCHECK(!AreAliased(receiver(), scratch1(), scratch2(), value()));
580  DCHECK(!AreAliased(holder_reg, scratch1(), scratch2(), value()));
581  // If the callback cannot leak, then push the callback directly,
582  // otherwise wrap it in a weak cell.
583  if (callback->data()->IsUndefined(isolate()) || callback->data()->IsSmi()) {
584    __ Mov(scratch1(), Operand(callback));
585  } else {
586    Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
587    __ Mov(scratch1(), Operand(cell));
588  }
589  __ Mov(scratch2(), Operand(name));
590  __ Push(receiver(), holder_reg, scratch1(), scratch2(), value());
591  __ Push(Smi::FromInt(language_mode));
592
593  // Do tail-call to the runtime system.
594  __ TailCallRuntime(Runtime::kStoreCallbackProperty);
595
596  // Return the generated code.
597  return GetCode(kind(), name);
598}
599
600
601#undef __
602}  // namespace internal
603}  // namespace v8
604
605#endif  // V8_TARGET_ARCH_IA32
606