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 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 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 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. 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 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 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 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 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 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 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 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 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 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 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 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 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 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