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_X64
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
15 namespace v8 {
16 namespace internal {
17
18 #define __ ACCESS_MASM(masm)
19
PushVectorAndSlot(Register vector,Register slot)20 void 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
PopVectorAndSlot(Register vector,Register slot)34 void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) {
35 MacroAssembler* masm = this->masm();
36 __ Pop(vector);
37 __ Pop(slot);
38 }
39
40
DiscardVectorAndSlot()41 void PropertyHandlerCompiler::DiscardVectorAndSlot() {
42 MacroAssembler* masm = this->masm();
43 // Remove vector and slot.
44 __ addp(rsp, Immediate(2 * kPointerSize));
45 }
46
PushReturnAddress(Register tmp)47 void PropertyHandlerCompiler::PushReturnAddress(Register tmp) {
48 MacroAssembler* masm = this->masm();
49 __ Push(tmp);
50 }
51
PopReturnAddress(Register tmp)52 void PropertyHandlerCompiler::PopReturnAddress(Register tmp) {
53 MacroAssembler* masm = this->masm();
54 __ Pop(tmp);
55 }
56
GenerateDictionaryNegativeLookup(MacroAssembler * masm,Label * miss_label,Register receiver,Handle<Name> name,Register scratch0,Register scratch1)57 void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup(
58 MacroAssembler* masm, Label* miss_label, Register receiver,
59 Handle<Name> name, Register scratch0, Register scratch1) {
60 DCHECK(name->IsUniqueName());
61 DCHECK(!receiver.is(scratch0));
62 Counters* counters = masm->isolate()->counters();
63 __ IncrementCounter(counters->negative_lookups(), 1);
64 __ IncrementCounter(counters->negative_lookups_miss(), 1);
65
66 __ movp(scratch0, FieldOperand(receiver, HeapObject::kMapOffset));
67
68 const int kInterceptorOrAccessCheckNeededMask =
69 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
70
71 // Bail out if the receiver has a named interceptor or requires access checks.
72 __ testb(FieldOperand(scratch0, Map::kBitFieldOffset),
73 Immediate(kInterceptorOrAccessCheckNeededMask));
74 __ j(not_zero, miss_label);
75
76 // Check that receiver is a JSObject.
77 __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE);
78 __ j(below, miss_label);
79
80 // Load properties array.
81 Register properties = scratch0;
82 __ movp(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
83
84 // Check that the properties array is a dictionary.
85 __ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset),
86 Heap::kHashTableMapRootIndex);
87 __ j(not_equal, miss_label);
88
89 Label done;
90 NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done,
91 properties, name, scratch1);
92 __ bind(&done);
93 __ DecrementCounter(counters->negative_lookups_miss(), 1);
94 }
95
96
GenerateDirectLoadGlobalFunctionPrototype(MacroAssembler * masm,int index,Register result,Label * miss)97 void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype(
98 MacroAssembler* masm, int index, Register result, Label* miss) {
99 __ LoadNativeContextSlot(index, result);
100 // Load its initial map. The global functions all have initial maps.
101 __ movp(result,
102 FieldOperand(result, JSFunction::kPrototypeOrInitialMapOffset));
103 // Load the prototype from the initial map.
104 __ movp(result, FieldOperand(result, Map::kPrototypeOffset));
105 }
106
107
GenerateLoadFunctionPrototype(MacroAssembler * masm,Register receiver,Register result,Register scratch,Label * miss_label)108 void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype(
109 MacroAssembler* masm, Register receiver, Register result, Register scratch,
110 Label* miss_label) {
111 __ TryGetFunctionPrototype(receiver, result, miss_label);
112 if (!result.is(rax)) __ movp(rax, result);
113 __ ret(0);
114 }
115
116
PushInterceptorArguments(MacroAssembler * masm,Register receiver,Register holder,Register name,Handle<JSObject> holder_obj)117 static void PushInterceptorArguments(MacroAssembler* masm, Register receiver,
118 Register holder, Register name,
119 Handle<JSObject> holder_obj) {
120 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0);
121 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1);
122 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2);
123 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3);
124 __ Push(name);
125 __ Push(receiver);
126 __ Push(holder);
127 }
128
129
CompileCallLoadPropertyWithInterceptor(MacroAssembler * masm,Register receiver,Register holder,Register name,Handle<JSObject> holder_obj,Runtime::FunctionId id)130 static void CompileCallLoadPropertyWithInterceptor(
131 MacroAssembler* masm, Register receiver, Register holder, Register name,
132 Handle<JSObject> holder_obj, Runtime::FunctionId id) {
133 DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength ==
134 Runtime::FunctionForId(id)->nargs);
135 PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
136 __ CallRuntime(id);
137 }
138
139
140 // Generate call to api function.
GenerateApiAccessorCall(MacroAssembler * masm,const CallOptimization & optimization,Handle<Map> receiver_map,Register receiver,Register scratch,bool is_store,Register store_parameter,Register accessor_holder,int accessor_index)141 void 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 DCHECK(optimization.is_simple_api_call());
148
149 __ PopReturnAddressTo(scratch);
150 // receiver
151 __ Push(receiver);
152 // Write the arguments to stack frame.
153 if (is_store) {
154 DCHECK(!receiver.is(store_parameter));
155 DCHECK(!scratch.is(store_parameter));
156 __ Push(store_parameter);
157 }
158 __ PushReturnAddressFrom(scratch);
159 // Stack now matches JSFunction abi.
160
161 // Abi for CallApiCallbackStub.
162 Register callee = rdi;
163 Register data = rbx;
164 Register holder = rcx;
165 Register api_function_address = rdx;
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 __ movp(holder, FieldOperand(receiver, HeapObject::kMapOffset));
183 __ movp(holder, FieldOperand(holder, Map::kPrototypeOffset));
184 for (int i = 1; i < holder_depth; i++) {
185 __ movp(holder, FieldOperand(holder, HeapObject::kMapOffset));
186 __ movp(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(isolate)) {
199 call_data_undefined = true;
200 __ LoadRoot(data, Heap::kUndefinedValueRootIndex);
201 } else {
202 if (optimization.is_constant_call()) {
203 __ movp(data,
204 FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset));
205 __ movp(data,
206 FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset));
207 __ movp(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset));
208 } else {
209 __ movp(data,
210 FieldOperand(callee, FunctionTemplateInfo::kCallCodeOffset));
211 }
212 __ movp(data, FieldOperand(data, CallHandlerInfo::kDataOffset));
213 }
214
215 if (api_call_info->fast_handler()->IsCode()) {
216 // Just tail call into the fast handler if present.
217 __ Jump(handle(Code::cast(api_call_info->fast_handler())),
218 RelocInfo::CODE_TARGET);
219 return;
220 }
221
222 // Put api_function_address in place.
223 Address function_address = v8::ToCData<Address>(api_call_info->callback());
224 __ Move(api_function_address, function_address,
225 RelocInfo::EXTERNAL_REFERENCE);
226
227 // Jump to stub.
228 CallApiCallbackStub stub(isolate, is_store, call_data_undefined,
229 !optimization.is_constant_call());
230 __ TailCallStub(&stub);
231 }
232
233
GenerateCheckPropertyCell(MacroAssembler * masm,Handle<JSGlobalObject> global,Handle<Name> name,Register scratch,Label * miss)234 void PropertyHandlerCompiler::GenerateCheckPropertyCell(
235 MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name,
236 Register scratch, Label* miss) {
237 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
238 global, name, PropertyCellType::kInvalidated);
239 Isolate* isolate = masm->isolate();
240 DCHECK(cell->value()->IsTheHole(isolate));
241 Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
242 __ LoadWeakValue(scratch, weak_cell, miss);
243 __ Cmp(FieldOperand(scratch, PropertyCell::kValueOffset),
244 isolate->factory()->the_hole_value());
245 __ j(not_equal, miss);
246 }
247
248
GenerateStoreViaSetter(MacroAssembler * masm,Handle<Map> map,Register receiver,Register holder,int accessor_index,int expected_arguments,Register scratch)249 void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
250 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
251 int accessor_index, int expected_arguments, Register scratch) {
252 // ----------- S t a t e -------------
253 // -- rsp[0] : return address
254 // -----------------------------------
255 {
256 FrameScope scope(masm, StackFrame::INTERNAL);
257
258 // Save context register
259 __ pushq(rsi);
260 // Save value register, so we can restore it later.
261 __ Push(value());
262
263 if (accessor_index >= 0) {
264 DCHECK(!holder.is(scratch));
265 DCHECK(!receiver.is(scratch));
266 DCHECK(!value().is(scratch));
267 // Call the JavaScript setter with receiver and value on the stack.
268 if (map->IsJSGlobalObjectMap()) {
269 // Swap in the global receiver.
270 __ movp(scratch,
271 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
272 receiver = scratch;
273 }
274 __ Push(receiver);
275 __ Push(value());
276 __ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_SETTER);
277 __ Set(rax, 1);
278 __ Call(masm->isolate()->builtins()->CallFunction(
279 ConvertReceiverMode::kNotNullOrUndefined),
280 RelocInfo::CODE_TARGET);
281 } else {
282 // If we generate a global code snippet for deoptimization only, remember
283 // the place to continue after deoptimization.
284 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
285 }
286
287 // We have to return the passed value, not the return value of the setter.
288 __ Pop(rax);
289
290 // Restore context register.
291 __ popq(rsi);
292 }
293 __ ret(0);
294 }
295
296
GenerateLoadViaGetter(MacroAssembler * masm,Handle<Map> map,Register receiver,Register holder,int accessor_index,int expected_arguments,Register scratch)297 void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
298 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder,
299 int accessor_index, int expected_arguments, Register scratch) {
300 // ----------- S t a t e -------------
301 // -- rax : receiver
302 // -- rcx : name
303 // -- rsp[0] : return address
304 // -----------------------------------
305 {
306 FrameScope scope(masm, StackFrame::INTERNAL);
307
308 // Save context register
309 __ pushq(rsi);
310
311 if (accessor_index >= 0) {
312 DCHECK(!holder.is(scratch));
313 DCHECK(!receiver.is(scratch));
314 // Call the JavaScript getter with the receiver on the stack.
315 if (map->IsJSGlobalObjectMap()) {
316 // Swap in the global receiver.
317 __ movp(scratch,
318 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset));
319 receiver = scratch;
320 }
321 __ Push(receiver);
322 __ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_GETTER);
323 __ Set(rax, 0);
324 __ Call(masm->isolate()->builtins()->CallFunction(
325 ConvertReceiverMode::kNotNullOrUndefined),
326 RelocInfo::CODE_TARGET);
327 } else {
328 // If we generate a global code snippet for deoptimization only, remember
329 // the place to continue after deoptimization.
330 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
331 }
332
333 // Restore context register.
334 __ popq(rsi);
335 }
336 __ ret(0);
337 }
338
339 #undef __
340 #define __ ACCESS_MASM((masm()))
341
342
GenerateRestoreName(Label * label,Handle<Name> name)343 void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label,
344 Handle<Name> name) {
345 if (!label->is_unused()) {
346 __ bind(label);
347 __ Move(this->name(), name);
348 }
349 }
350
351
GenerateRestoreName(Handle<Name> name)352 void NamedStoreHandlerCompiler::GenerateRestoreName(Handle<Name> name) {
353 __ Move(this->name(), name);
354 }
355
356
GenerateRestoreMap(Handle<Map> transition,Register map_reg,Register scratch,Label * miss)357 void NamedStoreHandlerCompiler::GenerateRestoreMap(Handle<Map> transition,
358 Register map_reg,
359 Register scratch,
360 Label* miss) {
361 Handle<WeakCell> cell = Map::WeakCellForMap(transition);
362 DCHECK(!map_reg.is(scratch));
363 __ LoadWeakValue(map_reg, cell, miss);
364 if (transition->CanBeDeprecated()) {
365 __ movl(scratch, FieldOperand(map_reg, Map::kBitField3Offset));
366 __ andl(scratch, Immediate(Map::Deprecated::kMask));
367 __ j(not_zero, miss);
368 }
369 }
370
371
GenerateConstantCheck(Register map_reg,int descriptor,Register value_reg,Register scratch,Label * miss_label)372 void NamedStoreHandlerCompiler::GenerateConstantCheck(Register map_reg,
373 int descriptor,
374 Register value_reg,
375 Register scratch,
376 Label* miss_label) {
377 DCHECK(!map_reg.is(scratch));
378 DCHECK(!map_reg.is(value_reg));
379 DCHECK(!value_reg.is(scratch));
380 __ LoadInstanceDescriptors(map_reg, scratch);
381 __ movp(scratch,
382 FieldOperand(scratch, DescriptorArray::GetValueOffset(descriptor)));
383 __ cmpp(value_reg, scratch);
384 __ j(not_equal, miss_label);
385 }
386
GenerateFieldTypeChecks(FieldType * field_type,Register value_reg,Label * miss_label)387 void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(FieldType* field_type,
388 Register value_reg,
389 Label* miss_label) {
390 Register map_reg = scratch1();
391 Register scratch = scratch2();
392 DCHECK(!value_reg.is(map_reg));
393 DCHECK(!value_reg.is(scratch));
394 __ JumpIfSmi(value_reg, miss_label);
395 if (field_type->IsClass()) {
396 Label do_store;
397 __ movp(map_reg, FieldOperand(value_reg, HeapObject::kMapOffset));
398 __ CmpWeakValue(map_reg, Map::WeakCellForMap(field_type->AsClass()),
399 scratch);
400 __ j(not_equal, miss_label);
401 }
402 }
403
GenerateAccessCheck(Handle<WeakCell> native_context_cell,Register scratch1,Register scratch2,Label * miss,bool compare_native_contexts_only)404 void PropertyHandlerCompiler::GenerateAccessCheck(
405 Handle<WeakCell> native_context_cell, Register scratch1, Register scratch2,
406 Label* miss, bool compare_native_contexts_only) {
407 Label done;
408 // Load current native context.
409 __ movp(scratch1, NativeContextOperand());
410 // Load expected native context.
411 __ LoadWeakValue(scratch2, native_context_cell, miss);
412 __ cmpp(scratch1, scratch2);
413
414 if (!compare_native_contexts_only) {
415 __ j(equal, &done);
416
417 // Compare security tokens of current and expected native contexts.
418 __ movp(scratch1, ContextOperand(scratch1, Context::SECURITY_TOKEN_INDEX));
419 __ movp(scratch2, ContextOperand(scratch2, Context::SECURITY_TOKEN_INDEX));
420 __ cmpp(scratch1, scratch2);
421 }
422 __ j(not_equal, miss);
423
424 __ bind(&done);
425 }
426
CheckPrototypes(Register object_reg,Register holder_reg,Register scratch1,Register scratch2,Handle<Name> name,Label * miss,ReturnHolder return_what)427 Register PropertyHandlerCompiler::CheckPrototypes(
428 Register object_reg, Register holder_reg, Register scratch1,
429 Register scratch2, Handle<Name> name, Label* miss,
430 ReturnHolder return_what) {
431 Handle<Map> receiver_map = map();
432
433 // Make sure there's no overlap between holder and object registers.
434 DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
435 DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) &&
436 !scratch2.is(scratch1));
437
438 Handle<Cell> validity_cell =
439 Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
440 if (!validity_cell.is_null()) {
441 DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), validity_cell->value());
442 __ Move(scratch1, validity_cell, RelocInfo::CELL);
443 // Move(..., CELL) loads the payload's address!
444 __ SmiCompare(Operand(scratch1, 0),
445 Smi::FromInt(Map::kPrototypeChainValid));
446 __ j(not_equal, miss);
447 }
448
449 // Keep track of the current object in register reg. On the first
450 // iteration, reg is an alias for object_reg, on later iterations,
451 // it is an alias for holder_reg.
452 Register reg = object_reg;
453 int depth = 0;
454
455 Handle<JSObject> current = Handle<JSObject>::null();
456 if (receiver_map->IsJSGlobalObjectMap()) {
457 current = isolate()->global_object();
458 }
459
460 Handle<Map> current_map(receiver_map->GetPrototypeChainRootMap(isolate()),
461 isolate());
462 Handle<Map> holder_map(holder()->map());
463 // Traverse the prototype chain and check the maps in the prototype chain for
464 // fast and global objects or do negative lookup for normal objects.
465 while (!current_map.is_identical_to(holder_map)) {
466 ++depth;
467
468 if (current_map->IsJSGlobalObjectMap()) {
469 GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current),
470 name, scratch2, miss);
471 } else if (current_map->is_dictionary_map()) {
472 DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast.
473 DCHECK(name->IsUniqueName());
474 DCHECK(current.is_null() ||
475 current->property_dictionary()->FindEntry(name) ==
476 NameDictionary::kNotFound);
477
478 if (depth > 1) {
479 Handle<WeakCell> weak_cell =
480 Map::GetOrCreatePrototypeWeakCell(current, isolate());
481 __ LoadWeakValue(reg, weak_cell, miss);
482 }
483 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1,
484 scratch2);
485 }
486
487 reg = holder_reg; // From now on the object will be in holder_reg.
488 // Go to the next object in the prototype chain.
489 current = handle(JSObject::cast(current_map->prototype()));
490 current_map = handle(current->map());
491 }
492
493 DCHECK(!current_map->IsJSGlobalProxyMap());
494
495 // Log the check depth.
496 LOG(isolate(), IntEvent("check-maps-depth", depth + 1));
497
498 bool return_holder = return_what == RETURN_HOLDER;
499 if (return_holder && depth != 0) {
500 Handle<WeakCell> weak_cell =
501 Map::GetOrCreatePrototypeWeakCell(current, isolate());
502 __ LoadWeakValue(reg, weak_cell, miss);
503 }
504
505 // Return the register containing the holder.
506 return return_holder ? reg : no_reg;
507 }
508
509
FrontendFooter(Handle<Name> name,Label * miss)510 void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
511 if (!miss->is_unused()) {
512 Label success;
513 __ jmp(&success);
514 __ bind(miss);
515 if (IC::ICUseVector(kind())) {
516 DCHECK(kind() == Code::LOAD_IC);
517 PopVectorAndSlot();
518 }
519 TailCallBuiltin(masm(), MissBuiltin(kind()));
520 __ bind(&success);
521 }
522 }
523
524
FrontendFooter(Handle<Name> name,Label * miss)525 void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) {
526 if (!miss->is_unused()) {
527 Label success;
528 __ jmp(&success);
529 GenerateRestoreName(miss, name);
530 if (IC::ICUseVector(kind())) PopVectorAndSlot();
531 TailCallBuiltin(masm(), MissBuiltin(kind()));
532 __ bind(&success);
533 }
534 }
535
GenerateLoadConstant(Handle<Object> value)536 void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) {
537 // Return the constant value.
538 __ Move(rax, value);
539 __ ret(0);
540 }
541
542
GenerateLoadInterceptorWithFollowup(LookupIterator * it,Register holder_reg)543 void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup(
544 LookupIterator* it, Register holder_reg) {
545 DCHECK(holder()->HasNamedInterceptor());
546 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
547
548 // Compile the interceptor call, followed by inline code to load the
549 // property from further up the prototype chain if the call fails.
550 // Check that the maps haven't changed.
551 DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1()));
552
553 // Preserve the receiver register explicitly whenever it is different from the
554 // holder and it is needed should the interceptor return without any result.
555 // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD
556 // case might cause a miss during the prototype check.
557 bool must_perform_prototype_check =
558 !holder().is_identical_to(it->GetHolder<JSObject>());
559 bool must_preserve_receiver_reg =
560 !receiver().is(holder_reg) &&
561 (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check);
562
563 // Save necessary data before invoking an interceptor.
564 // Requires a frame to make GC aware of pushed pointers.
565 {
566 FrameScope frame_scope(masm(), StackFrame::INTERNAL);
567
568 if (must_preserve_receiver_reg) {
569 __ Push(receiver());
570 }
571 __ Push(holder_reg);
572 __ Push(this->name());
573 InterceptorVectorSlotPush(holder_reg);
574
575 // Invoke an interceptor. Note: map checks from receiver to
576 // interceptor's holder has been compiled before (see a caller
577 // of this method.)
578 CompileCallLoadPropertyWithInterceptor(
579 masm(), receiver(), holder_reg, this->name(), holder(),
580 Runtime::kLoadPropertyWithInterceptorOnly);
581
582 // Check if interceptor provided a value for property. If it's
583 // the case, return immediately.
584 Label interceptor_failed;
585 __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
586 __ j(equal, &interceptor_failed);
587 frame_scope.GenerateLeaveFrame();
588 __ ret(0);
589
590 __ bind(&interceptor_failed);
591 InterceptorVectorSlotPop(holder_reg);
592 __ Pop(this->name());
593 __ Pop(holder_reg);
594 if (must_preserve_receiver_reg) {
595 __ Pop(receiver());
596 }
597
598 // Leave the internal frame.
599 }
600
601 GenerateLoadPostInterceptor(it, holder_reg);
602 }
603
604
GenerateLoadInterceptor(Register holder_reg)605 void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) {
606 // Call the runtime system to load the interceptor.
607 DCHECK(holder()->HasNamedInterceptor());
608 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate()));
609 __ PopReturnAddressTo(scratch2());
610 PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(),
611 holder());
612 __ PushReturnAddressFrom(scratch2());
613
614 __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor);
615 }
616
ZapStackArgumentsRegisterAliases()617 void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() {
618 STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack);
619 }
620
CompileStoreCallback(Handle<JSObject> object,Handle<Name> name,Handle<AccessorInfo> callback,LanguageMode language_mode)621 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
622 Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback,
623 LanguageMode language_mode) {
624 Register holder_reg = Frontend(name);
625
626 __ PopReturnAddressTo(scratch1());
627 __ Push(receiver());
628 __ Push(holder_reg);
629 // If the callback cannot leak, then push the callback directly,
630 // otherwise wrap it in a weak cell.
631 if (callback->data()->IsUndefined(isolate()) || callback->data()->IsSmi()) {
632 __ Push(callback);
633 } else {
634 Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
635 __ Push(cell);
636 }
637 __ Push(name);
638 __ Push(value());
639 __ Push(Smi::FromInt(language_mode));
640 __ PushReturnAddressFrom(scratch1());
641
642 // Do tail-call to the runtime system.
643 __ TailCallRuntime(Runtime::kStoreCallbackProperty);
644
645 // Return the generated code.
646 return GetCode(kind(), name);
647 }
648
649
value()650 Register NamedStoreHandlerCompiler::value() {
651 return StoreDescriptor::ValueRegister();
652 }
653
654
CompileLoadGlobal(Handle<PropertyCell> cell,Handle<Name> name,bool is_configurable)655 Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal(
656 Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) {
657 Label miss;
658 if (IC::ICUseVector(kind())) {
659 PushVectorAndSlot();
660 }
661 FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
662
663 // Get the value from the cell.
664 Register result = StoreDescriptor::ValueRegister();
665 Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell);
666 __ LoadWeakValue(result, weak_cell, &miss);
667 __ movp(result, FieldOperand(result, PropertyCell::kValueOffset));
668
669 // Check for deleted property if property can actually be deleted.
670 if (is_configurable) {
671 __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
672 __ j(equal, &miss);
673 } else if (FLAG_debug_code) {
674 __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
675 __ Check(not_equal, kDontDeleteCellsCannotContainTheHole);
676 }
677
678 Counters* counters = isolate()->counters();
679 __ IncrementCounter(counters->ic_named_load_global_stub(), 1);
680 if (IC::ICUseVector(kind())) {
681 DiscardVectorAndSlot();
682 }
683 __ ret(0);
684
685 FrontendFooter(name, &miss);
686
687 // Return the generated code.
688 return GetCode(kind(), name);
689 }
690
691
692 #undef __
693 } // namespace internal
694 } // namespace v8
695
696 #endif // V8_TARGET_ARCH_X64
697