acf380fbee93f8c162e2463b76141d1d17e40fd3
[platform/upstream/nodejs.git] / deps / v8 / src / ic / handler-compiler.cc
1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/v8.h"
6
7 #include "src/ic/call-optimization.h"
8 #include "src/ic/handler-compiler.h"
9 #include "src/ic/ic.h"
10 #include "src/ic/ic-inl.h"
11
12 namespace v8 {
13 namespace internal {
14
15
16 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name,
17                                            Handle<Map> stub_holder,
18                                            Code::Kind kind,
19                                            CacheHolderFlag cache_holder,
20                                            Code::StubType type) {
21   Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder);
22   Object* probe = stub_holder->FindInCodeCache(*name, flags);
23   if (probe->IsCode()) return handle(Code::cast(probe));
24   return Handle<Code>::null();
25 }
26
27
28 Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent(
29     Handle<Name> name, Handle<Map> receiver_map) {
30   Isolate* isolate = name->GetIsolate();
31   if (receiver_map->prototype()->IsNull()) {
32     // TODO(jkummerow/verwaest): If there is no prototype and the property
33     // is nonexistent, introduce a builtin to handle this (fast properties
34     // -> return undefined, dictionary properties -> do negative lookup).
35     return Handle<Code>();
36   }
37   CacheHolderFlag flag;
38   Handle<Map> stub_holder_map =
39       IC::GetHandlerCacheHolder(receiver_map, false, isolate, &flag);
40
41   // If no dictionary mode objects are present in the prototype chain, the load
42   // nonexistent IC stub can be shared for all names for a given map and we use
43   // the empty string for the map cache in that case. If there are dictionary
44   // mode objects involved, we need to do negative lookups in the stub and
45   // therefore the stub will be specific to the name.
46   Handle<Name> cache_name =
47       receiver_map->is_dictionary_map()
48           ? name
49           : Handle<Name>::cast(isolate->factory()->nonexistent_symbol());
50   Handle<Map> current_map = stub_holder_map;
51   Handle<JSObject> last(JSObject::cast(receiver_map->prototype()));
52   while (true) {
53     if (current_map->is_dictionary_map()) cache_name = name;
54     if (current_map->prototype()->IsNull()) break;
55     last = handle(JSObject::cast(current_map->prototype()));
56     current_map = handle(last->map());
57   }
58   // Compile the stub that is either shared for all names or
59   // name specific if there are global objects involved.
60   Handle<Code> handler = PropertyHandlerCompiler::Find(
61       cache_name, stub_holder_map, Code::LOAD_IC, flag, Code::FAST);
62   if (!handler.is_null()) return handler;
63
64   NamedLoadHandlerCompiler compiler(isolate, receiver_map, last, flag);
65   handler = compiler.CompileLoadNonexistent(cache_name);
66   Map::UpdateCodeCache(stub_holder_map, cache_name, handler);
67   return handler;
68 }
69
70
71 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
72                                               Code::StubType type,
73                                               Handle<Name> name) {
74   Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder());
75   Handle<Code> code = GetCodeWithFlags(flags, name);
76   PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, *name));
77 #ifdef DEBUG
78   code->VerifyEmbeddedObjects();
79 #endif
80   return code;
81 }
82
83
84 #define __ ACCESS_MASM(masm())
85
86
87 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
88                                                   Handle<Name> name,
89                                                   Label* miss) {
90   PrototypeCheckType check_type = CHECK_ALL_MAPS;
91   int function_index = -1;
92   if (map()->instance_type() < FIRST_NONSTRING_TYPE) {
93     function_index = Context::STRING_FUNCTION_INDEX;
94   } else if (map()->instance_type() == SYMBOL_TYPE) {
95     function_index = Context::SYMBOL_FUNCTION_INDEX;
96   } else if (map()->instance_type() == HEAP_NUMBER_TYPE) {
97     function_index = Context::NUMBER_FUNCTION_INDEX;
98   } else if (*map() == isolate()->heap()->boolean_map()) {
99     function_index = Context::BOOLEAN_FUNCTION_INDEX;
100   } else {
101     check_type = SKIP_RECEIVER;
102   }
103
104   if (check_type == CHECK_ALL_MAPS) {
105     GenerateDirectLoadGlobalFunctionPrototype(masm(), function_index,
106                                               scratch1(), miss);
107     Object* function = isolate()->native_context()->get(function_index);
108     Object* prototype = JSFunction::cast(function)->instance_prototype();
109     Handle<Map> map(JSObject::cast(prototype)->map());
110     set_map(map);
111     object_reg = scratch1();
112   }
113
114   // Check that the maps starting from the prototype haven't changed.
115   return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
116                          miss, check_type);
117 }
118
119
120 // Frontend for store uses the name register. It has to be restored before a
121 // miss.
122 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
123                                                    Handle<Name> name,
124                                                    Label* miss) {
125   return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
126                          miss, SKIP_RECEIVER);
127 }
128
129
130 Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
131   Label miss;
132   if (IC::ICUseVector(kind())) {
133     PushVectorAndSlot();
134   }
135   Register reg = FrontendHeader(receiver(), name, &miss);
136   FrontendFooter(name, &miss);
137   // The footer consumes the vector and slot from the stack if miss occurs.
138   if (IC::ICUseVector(kind())) {
139     DiscardVectorAndSlot();
140   }
141   return reg;
142 }
143
144
145 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
146                                                         Label* miss,
147                                                         Register scratch1,
148                                                         Register scratch2) {
149   Register holder_reg;
150   Handle<Map> last_map;
151   if (holder().is_null()) {
152     holder_reg = receiver();
153     last_map = map();
154     // If |type| has null as its prototype, |holder()| is
155     // Handle<JSObject>::null().
156     DCHECK(last_map->prototype() == isolate()->heap()->null_value());
157   } else {
158     holder_reg = FrontendHeader(receiver(), name, miss);
159     last_map = handle(holder()->map());
160   }
161
162   if (last_map->is_dictionary_map()) {
163     if (last_map->IsJSGlobalObjectMap()) {
164       Handle<JSGlobalObject> global =
165           holder().is_null()
166               ? Handle<JSGlobalObject>::cast(isolate()->global_object())
167               : Handle<JSGlobalObject>::cast(holder());
168       GenerateCheckPropertyCell(masm(), global, name, scratch1, miss);
169     } else {
170       if (!name->IsUniqueName()) {
171         DCHECK(name->IsString());
172         name = factory()->InternalizeString(Handle<String>::cast(name));
173       }
174       DCHECK(holder().is_null() ||
175              holder()->property_dictionary()->FindEntry(name) ==
176                  NameDictionary::kNotFound);
177       GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1,
178                                        scratch2);
179     }
180   }
181 }
182
183
184 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name,
185                                                         FieldIndex field) {
186   Register reg = Frontend(name);
187   __ Move(receiver(), reg);
188   LoadFieldStub stub(isolate(), field);
189   GenerateTailCall(masm(), stub.GetCode());
190   return GetCode(kind(), Code::FAST, name);
191 }
192
193
194 Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name,
195                                                            int constant_index) {
196   Register reg = Frontend(name);
197   __ Move(receiver(), reg);
198   LoadConstantStub stub(isolate(), constant_index);
199   GenerateTailCall(masm(), stub.GetCode());
200   return GetCode(kind(), Code::FAST, name);
201 }
202
203
204 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent(
205     Handle<Name> name) {
206   Label miss;
207   if (IC::ICUseVector(kind())) {
208     DCHECK(kind() == Code::LOAD_IC);
209     PushVectorAndSlot();
210   }
211   NonexistentFrontendHeader(name, &miss, scratch2(), scratch3());
212   if (IC::ICUseVector(kind())) {
213     DiscardVectorAndSlot();
214   }
215   GenerateLoadConstant(isolate()->factory()->undefined_value());
216   FrontendFooter(name, &miss);
217   return GetCode(kind(), Code::FAST, name);
218 }
219
220
221 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
222     Handle<Name> name, Handle<ExecutableAccessorInfo> callback) {
223   Register reg = Frontend(name);
224   GenerateLoadCallback(reg, callback);
225   return GetCode(kind(), Code::FAST, name);
226 }
227
228
229 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
230     Handle<Name> name, const CallOptimization& call_optimization,
231     int accessor_index) {
232   DCHECK(call_optimization.is_simple_api_call());
233   Register holder = Frontend(name);
234   GenerateApiAccessorCall(masm(), call_optimization, map(), receiver(),
235                           scratch2(), false, no_reg, holder, accessor_index);
236   return GetCode(kind(), Code::FAST, name);
237 }
238
239
240 void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
241   if (IC::ICUseVector(kind())) {
242     if (holder_reg.is(receiver())) {
243       PushVectorAndSlot();
244     } else {
245       DCHECK(holder_reg.is(scratch1()));
246       PushVectorAndSlot(scratch2(), scratch3());
247     }
248   }
249 }
250
251
252 void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
253                                                         PopMode mode) {
254   if (IC::ICUseVector(kind())) {
255     if (mode == DISCARD) {
256       DiscardVectorAndSlot();
257     } else {
258       if (holder_reg.is(receiver())) {
259         PopVectorAndSlot();
260       } else {
261         DCHECK(holder_reg.is(scratch1()));
262         PopVectorAndSlot(scratch2(), scratch3());
263       }
264     }
265   }
266 }
267
268
269 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
270     LookupIterator* it) {
271   // So far the most popular follow ups for interceptor loads are DATA and
272   // ExecutableAccessorInfo, so inline only them. Other cases may be added
273   // later.
274   bool inline_followup = false;
275   switch (it->state()) {
276     case LookupIterator::TRANSITION:
277       UNREACHABLE();
278     case LookupIterator::ACCESS_CHECK:
279     case LookupIterator::INTERCEPTOR:
280     case LookupIterator::JSPROXY:
281     case LookupIterator::NOT_FOUND:
282       break;
283     case LookupIterator::DATA:
284       inline_followup =
285           it->property_details().type() == DATA && !it->is_dictionary_holder();
286       break;
287     case LookupIterator::ACCESSOR: {
288       Handle<Object> accessors = it->GetAccessors();
289       if (accessors->IsExecutableAccessorInfo()) {
290         Handle<ExecutableAccessorInfo> info =
291             Handle<ExecutableAccessorInfo>::cast(accessors);
292         inline_followup = info->getter() != NULL &&
293                           ExecutableAccessorInfo::IsCompatibleReceiverMap(
294                               isolate(), info, map());
295       } else if (accessors->IsAccessorPair()) {
296         Handle<JSObject> property_holder(it->GetHolder<JSObject>());
297         Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
298                               isolate());
299         if (!getter->IsJSFunction()) break;
300         if (!property_holder->HasFastProperties()) break;
301         auto function = Handle<JSFunction>::cast(getter);
302         CallOptimization call_optimization(function);
303         Handle<Map> receiver_map = map();
304         inline_followup = call_optimization.is_simple_api_call() &&
305                           call_optimization.IsCompatibleReceiverMap(
306                               receiver_map, property_holder);
307       }
308     }
309   }
310
311   Label miss;
312   InterceptorVectorSlotPush(receiver());
313   Register reg = FrontendHeader(receiver(), it->name(), &miss);
314   FrontendFooter(it->name(), &miss);
315   InterceptorVectorSlotPop(reg);
316
317   if (inline_followup) {
318     // TODO(368): Compile in the whole chain: all the interceptors in
319     // prototypes and ultimate answer.
320     GenerateLoadInterceptorWithFollowup(it, reg);
321   } else {
322     GenerateLoadInterceptor(reg);
323   }
324   return GetCode(kind(), Code::FAST, it->name());
325 }
326
327
328 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
329     LookupIterator* it, Register interceptor_reg) {
330   Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
331
332   Handle<Map> holder_map(holder()->map());
333   set_map(holder_map);
334   set_holder(real_named_property_holder);
335
336   Label miss;
337   InterceptorVectorSlotPush(interceptor_reg);
338   Register reg = FrontendHeader(interceptor_reg, it->name(), &miss);
339   FrontendFooter(it->name(), &miss);
340   // We discard the vector and slot now because we don't miss below this point.
341   InterceptorVectorSlotPop(reg, DISCARD);
342
343   switch (it->state()) {
344     case LookupIterator::ACCESS_CHECK:
345     case LookupIterator::INTERCEPTOR:
346     case LookupIterator::JSPROXY:
347     case LookupIterator::NOT_FOUND:
348     case LookupIterator::TRANSITION:
349       UNREACHABLE();
350     case LookupIterator::DATA: {
351       DCHECK_EQ(DATA, it->property_details().type());
352       __ Move(receiver(), reg);
353       LoadFieldStub stub(isolate(), it->GetFieldIndex());
354       GenerateTailCall(masm(), stub.GetCode());
355       break;
356     }
357     case LookupIterator::ACCESSOR:
358       if (it->GetAccessors()->IsExecutableAccessorInfo()) {
359         Handle<ExecutableAccessorInfo> info =
360             Handle<ExecutableAccessorInfo>::cast(it->GetAccessors());
361         DCHECK_NOT_NULL(info->getter());
362         GenerateLoadCallback(reg, info);
363       } else {
364         auto function = handle(JSFunction::cast(
365             AccessorPair::cast(*it->GetAccessors())->getter()));
366         CallOptimization call_optimization(function);
367         GenerateApiAccessorCall(masm(), call_optimization, holder_map,
368                                 receiver(), scratch2(), false, no_reg, reg,
369                                 it->GetAccessorIndex());
370       }
371   }
372 }
373
374
375 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
376     Handle<Name> name, int accessor_index, int expected_arguments) {
377   Register holder = Frontend(name);
378   GenerateLoadViaGetter(masm(), map(), receiver(), holder, accessor_index,
379                         expected_arguments, scratch2());
380   return GetCode(kind(), Code::FAST, name);
381 }
382
383
384 // TODO(verwaest): Cleanup. holder() is actually the receiver.
385 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
386     Handle<Map> transition, Handle<Name> name) {
387   Label miss;
388
389   // Check that we are allowed to write this.
390   bool is_nonexistent = holder()->map() == transition->GetBackPointer();
391   if (is_nonexistent) {
392     // Find the top object.
393     Handle<JSObject> last;
394     PrototypeIterator iter(isolate(), holder());
395     while (!iter.IsAtEnd()) {
396       last = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
397       iter.Advance();
398     }
399     if (!last.is_null()) set_holder(last);
400     NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
401   } else {
402     FrontendHeader(receiver(), name, &miss);
403     DCHECK(holder()->HasFastProperties());
404   }
405
406   int descriptor = transition->LastAdded();
407   Handle<DescriptorArray> descriptors(transition->instance_descriptors());
408   PropertyDetails details = descriptors->GetDetails(descriptor);
409   Representation representation = details.representation();
410   DCHECK(!representation.IsNone());
411
412   // Stub is never generated for objects that require access checks.
413   DCHECK(!transition->is_access_check_needed());
414
415   // Call to respective StoreTransitionStub.
416   if (details.type() == DATA_CONSTANT) {
417     GenerateRestoreMap(transition, scratch2(), &miss);
418     DCHECK(descriptors->GetValue(descriptor)->IsJSFunction());
419     Register map_reg = StoreTransitionDescriptor::MapRegister();
420     GenerateConstantCheck(map_reg, descriptor, value(), scratch2(), &miss);
421     GenerateRestoreName(name);
422     StoreTransitionStub stub(isolate());
423     GenerateTailCall(masm(), stub.GetCode());
424
425   } else {
426     if (representation.IsHeapObject()) {
427       GenerateFieldTypeChecks(descriptors->GetFieldType(descriptor), value(),
428                               &miss);
429     }
430     StoreTransitionStub::StoreMode store_mode =
431         Map::cast(transition->GetBackPointer())->unused_property_fields() == 0
432             ? StoreTransitionStub::ExtendStorageAndStoreMapAndValue
433             : StoreTransitionStub::StoreMapAndValue;
434
435     GenerateRestoreMap(transition, scratch2(), &miss);
436     GenerateRestoreName(name);
437     StoreTransitionStub stub(isolate(),
438                              FieldIndex::ForDescriptor(*transition, descriptor),
439                              representation, store_mode);
440     GenerateTailCall(masm(), stub.GetCode());
441   }
442
443   GenerateRestoreName(&miss, name);
444   TailCallBuiltin(masm(), MissBuiltin(kind()));
445
446   return GetCode(kind(), Code::FAST, name);
447 }
448
449
450 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) {
451   Label miss;
452   DCHECK(it->representation().IsHeapObject());
453
454   GenerateFieldTypeChecks(*it->GetFieldType(), value(), &miss);
455   StoreFieldStub stub(isolate(), it->GetFieldIndex(), it->representation());
456   GenerateTailCall(masm(), stub.GetCode());
457
458   __ bind(&miss);
459   TailCallBuiltin(masm(), MissBuiltin(kind()));
460   return GetCode(kind(), Code::FAST, it->name());
461 }
462
463
464 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
465     Handle<JSObject> object, Handle<Name> name, int accessor_index,
466     int expected_arguments) {
467   Register holder = Frontend(name);
468   GenerateStoreViaSetter(masm(), map(), receiver(), holder, accessor_index,
469                          expected_arguments, scratch2());
470
471   return GetCode(kind(), Code::FAST, name);
472 }
473
474
475 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
476     Handle<JSObject> object, Handle<Name> name,
477     const CallOptimization& call_optimization, int accessor_index) {
478   Register holder = Frontend(name);
479   GenerateApiAccessorCall(masm(), call_optimization, handle(object->map()),
480                           receiver(), scratch2(), true, value(), holder,
481                           accessor_index);
482   return GetCode(kind(), Code::FAST, name);
483 }
484
485
486 #undef __
487
488
489 void ElementHandlerCompiler::CompileElementHandlers(
490     MapHandleList* receiver_maps, CodeHandleList* handlers) {
491   for (int i = 0; i < receiver_maps->length(); ++i) {
492     Handle<Map> receiver_map = receiver_maps->at(i);
493     Handle<Code> cached_stub;
494
495     if (receiver_map->IsStringMap()) {
496       cached_stub = LoadIndexedStringStub(isolate()).GetCode();
497     } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) {
498       cached_stub = isolate()->builtins()->KeyedLoadIC_Slow();
499     } else {
500       bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
501       ElementsKind elements_kind = receiver_map->elements_kind();
502       if (receiver_map->has_indexed_interceptor()) {
503         cached_stub = LoadIndexedInterceptorStub(isolate()).GetCode();
504       } else if (IsSloppyArgumentsElements(elements_kind)) {
505         cached_stub = KeyedLoadSloppyArgumentsStub(isolate()).GetCode();
506       } else if (IsFastElementsKind(elements_kind) ||
507                  IsExternalArrayElementsKind(elements_kind) ||
508                  IsFixedTypedArrayElementsKind(elements_kind)) {
509         cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind)
510                           .GetCode();
511       } else {
512         DCHECK(elements_kind == DICTIONARY_ELEMENTS);
513         cached_stub = LoadDictionaryElementStub(isolate()).GetCode();
514       }
515     }
516
517     handlers->Add(cached_stub);
518   }
519 }
520 }
521 }  // namespace v8::internal