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/ic/handler-compiler.h"
6
7 #include "src/field-type.h"
8 #include "src/ic/call-optimization.h"
9 #include "src/ic/handler-configuration-inl.h"
10 #include "src/ic/ic-inl.h"
11 #include "src/ic/ic.h"
12 #include "src/isolate-inl.h"
13
14 namespace v8 {
15 namespace internal {
16
Find(Handle<Name> name,Handle<Map> stub_holder,Code::Kind kind,CacheHolderFlag cache_holder)17 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name,
18 Handle<Map> stub_holder,
19 Code::Kind kind,
20 CacheHolderFlag cache_holder) {
21 Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder);
22 Code* code = stub_holder->LookupInCodeCache(*name, flags);
23 if (code == nullptr) return Handle<Code>();
24 return handle(code);
25 }
26
GetCode(Code::Kind kind,Handle<Name> name)27 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
28 Handle<Name> name) {
29 Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder());
30 Handle<Code> code = GetCodeWithFlags(flags, name);
31 PROFILE(isolate(), CodeCreateEvent(CodeEventListener::HANDLER_TAG,
32 AbstractCode::cast(*code), *name));
33 #ifdef DEBUG
34 code->VerifyEmbeddedObjects();
35 #endif
36 return code;
37 }
38
39
40 #define __ ACCESS_MASM(masm())
41
42
FrontendHeader(Register object_reg,Handle<Name> name,Label * miss,ReturnHolder return_what)43 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
44 Handle<Name> name,
45 Label* miss,
46 ReturnHolder return_what) {
47 if (map()->IsPrimitiveMap() || map()->IsJSGlobalProxyMap()) {
48 // If the receiver is a global proxy and if we get to this point then
49 // the compile-time (current) native context has access to global proxy's
50 // native context. Since access rights revocation is not supported at all,
51 // we can generate a check that an execution-time native context is either
52 // the same as compile-time native context or has the same access token.
53 Handle<Context> native_context = isolate()->native_context();
54 Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
55
56 bool compare_native_contexts_only = map()->IsPrimitiveMap();
57 GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss,
58 compare_native_contexts_only);
59 }
60
61 // Check that the maps starting from the prototype haven't changed.
62 return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
63 miss, return_what);
64 }
65
66
67 // Frontend for store uses the name register. It has to be restored before a
68 // miss.
FrontendHeader(Register object_reg,Handle<Name> name,Label * miss,ReturnHolder return_what)69 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
70 Handle<Name> name,
71 Label* miss,
72 ReturnHolder return_what) {
73 if (map()->IsJSGlobalProxyMap()) {
74 Handle<Context> native_context = isolate()->native_context();
75 Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
76 GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss, false);
77 }
78
79 return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
80 miss, return_what);
81 }
82
83
Frontend(Handle<Name> name)84 Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
85 Label miss;
86 if (IC::ShouldPushPopSlotAndVector(kind())) {
87 PushVectorAndSlot();
88 }
89 Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER);
90 FrontendFooter(name, &miss);
91 // The footer consumes the vector and slot from the stack if miss occurs.
92 if (IC::ShouldPushPopSlotAndVector(kind())) {
93 DiscardVectorAndSlot();
94 }
95 return reg;
96 }
97
CompileLoadCallback(Handle<Name> name,Handle<AccessorInfo> callback,Handle<Code> slow_stub)98 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
99 Handle<Name> name, Handle<AccessorInfo> callback, Handle<Code> slow_stub) {
100 if (V8_UNLIKELY(FLAG_runtime_stats)) {
101 GenerateTailCall(masm(), slow_stub);
102 }
103 Register reg = Frontend(name);
104 GenerateLoadCallback(reg, callback);
105 return GetCode(kind(), name);
106 }
107
CompileLoadCallback(Handle<Name> name,const CallOptimization & call_optimization,int accessor_index,Handle<Code> slow_stub)108 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
109 Handle<Name> name, const CallOptimization& call_optimization,
110 int accessor_index, Handle<Code> slow_stub) {
111 DCHECK(call_optimization.is_simple_api_call());
112 if (V8_UNLIKELY(FLAG_runtime_stats)) {
113 GenerateTailCall(masm(), slow_stub);
114 }
115 Register holder = Frontend(name);
116 GenerateApiAccessorCall(masm(), call_optimization, map(), receiver(),
117 scratch2(), false, no_reg, holder, accessor_index);
118 return GetCode(kind(), name);
119 }
120
121
InterceptorVectorSlotPush(Register holder_reg)122 void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
123 if (IC::ShouldPushPopSlotAndVector(kind())) {
124 if (holder_reg.is(receiver())) {
125 PushVectorAndSlot();
126 } else {
127 DCHECK(holder_reg.is(scratch1()));
128 PushVectorAndSlot(scratch2(), scratch3());
129 }
130 }
131 }
132
133
InterceptorVectorSlotPop(Register holder_reg,PopMode mode)134 void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
135 PopMode mode) {
136 if (IC::ShouldPushPopSlotAndVector(kind())) {
137 if (mode == DISCARD) {
138 DiscardVectorAndSlot();
139 } else {
140 if (holder_reg.is(receiver())) {
141 PopVectorAndSlot();
142 } else {
143 DCHECK(holder_reg.is(scratch1()));
144 PopVectorAndSlot(scratch2(), scratch3());
145 }
146 }
147 }
148 }
149
150
CompileLoadInterceptor(LookupIterator * it)151 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
152 LookupIterator* it) {
153 // So far the most popular follow ups for interceptor loads are DATA and
154 // AccessorInfo, so inline only them. Other cases may be added
155 // later.
156 bool inline_followup = false;
157 switch (it->state()) {
158 case LookupIterator::TRANSITION:
159 UNREACHABLE();
160 case LookupIterator::ACCESS_CHECK:
161 case LookupIterator::INTERCEPTOR:
162 case LookupIterator::JSPROXY:
163 case LookupIterator::NOT_FOUND:
164 case LookupIterator::INTEGER_INDEXED_EXOTIC:
165 break;
166 case LookupIterator::DATA: {
167 PropertyDetails details = it->property_details();
168 inline_followup = details.kind() == kData &&
169 details.location() == kField &&
170 !it->is_dictionary_holder();
171 break;
172 }
173 case LookupIterator::ACCESSOR: {
174 Handle<Object> accessors = it->GetAccessors();
175 if (accessors->IsAccessorInfo()) {
176 Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
177 inline_followup =
178 info->getter() != NULL &&
179 AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map());
180 } else if (accessors->IsAccessorPair()) {
181 Handle<JSObject> property_holder(it->GetHolder<JSObject>());
182 Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
183 isolate());
184 if (!(getter->IsJSFunction() || getter->IsFunctionTemplateInfo())) {
185 break;
186 }
187 if (!property_holder->HasFastProperties()) break;
188 CallOptimization call_optimization(getter);
189 Handle<Map> receiver_map = map();
190 inline_followup = call_optimization.is_simple_api_call() &&
191 call_optimization.IsCompatibleReceiverMap(
192 receiver_map, property_holder);
193 }
194 }
195 }
196
197 Label miss;
198 InterceptorVectorSlotPush(receiver());
199 bool lost_holder_register = false;
200 auto holder_orig = holder();
201 // non masking interceptors must check the entire chain, so temporarily reset
202 // the holder to be that last element for the FrontendHeader call.
203 if (holder()->GetNamedInterceptor()->non_masking()) {
204 DCHECK(!inline_followup);
205 JSObject* last = *holder();
206 PrototypeIterator iter(isolate(), last);
207 while (!iter.IsAtEnd()) {
208 lost_holder_register = true;
209 // Casting to JSObject is fine here. The LookupIterator makes sure to
210 // look behind non-masking interceptors during the original lookup, and
211 // we wouldn't try to compile a handler if there was a Proxy anywhere.
212 last = iter.GetCurrent<JSObject>();
213 iter.Advance();
214 }
215 auto last_handle = handle(last);
216 set_holder(last_handle);
217 }
218 Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER);
219 // Reset the holder so further calculations are correct.
220 set_holder(holder_orig);
221 if (lost_holder_register) {
222 if (*it->GetReceiver() == *holder()) {
223 reg = receiver();
224 } else {
225 // Reload lost holder register.
226 auto cell = isolate()->factory()->NewWeakCell(holder());
227 __ LoadWeakValue(reg, cell, &miss);
228 }
229 }
230 FrontendFooter(it->name(), &miss);
231 InterceptorVectorSlotPop(reg);
232 if (inline_followup) {
233 // TODO(368): Compile in the whole chain: all the interceptors in
234 // prototypes and ultimate answer.
235 GenerateLoadInterceptorWithFollowup(it, reg);
236 } else {
237 GenerateLoadInterceptor(reg);
238 }
239 return GetCode(kind(), it->name());
240 }
241
GenerateLoadCallback(Register reg,Handle<AccessorInfo> callback)242 void NamedLoadHandlerCompiler::GenerateLoadCallback(
243 Register reg, Handle<AccessorInfo> callback) {
244 DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister()));
245 __ Move(ApiGetterDescriptor::HolderRegister(), reg);
246 // The callback is alive if this instruction is executed,
247 // so the weak cell is not cleared and points to data.
248 Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
249 __ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell);
250
251 CallApiGetterStub stub(isolate());
252 __ TailCallStub(&stub);
253 }
254
GenerateLoadPostInterceptor(LookupIterator * it,Register interceptor_reg)255 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
256 LookupIterator* it, Register interceptor_reg) {
257 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
258
259 Handle<Map> holder_map(holder()->map());
260 set_map(holder_map);
261 set_holder(real_named_property_holder);
262
263 Label miss;
264 InterceptorVectorSlotPush(interceptor_reg);
265 Register reg =
266 FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER);
267 FrontendFooter(it->name(), &miss);
268 // We discard the vector and slot now because we don't miss below this point.
269 InterceptorVectorSlotPop(reg, DISCARD);
270
271 switch (it->state()) {
272 case LookupIterator::ACCESS_CHECK:
273 case LookupIterator::INTERCEPTOR:
274 case LookupIterator::JSPROXY:
275 case LookupIterator::NOT_FOUND:
276 case LookupIterator::INTEGER_INDEXED_EXOTIC:
277 case LookupIterator::TRANSITION:
278 UNREACHABLE();
279 case LookupIterator::DATA: {
280 DCHECK_EQ(kData, it->property_details().kind());
281 DCHECK_EQ(kField, it->property_details().location());
282 __ Move(LoadFieldDescriptor::ReceiverRegister(), reg);
283 Handle<Object> smi_handler =
284 LoadIC::SimpleFieldLoad(isolate(), it->GetFieldIndex());
285 __ Move(LoadFieldDescriptor::SmiHandlerRegister(), smi_handler);
286 GenerateTailCall(masm(), isolate()->builtins()->LoadField());
287 break;
288 }
289 case LookupIterator::ACCESSOR:
290 if (it->GetAccessors()->IsAccessorInfo()) {
291 Handle<AccessorInfo> info =
292 Handle<AccessorInfo>::cast(it->GetAccessors());
293 DCHECK_NOT_NULL(info->getter());
294 GenerateLoadCallback(reg, info);
295 } else {
296 Handle<Object> function = handle(
297 AccessorPair::cast(*it->GetAccessors())->getter(), isolate());
298 CallOptimization call_optimization(function);
299 GenerateApiAccessorCall(masm(), call_optimization, holder_map,
300 receiver(), scratch2(), false, no_reg, reg,
301 it->GetAccessorIndex());
302 }
303 }
304 }
305
CompileLoadViaGetter(Handle<Name> name,int accessor_index,int expected_arguments)306 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
307 Handle<Name> name, int accessor_index, int expected_arguments) {
308 Register holder = Frontend(name);
309 GenerateLoadViaGetter(masm(), map(), receiver(), holder, accessor_index,
310 expected_arguments, scratch2());
311 return GetCode(kind(), name);
312 }
313
CompileStoreViaSetter(Handle<JSObject> object,Handle<Name> name,int accessor_index,int expected_arguments)314 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
315 Handle<JSObject> object, Handle<Name> name, int accessor_index,
316 int expected_arguments) {
317 Register holder = Frontend(name);
318 GenerateStoreViaSetter(masm(), map(), receiver(), holder, accessor_index,
319 expected_arguments, scratch2());
320
321 return GetCode(kind(), name);
322 }
323
CompileStoreCallback(Handle<JSObject> object,Handle<Name> name,const CallOptimization & call_optimization,int accessor_index,Handle<Code> slow_stub)324 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
325 Handle<JSObject> object, Handle<Name> name,
326 const CallOptimization& call_optimization, int accessor_index,
327 Handle<Code> slow_stub) {
328 if (V8_UNLIKELY(FLAG_runtime_stats)) {
329 GenerateTailCall(masm(), slow_stub);
330 }
331 Register holder = Frontend(name);
332 if (Descriptor::kPassLastArgsOnStack) {
333 __ LoadParameterFromStack<Descriptor>(value(), Descriptor::kValue);
334 }
335 GenerateApiAccessorCall(masm(), call_optimization, handle(object->map()),
336 receiver(), scratch2(), true, value(), holder,
337 accessor_index);
338 return GetCode(kind(), name);
339 }
340
341
342 #undef __
343
344 // static
GetKeyedLoadHandler(Handle<Map> receiver_map,Isolate * isolate)345 Handle<Object> ElementHandlerCompiler::GetKeyedLoadHandler(
346 Handle<Map> receiver_map, Isolate* isolate) {
347 if (receiver_map->has_indexed_interceptor() &&
348 !receiver_map->GetIndexedInterceptor()->getter()->IsUndefined(isolate) &&
349 !receiver_map->GetIndexedInterceptor()->non_masking()) {
350 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedInterceptorStub);
351 return LoadIndexedInterceptorStub(isolate).GetCode();
352 }
353 if (receiver_map->IsStringMap()) {
354 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedStringStub);
355 return isolate->builtins()->KeyedLoadIC_IndexedString();
356 }
357 InstanceType instance_type = receiver_map->instance_type();
358 if (instance_type < FIRST_JS_RECEIVER_TYPE) {
359 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_SlowStub);
360 return isolate->builtins()->KeyedLoadIC_Slow();
361 }
362
363 ElementsKind elements_kind = receiver_map->elements_kind();
364 if (IsSloppyArgumentsElements(elements_kind)) {
365 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_KeyedLoadSloppyArgumentsStub);
366 return KeyedLoadSloppyArgumentsStub(isolate).GetCode();
367 }
368 bool is_js_array = instance_type == JS_ARRAY_TYPE;
369 if (elements_kind == DICTIONARY_ELEMENTS) {
370 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
371 return LoadHandler::LoadElement(isolate, elements_kind, false, is_js_array);
372 }
373 DCHECK(IsFastElementsKind(elements_kind) ||
374 IsFixedTypedArrayElementsKind(elements_kind));
375 // TODO(jkummerow): Use IsHoleyElementsKind(elements_kind).
376 bool convert_hole_to_undefined =
377 is_js_array && elements_kind == FAST_HOLEY_ELEMENTS &&
378 *receiver_map == isolate->get_initial_js_array_map(elements_kind);
379 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
380 return LoadHandler::LoadElement(isolate, elements_kind,
381 convert_hole_to_undefined, is_js_array);
382 }
383
CompileElementHandlers(MapHandleList * receiver_maps,List<Handle<Object>> * handlers)384 void ElementHandlerCompiler::CompileElementHandlers(
385 MapHandleList* receiver_maps, List<Handle<Object>>* handlers) {
386 for (int i = 0; i < receiver_maps->length(); ++i) {
387 handlers->Add(GetKeyedLoadHandler(receiver_maps->at(i), isolate()));
388 }
389 }
390 } // namespace internal
391 } // namespace v8
392