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<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 72 Handle<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 82 void PropertyHandlerCompiler::set_type_for_object(Handle<Object> object) { 83 type_ = IC::CurrentTypeOf(object, isolate()); 84 } 85 86 87 #define __ ACCESS_MASM(masm()) 88 89 90 Register 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. 124 Register 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 132 Register PropertyHandlerCompiler::Frontend(Register object_reg, 133 Handle<Name> name) { 134 Label miss; 135 Register reg = FrontendHeader(object_reg, name, &miss); 136 FrontendFooter(name, &miss); 137 return reg; 138 } 139 140 141 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name, 142 Label* miss, 143 Register scratch1, 144 Register scratch2) { 145 Register holder_reg; 146 Handle<Map> last_map; 147 if (holder().is_null()) { 148 holder_reg = receiver(); 149 last_map = IC::TypeToMap(*type(), isolate()); 150 // If |type| has null as its prototype, |holder()| is 151 // Handle<JSObject>::null(). 152 DCHECK(last_map->prototype() == isolate()->heap()->null_value()); 153 } else { 154 holder_reg = FrontendHeader(receiver(), name, miss); 155 last_map = handle(holder()->map()); 156 } 157 158 if (last_map->is_dictionary_map()) { 159 if (last_map->IsJSGlobalObjectMap()) { 160 Handle<JSGlobalObject> global = 161 holder().is_null() 162 ? Handle<JSGlobalObject>::cast(type()->AsConstant()->Value()) 163 : Handle<JSGlobalObject>::cast(holder()); 164 GenerateCheckPropertyCell(masm(), global, name, scratch1, miss); 165 } else { 166 if (!name->IsUniqueName()) { 167 DCHECK(name->IsString()); 168 name = factory()->InternalizeString(Handle<String>::cast(name)); 169 } 170 DCHECK(holder().is_null() || 171 holder()->property_dictionary()->FindEntry(name) == 172 NameDictionary::kNotFound); 173 GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1, 174 scratch2); 175 } 176 } 177 } 178 179 180 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name, 181 FieldIndex field) { 182 Register reg = Frontend(receiver(), name); 183 __ Move(receiver(), reg); 184 LoadFieldStub stub(isolate(), field); 185 GenerateTailCall(masm(), stub.GetCode()); 186 return GetCode(kind(), Code::FAST, name); 187 } 188 189 190 Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name, 191 int constant_index) { 192 Register reg = Frontend(receiver(), name); 193 __ Move(receiver(), reg); 194 LoadConstantStub stub(isolate(), constant_index); 195 GenerateTailCall(masm(), stub.GetCode()); 196 return GetCode(kind(), Code::FAST, name); 197 } 198 199 200 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent( 201 Handle<Name> name) { 202 Label miss; 203 NonexistentFrontendHeader(name, &miss, scratch2(), scratch3()); 204 GenerateLoadConstant(isolate()->factory()->undefined_value()); 205 FrontendFooter(name, &miss); 206 return GetCode(kind(), Code::FAST, name); 207 } 208 209 210 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( 211 Handle<Name> name, Handle<ExecutableAccessorInfo> callback) { 212 Register reg = Frontend(receiver(), name); 213 GenerateLoadCallback(reg, callback); 214 return GetCode(kind(), Code::FAST, name); 215 } 216 217 218 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( 219 Handle<Name> name, const CallOptimization& call_optimization) { 220 DCHECK(call_optimization.is_simple_api_call()); 221 Frontend(receiver(), name); 222 Handle<Map> receiver_map = IC::TypeToMap(*type(), isolate()); 223 GenerateFastApiCall(masm(), call_optimization, receiver_map, receiver(), 224 scratch1(), false, 0, NULL); 225 return GetCode(kind(), Code::FAST, name); 226 } 227 228 229 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor( 230 LookupIterator* it) { 231 // So far the most popular follow ups for interceptor loads are FIELD and 232 // ExecutableAccessorInfo, so inline only them. Other cases may be added 233 // later. 234 bool inline_followup = false; 235 switch (it->state()) { 236 case LookupIterator::TRANSITION: 237 UNREACHABLE(); 238 case LookupIterator::ACCESS_CHECK: 239 case LookupIterator::INTERCEPTOR: 240 case LookupIterator::JSPROXY: 241 case LookupIterator::NOT_FOUND: 242 break; 243 case LookupIterator::DATA: 244 inline_followup = it->property_details().type() == FIELD; 245 break; 246 case LookupIterator::ACCESSOR: { 247 Handle<Object> accessors = it->GetAccessors(); 248 inline_followup = accessors->IsExecutableAccessorInfo(); 249 if (!inline_followup) break; 250 Handle<ExecutableAccessorInfo> info = 251 Handle<ExecutableAccessorInfo>::cast(accessors); 252 inline_followup = info->getter() != NULL && 253 ExecutableAccessorInfo::IsCompatibleReceiverType( 254 isolate(), info, type()); 255 } 256 } 257 258 Register reg = Frontend(receiver(), it->name()); 259 if (inline_followup) { 260 // TODO(368): Compile in the whole chain: all the interceptors in 261 // prototypes and ultimate answer. 262 GenerateLoadInterceptorWithFollowup(it, reg); 263 } else { 264 GenerateLoadInterceptor(reg); 265 } 266 return GetCode(kind(), Code::FAST, it->name()); 267 } 268 269 270 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor( 271 LookupIterator* it, Register interceptor_reg) { 272 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>()); 273 274 set_type_for_object(holder()); 275 set_holder(real_named_property_holder); 276 Register reg = Frontend(interceptor_reg, it->name()); 277 278 switch (it->state()) { 279 case LookupIterator::ACCESS_CHECK: 280 case LookupIterator::INTERCEPTOR: 281 case LookupIterator::JSPROXY: 282 case LookupIterator::NOT_FOUND: 283 case LookupIterator::TRANSITION: 284 UNREACHABLE(); 285 case LookupIterator::DATA: { 286 DCHECK_EQ(FIELD, it->property_details().type()); 287 __ Move(receiver(), reg); 288 LoadFieldStub stub(isolate(), it->GetFieldIndex()); 289 GenerateTailCall(masm(), stub.GetCode()); 290 break; 291 } 292 case LookupIterator::ACCESSOR: 293 Handle<ExecutableAccessorInfo> info = 294 Handle<ExecutableAccessorInfo>::cast(it->GetAccessors()); 295 DCHECK_NE(NULL, info->getter()); 296 GenerateLoadCallback(reg, info); 297 } 298 } 299 300 301 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter( 302 Handle<Name> name, Handle<JSFunction> getter) { 303 Frontend(receiver(), name); 304 GenerateLoadViaGetter(masm(), type(), receiver(), getter); 305 return GetCode(kind(), Code::FAST, name); 306 } 307 308 309 // TODO(verwaest): Cleanup. holder() is actually the receiver. 310 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition( 311 Handle<Map> transition, Handle<Name> name) { 312 Label miss, slow; 313 314 // Ensure no transitions to deprecated maps are followed. 315 __ CheckMapDeprecated(transition, scratch1(), &miss); 316 317 // Check that we are allowed to write this. 318 bool is_nonexistent = holder()->map() == transition->GetBackPointer(); 319 if (is_nonexistent) { 320 // Find the top object. 321 Handle<JSObject> last; 322 PrototypeIterator iter(isolate(), holder()); 323 while (!iter.IsAtEnd()) { 324 last = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); 325 iter.Advance(); 326 } 327 if (!last.is_null()) set_holder(last); 328 NonexistentFrontendHeader(name, &miss, scratch1(), scratch2()); 329 } else { 330 FrontendHeader(receiver(), name, &miss); 331 DCHECK(holder()->HasFastProperties()); 332 } 333 334 GenerateStoreTransition(transition, name, receiver(), this->name(), value(), 335 scratch1(), scratch2(), scratch3(), &miss, &slow); 336 337 GenerateRestoreName(&miss, name); 338 TailCallBuiltin(masm(), MissBuiltin(kind())); 339 340 GenerateRestoreName(&slow, name); 341 TailCallBuiltin(masm(), SlowBuiltin(kind())); 342 return GetCode(kind(), Code::FAST, name); 343 } 344 345 346 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) { 347 Label miss; 348 GenerateStoreField(it, value(), &miss); 349 __ bind(&miss); 350 TailCallBuiltin(masm(), MissBuiltin(kind())); 351 return GetCode(kind(), Code::FAST, it->name()); 352 } 353 354 355 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter( 356 Handle<JSObject> object, Handle<Name> name, Handle<JSFunction> setter) { 357 Frontend(receiver(), name); 358 GenerateStoreViaSetter(masm(), type(), receiver(), setter); 359 360 return GetCode(kind(), Code::FAST, name); 361 } 362 363 364 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( 365 Handle<JSObject> object, Handle<Name> name, 366 const CallOptimization& call_optimization) { 367 Frontend(receiver(), name); 368 Register values[] = {value()}; 369 GenerateFastApiCall(masm(), call_optimization, handle(object->map()), 370 receiver(), scratch1(), true, 1, values); 371 return GetCode(kind(), Code::FAST, name); 372 } 373 374 375 #undef __ 376 377 378 void ElementHandlerCompiler::CompileElementHandlers( 379 MapHandleList* receiver_maps, CodeHandleList* handlers) { 380 for (int i = 0; i < receiver_maps->length(); ++i) { 381 Handle<Map> receiver_map = receiver_maps->at(i); 382 Handle<Code> cached_stub; 383 384 if ((receiver_map->instance_type() & kNotStringTag) == 0) { 385 cached_stub = isolate()->builtins()->KeyedLoadIC_String(); 386 } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) { 387 cached_stub = isolate()->builtins()->KeyedLoadIC_Slow(); 388 } else { 389 bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; 390 ElementsKind elements_kind = receiver_map->elements_kind(); 391 if (receiver_map->has_indexed_interceptor()) { 392 cached_stub = LoadIndexedInterceptorStub(isolate()).GetCode(); 393 } else if (IsSloppyArgumentsElements(elements_kind)) { 394 cached_stub = KeyedLoadSloppyArgumentsStub(isolate()).GetCode(); 395 } else if (IsFastElementsKind(elements_kind) || 396 IsExternalArrayElementsKind(elements_kind) || 397 IsFixedTypedArrayElementsKind(elements_kind)) { 398 cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind) 399 .GetCode(); 400 } else { 401 DCHECK(elements_kind == DICTIONARY_ELEMENTS); 402 cached_stub = LoadDictionaryElementStub(isolate()).GetCode(); 403 } 404 } 405 406 handlers->Add(cached_stub); 407 } 408 } 409 } 410 } // namespace v8::internal 411