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.
7 #include "src/ic/call-optimization.h"
8 #include "src/ic/handler-compiler.h"
10 #include "src/ic/ic-inl.h"
16 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name,
17 Handle<Map> stub_holder,
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();
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>();
38 Handle<Map> stub_holder_map =
39 IC::GetHandlerCacheHolder(receiver_map, false, isolate, &flag);
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()
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()));
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());
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;
64 NamedLoadHandlerCompiler compiler(isolate, receiver_map, last, flag);
65 handler = compiler.CompileLoadNonexistent(cache_name);
66 Map::UpdateCodeCache(stub_holder_map, cache_name, handler);
71 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
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));
78 code->VerifyEmbeddedObjects();
84 #define __ ACCESS_MASM(masm())
87 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
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;
101 check_type = SKIP_RECEIVER;
104 if (check_type == CHECK_ALL_MAPS) {
105 GenerateDirectLoadGlobalFunctionPrototype(masm(), function_index,
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());
111 object_reg = scratch1();
114 // Check that the maps starting from the prototype haven't changed.
115 return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
120 // Frontend for store uses the name register. It has to be restored before a
122 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
125 return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
126 miss, SKIP_RECEIVER);
130 Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
132 if (IC::ICUseVector(kind())) {
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();
145 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
150 Handle<Map> last_map;
151 if (holder().is_null()) {
152 holder_reg = receiver();
154 // If |type| has null as its prototype, |holder()| is
155 // Handle<JSObject>::null().
156 DCHECK(last_map->prototype() == isolate()->heap()->null_value());
158 holder_reg = FrontendHeader(receiver(), name, miss);
159 last_map = handle(holder()->map());
162 if (last_map->is_dictionary_map()) {
163 if (last_map->IsJSGlobalObjectMap()) {
164 Handle<JSGlobalObject> global =
166 ? Handle<JSGlobalObject>::cast(isolate()->global_object())
167 : Handle<JSGlobalObject>::cast(holder());
168 GenerateCheckPropertyCell(masm(), global, name, scratch1, miss);
170 if (!name->IsUniqueName()) {
171 DCHECK(name->IsString());
172 name = factory()->InternalizeString(Handle<String>::cast(name));
174 DCHECK(holder().is_null() ||
175 holder()->property_dictionary()->FindEntry(name) ==
176 NameDictionary::kNotFound);
177 GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1,
184 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name,
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);
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);
204 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent(
207 if (IC::ICUseVector(kind())) {
208 DCHECK(kind() == Code::LOAD_IC);
211 NonexistentFrontendHeader(name, &miss, scratch2(), scratch3());
212 if (IC::ICUseVector(kind())) {
213 DiscardVectorAndSlot();
215 GenerateLoadConstant(isolate()->factory()->undefined_value());
216 FrontendFooter(name, &miss);
217 return GetCode(kind(), Code::FAST, name);
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);
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);
240 void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
241 if (IC::ICUseVector(kind())) {
242 if (holder_reg.is(receiver())) {
245 DCHECK(holder_reg.is(scratch1()));
246 PushVectorAndSlot(scratch2(), scratch3());
252 void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
254 if (IC::ICUseVector(kind())) {
255 if (mode == DISCARD) {
256 DiscardVectorAndSlot();
258 if (holder_reg.is(receiver())) {
261 DCHECK(holder_reg.is(scratch1()));
262 PopVectorAndSlot(scratch2(), scratch3());
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
274 bool inline_followup = false;
275 switch (it->state()) {
276 case LookupIterator::TRANSITION:
278 case LookupIterator::ACCESS_CHECK:
279 case LookupIterator::INTERCEPTOR:
280 case LookupIterator::JSPROXY:
281 case LookupIterator::NOT_FOUND:
283 case LookupIterator::DATA:
285 it->property_details().type() == DATA && !it->is_dictionary_holder();
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(),
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);
312 InterceptorVectorSlotPush(receiver());
313 Register reg = FrontendHeader(receiver(), it->name(), &miss);
314 FrontendFooter(it->name(), &miss);
315 InterceptorVectorSlotPop(reg);
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);
322 GenerateLoadInterceptor(reg);
324 return GetCode(kind(), Code::FAST, it->name());
328 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
329 LookupIterator* it, Register interceptor_reg) {
330 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
332 Handle<Map> holder_map(holder()->map());
334 set_holder(real_named_property_holder);
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);
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:
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());
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);
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());
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);
384 // TODO(verwaest): Cleanup. holder() is actually the receiver.
385 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
386 Handle<Map> transition, Handle<Name> name) {
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));
399 if (!last.is_null()) set_holder(last);
400 NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
402 FrontendHeader(receiver(), name, &miss);
403 DCHECK(holder()->HasFastProperties());
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());
412 // Stub is never generated for objects that require access checks.
413 DCHECK(!transition->is_access_check_needed());
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());
426 if (representation.IsHeapObject()) {
427 GenerateFieldTypeChecks(descriptors->GetFieldType(descriptor), value(),
430 StoreTransitionStub::StoreMode store_mode =
431 Map::cast(transition->GetBackPointer())->unused_property_fields() == 0
432 ? StoreTransitionStub::ExtendStorageAndStoreMapAndValue
433 : StoreTransitionStub::StoreMapAndValue;
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());
443 GenerateRestoreName(&miss, name);
444 TailCallBuiltin(masm(), MissBuiltin(kind()));
446 return GetCode(kind(), Code::FAST, name);
450 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) {
452 DCHECK(it->representation().IsHeapObject());
454 GenerateFieldTypeChecks(*it->GetFieldType(), value(), &miss);
455 StoreFieldStub stub(isolate(), it->GetFieldIndex(), it->representation());
456 GenerateTailCall(masm(), stub.GetCode());
459 TailCallBuiltin(masm(), MissBuiltin(kind()));
460 return GetCode(kind(), Code::FAST, it->name());
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());
471 return GetCode(kind(), Code::FAST, name);
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,
482 return GetCode(kind(), Code::FAST, name);
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;
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();
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)
512 DCHECK(elements_kind == DICTIONARY_ELEMENTS);
513 cached_stub = LoadDictionaryElementStub(isolate()).GetCode();
517 handlers->Add(cached_stub);
521 } // namespace v8::internal