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