blob: ae977c391533a9c18efd88b1cd1201bfcb4b9da8 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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
12namespace v8 {
13namespace internal {
14
15
16Handle<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
28Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent(
29 Handle<Name> name, Handle<HeapType> type) {
30 Isolate* isolate = name->GetIsolate();
31 Handle<Map> receiver_map = IC::TypeToMap(*type, isolate);
32 if (receiver_map->prototype()->IsNull()) {
33 // TODO(jkummerow/verwaest): If there is no prototype and the property
34 // is nonexistent, introduce a builtin to handle this (fast properties
35 // -> return undefined, dictionary properties -> do negative lookup).
36 return Handle<Code>();
37 }
38 CacheHolderFlag flag;
39 Handle<Map> stub_holder_map =
40 IC::GetHandlerCacheHolder(*type, false, isolate, &flag);
41
42 // If no dictionary mode objects are present in the prototype chain, the load
43 // nonexistent IC stub can be shared for all names for a given map and we use
44 // the empty string for the map cache in that case. If there are dictionary
45 // mode objects involved, we need to do negative lookups in the stub and
46 // therefore the stub will be specific to the name.
47 Handle<Name> cache_name =
48 receiver_map->is_dictionary_map()
49 ? name
50 : Handle<Name>::cast(isolate->factory()->nonexistent_symbol());
51 Handle<Map> current_map = stub_holder_map;
52 Handle<JSObject> last(JSObject::cast(receiver_map->prototype()));
53 while (true) {
54 if (current_map->is_dictionary_map()) cache_name = name;
55 if (current_map->prototype()->IsNull()) break;
56 last = handle(JSObject::cast(current_map->prototype()));
57 current_map = handle(last->map());
58 }
59 // Compile the stub that is either shared for all names or
60 // name specific if there are global objects involved.
61 Handle<Code> handler = PropertyHandlerCompiler::Find(
62 cache_name, stub_holder_map, Code::LOAD_IC, flag, Code::FAST);
63 if (!handler.is_null()) return handler;
64
65 NamedLoadHandlerCompiler compiler(isolate, type, last, flag);
66 handler = compiler.CompileLoadNonexistent(cache_name);
67 Map::UpdateCodeCache(stub_holder_map, cache_name, handler);
68 return handler;
69}
70
71
72Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
73 Code::StubType type,
74 Handle<Name> name) {
75 Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder());
76 Handle<Code> code = GetCodeWithFlags(flags, name);
77 PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, *name));
78 return code;
79}
80
81
82void PropertyHandlerCompiler::set_type_for_object(Handle<Object> object) {
83 type_ = IC::CurrentTypeOf(object, isolate());
84}
85
86
87#define __ ACCESS_MASM(masm())
88
89
90Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
91 Handle<Name> name,
92 Label* miss) {
93 PrototypeCheckType check_type = CHECK_ALL_MAPS;
94 int function_index = -1;
95 if (type()->Is(HeapType::String())) {
96 function_index = Context::STRING_FUNCTION_INDEX;
97 } else if (type()->Is(HeapType::Symbol())) {
98 function_index = Context::SYMBOL_FUNCTION_INDEX;
99 } else if (type()->Is(HeapType::Number())) {
100 function_index = Context::NUMBER_FUNCTION_INDEX;
101 } else if (type()->Is(HeapType::Boolean())) {
102 function_index = Context::BOOLEAN_FUNCTION_INDEX;
103 } else {
104 check_type = SKIP_RECEIVER;
105 }
106
107 if (check_type == CHECK_ALL_MAPS) {
108 GenerateDirectLoadGlobalFunctionPrototype(masm(), function_index,
109 scratch1(), miss);
110 Object* function = isolate()->native_context()->get(function_index);
111 Object* prototype = JSFunction::cast(function)->instance_prototype();
112 set_type_for_object(handle(prototype, isolate()));
113 object_reg = scratch1();
114 }
115
116 // Check that the maps starting from the prototype haven't changed.
117 return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
118 miss, check_type);
119}
120
121
122// Frontend for store uses the name register. It has to be restored before a
123// miss.
124Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
125 Handle<Name> name,
126 Label* miss) {
127 return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
128 miss, SKIP_RECEIVER);
129}
130
131
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400132Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000133 Label miss;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400134 if (IC::ICUseVector(kind())) {
135 PushVectorAndSlot();
136 }
137 Register reg = FrontendHeader(receiver(), name, &miss);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000138 FrontendFooter(name, &miss);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400139 // The footer consumes the vector and slot from the stack if miss occurs.
140 if (IC::ICUseVector(kind())) {
141 DiscardVectorAndSlot();
142 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000143 return reg;
144}
145
146
147void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
148 Label* miss,
149 Register scratch1,
150 Register scratch2) {
151 Register holder_reg;
152 Handle<Map> last_map;
153 if (holder().is_null()) {
154 holder_reg = receiver();
155 last_map = IC::TypeToMap(*type(), isolate());
156 // If |type| has null as its prototype, |holder()| is
157 // Handle<JSObject>::null().
158 DCHECK(last_map->prototype() == isolate()->heap()->null_value());
159 } else {
160 holder_reg = FrontendHeader(receiver(), name, miss);
161 last_map = handle(holder()->map());
162 }
163
164 if (last_map->is_dictionary_map()) {
165 if (last_map->IsJSGlobalObjectMap()) {
166 Handle<JSGlobalObject> global =
167 holder().is_null()
168 ? Handle<JSGlobalObject>::cast(type()->AsConstant()->Value())
169 : Handle<JSGlobalObject>::cast(holder());
170 GenerateCheckPropertyCell(masm(), global, name, scratch1, miss);
171 } else {
172 if (!name->IsUniqueName()) {
173 DCHECK(name->IsString());
174 name = factory()->InternalizeString(Handle<String>::cast(name));
175 }
176 DCHECK(holder().is_null() ||
177 holder()->property_dictionary()->FindEntry(name) ==
178 NameDictionary::kNotFound);
179 GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1,
180 scratch2);
181 }
182 }
183}
184
185
186Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name,
187 FieldIndex field) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400188 Register reg = Frontend(name);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000189 __ Move(receiver(), reg);
190 LoadFieldStub stub(isolate(), field);
191 GenerateTailCall(masm(), stub.GetCode());
192 return GetCode(kind(), Code::FAST, name);
193}
194
195
196Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name,
197 int constant_index) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400198 Register reg = Frontend(name);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000199 __ Move(receiver(), reg);
200 LoadConstantStub stub(isolate(), constant_index);
201 GenerateTailCall(masm(), stub.GetCode());
202 return GetCode(kind(), Code::FAST, name);
203}
204
205
206Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent(
207 Handle<Name> name) {
208 Label miss;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400209 if (IC::ICUseVector(kind())) {
210 DCHECK(kind() == Code::LOAD_IC);
211 PushVectorAndSlot();
212 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000213 NonexistentFrontendHeader(name, &miss, scratch2(), scratch3());
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400214 if (IC::ICUseVector(kind())) {
215 DiscardVectorAndSlot();
216 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000217 GenerateLoadConstant(isolate()->factory()->undefined_value());
218 FrontendFooter(name, &miss);
219 return GetCode(kind(), Code::FAST, name);
220}
221
222
223Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
224 Handle<Name> name, Handle<ExecutableAccessorInfo> callback) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400225 Register reg = Frontend(name);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000226 GenerateLoadCallback(reg, callback);
227 return GetCode(kind(), Code::FAST, name);
228}
229
230
231Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
232 Handle<Name> name, const CallOptimization& call_optimization) {
233 DCHECK(call_optimization.is_simple_api_call());
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400234 Frontend(name);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000235 Handle<Map> receiver_map = IC::TypeToMap(*type(), isolate());
236 GenerateFastApiCall(masm(), call_optimization, receiver_map, receiver(),
237 scratch1(), false, 0, NULL);
238 return GetCode(kind(), Code::FAST, name);
239}
240
241
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400242void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
243 if (IC::ICUseVector(kind())) {
244 if (holder_reg.is(receiver())) {
245 PushVectorAndSlot();
246 } else {
247 DCHECK(holder_reg.is(scratch1()));
248 PushVectorAndSlot(scratch2(), scratch3());
249 }
250 }
251}
252
253
254void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
255 PopMode mode) {
256 if (IC::ICUseVector(kind())) {
257 if (mode == DISCARD) {
258 DiscardVectorAndSlot();
259 } else {
260 if (holder_reg.is(receiver())) {
261 PopVectorAndSlot();
262 } else {
263 DCHECK(holder_reg.is(scratch1()));
264 PopVectorAndSlot(scratch2(), scratch3());
265 }
266 }
267 }
268}
269
270
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000271Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
272 LookupIterator* it) {
273 // So far the most popular follow ups for interceptor loads are FIELD and
274 // ExecutableAccessorInfo, so inline only them. Other cases may be added
275 // later.
276 bool inline_followup = false;
277 switch (it->state()) {
278 case LookupIterator::TRANSITION:
279 UNREACHABLE();
280 case LookupIterator::ACCESS_CHECK:
281 case LookupIterator::INTERCEPTOR:
282 case LookupIterator::JSPROXY:
283 case LookupIterator::NOT_FOUND:
284 break;
285 case LookupIterator::DATA:
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400286 inline_followup =
287 it->property_details().type() == FIELD && !it->is_dictionary_holder();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000288 break;
289 case LookupIterator::ACCESSOR: {
290 Handle<Object> accessors = it->GetAccessors();
291 inline_followup = accessors->IsExecutableAccessorInfo();
292 if (!inline_followup) break;
293 Handle<ExecutableAccessorInfo> info =
294 Handle<ExecutableAccessorInfo>::cast(accessors);
295 inline_followup = info->getter() != NULL &&
296 ExecutableAccessorInfo::IsCompatibleReceiverType(
297 isolate(), info, type());
298 }
299 }
300
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400301 Label miss;
302 InterceptorVectorSlotPush(receiver());
303 Register reg = FrontendHeader(receiver(), it->name(), &miss);
304 FrontendFooter(it->name(), &miss);
305 InterceptorVectorSlotPop(reg);
306
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000307 if (inline_followup) {
308 // TODO(368): Compile in the whole chain: all the interceptors in
309 // prototypes and ultimate answer.
310 GenerateLoadInterceptorWithFollowup(it, reg);
311 } else {
312 GenerateLoadInterceptor(reg);
313 }
314 return GetCode(kind(), Code::FAST, it->name());
315}
316
317
318void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
319 LookupIterator* it, Register interceptor_reg) {
320 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
321
322 set_type_for_object(holder());
323 set_holder(real_named_property_holder);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400324
325 Label miss;
326 InterceptorVectorSlotPush(interceptor_reg);
327 Register reg = FrontendHeader(interceptor_reg, it->name(), &miss);
328 FrontendFooter(it->name(), &miss);
329 // We discard the vector and slot now because we don't miss below this point.
330 InterceptorVectorSlotPop(reg, DISCARD);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000331
332 switch (it->state()) {
333 case LookupIterator::ACCESS_CHECK:
334 case LookupIterator::INTERCEPTOR:
335 case LookupIterator::JSPROXY:
336 case LookupIterator::NOT_FOUND:
337 case LookupIterator::TRANSITION:
338 UNREACHABLE();
339 case LookupIterator::DATA: {
340 DCHECK_EQ(FIELD, it->property_details().type());
341 __ Move(receiver(), reg);
342 LoadFieldStub stub(isolate(), it->GetFieldIndex());
343 GenerateTailCall(masm(), stub.GetCode());
344 break;
345 }
346 case LookupIterator::ACCESSOR:
347 Handle<ExecutableAccessorInfo> info =
348 Handle<ExecutableAccessorInfo>::cast(it->GetAccessors());
349 DCHECK_NE(NULL, info->getter());
350 GenerateLoadCallback(reg, info);
351 }
352}
353
354
355Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
356 Handle<Name> name, Handle<JSFunction> getter) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400357 Frontend(name);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000358 GenerateLoadViaGetter(masm(), type(), receiver(), getter);
359 return GetCode(kind(), Code::FAST, name);
360}
361
362
363// TODO(verwaest): Cleanup. holder() is actually the receiver.
364Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
365 Handle<Map> transition, Handle<Name> name) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400366 Label miss;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000367
368 // Check that we are allowed to write this.
369 bool is_nonexistent = holder()->map() == transition->GetBackPointer();
370 if (is_nonexistent) {
371 // Find the top object.
372 Handle<JSObject> last;
373 PrototypeIterator iter(isolate(), holder());
374 while (!iter.IsAtEnd()) {
375 last = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
376 iter.Advance();
377 }
378 if (!last.is_null()) set_holder(last);
379 NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
380 } else {
381 FrontendHeader(receiver(), name, &miss);
382 DCHECK(holder()->HasFastProperties());
383 }
384
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400385 int descriptor = transition->LastAdded();
386 Handle<DescriptorArray> descriptors(transition->instance_descriptors());
387 PropertyDetails details = descriptors->GetDetails(descriptor);
388 Representation representation = details.representation();
389 DCHECK(!representation.IsNone());
390
391 // Stub is never generated for objects that require access checks.
392 DCHECK(!transition->is_access_check_needed());
393
394 // Call to respective StoreTransitionStub.
395 if (details.type() == CONSTANT) {
396 GenerateRestoreMap(transition, scratch2(), &miss);
397 DCHECK(descriptors->GetValue(descriptor)->IsJSFunction());
398 Register map_reg = StoreTransitionDescriptor::MapRegister();
399 GenerateConstantCheck(map_reg, descriptor, value(), scratch2(), &miss);
400 GenerateRestoreName(name);
401 StoreTransitionStub stub(isolate());
402 GenerateTailCall(masm(), stub.GetCode());
403
404 } else {
405 if (representation.IsHeapObject()) {
406 GenerateFieldTypeChecks(descriptors->GetFieldType(descriptor), value(),
407 &miss);
408 }
409 StoreTransitionStub::StoreMode store_mode =
410 Map::cast(transition->GetBackPointer())->unused_property_fields() == 0
411 ? StoreTransitionStub::ExtendStorageAndStoreMapAndValue
412 : StoreTransitionStub::StoreMapAndValue;
413
414 GenerateRestoreMap(transition, scratch2(), &miss);
415 GenerateRestoreName(name);
416 StoreTransitionStub stub(isolate(),
417 FieldIndex::ForDescriptor(*transition, descriptor),
418 representation, store_mode);
419 GenerateTailCall(masm(), stub.GetCode());
420 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000421
422 GenerateRestoreName(&miss, name);
423 TailCallBuiltin(masm(), MissBuiltin(kind()));
424
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000425 return GetCode(kind(), Code::FAST, name);
426}
427
428
429Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) {
430 Label miss;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400431 DCHECK(it->representation().IsHeapObject());
432
433 GenerateFieldTypeChecks(*it->GetFieldType(), value(), &miss);
434 StoreFieldStub stub(isolate(), it->GetFieldIndex(), it->representation());
435 GenerateTailCall(masm(), stub.GetCode());
436
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000437 __ bind(&miss);
438 TailCallBuiltin(masm(), MissBuiltin(kind()));
439 return GetCode(kind(), Code::FAST, it->name());
440}
441
442
443Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
444 Handle<JSObject> object, Handle<Name> name, Handle<JSFunction> setter) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400445 Frontend(name);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000446 GenerateStoreViaSetter(masm(), type(), receiver(), setter);
447
448 return GetCode(kind(), Code::FAST, name);
449}
450
451
452Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
453 Handle<JSObject> object, Handle<Name> name,
454 const CallOptimization& call_optimization) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400455 Frontend(name);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000456 Register values[] = {value()};
457 GenerateFastApiCall(masm(), call_optimization, handle(object->map()),
458 receiver(), scratch1(), true, 1, values);
459 return GetCode(kind(), Code::FAST, name);
460}
461
462
463#undef __
464
465
466void ElementHandlerCompiler::CompileElementHandlers(
467 MapHandleList* receiver_maps, CodeHandleList* handlers) {
468 for (int i = 0; i < receiver_maps->length(); ++i) {
469 Handle<Map> receiver_map = receiver_maps->at(i);
470 Handle<Code> cached_stub;
471
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400472 if (receiver_map->IsStringMap()) {
473 cached_stub = LoadIndexedStringStub(isolate()).GetCode();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000474 } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) {
475 cached_stub = isolate()->builtins()->KeyedLoadIC_Slow();
476 } else {
477 bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
478 ElementsKind elements_kind = receiver_map->elements_kind();
479 if (receiver_map->has_indexed_interceptor()) {
480 cached_stub = LoadIndexedInterceptorStub(isolate()).GetCode();
481 } else if (IsSloppyArgumentsElements(elements_kind)) {
482 cached_stub = KeyedLoadSloppyArgumentsStub(isolate()).GetCode();
483 } else if (IsFastElementsKind(elements_kind) ||
484 IsExternalArrayElementsKind(elements_kind) ||
485 IsFixedTypedArrayElementsKind(elements_kind)) {
486 cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind)
487 .GetCode();
488 } else {
489 DCHECK(elements_kind == DICTIONARY_ELEMENTS);
490 cached_stub = LoadDictionaryElementStub(isolate()).GetCode();
491 }
492 }
493
494 handlers->Add(cached_stub);
495 }
496}
497}
498} // namespace v8::internal