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 #if V8_TARGET_ARCH_ARM 6 7 #include "src/ic/handler-compiler.h" 8 9 #include "src/api-arguments.h" 10 #include "src/field-type.h" 11 #include "src/ic/call-optimization.h" 12 #include "src/ic/ic.h" 13 #include "src/isolate-inl.h" 14 15 namespace v8 { 16 namespace internal { 17 18 #define __ ACCESS_MASM(masm) 19 20 21 void NamedLoadHandlerCompiler::GenerateLoadViaGetter( 22 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder, 23 int accessor_index, int expected_arguments, Register scratch) { 24 // ----------- S t a t e ------------- 25 // -- r0 : receiver 26 // -- r2 : name 27 // -- lr : return address 28 // ----------------------------------- 29 { 30 FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); 31 32 // Save context register 33 __ push(cp); 34 35 if (accessor_index >= 0) { 36 DCHECK(!holder.is(scratch)); 37 DCHECK(!receiver.is(scratch)); 38 // Call the JavaScript getter with the receiver on the stack. 39 if (map->IsJSGlobalObjectMap()) { 40 // Swap in the global receiver. 41 __ ldr(scratch, 42 FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 43 receiver = scratch; 44 } 45 __ push(receiver); 46 __ LoadAccessor(r1, holder, accessor_index, ACCESSOR_GETTER); 47 __ mov(r0, Operand(0)); 48 __ Call(masm->isolate()->builtins()->CallFunction( 49 ConvertReceiverMode::kNotNullOrUndefined), 50 RelocInfo::CODE_TARGET); 51 } else { 52 // If we generate a global code snippet for deoptimization only, remember 53 // the place to continue after deoptimization. 54 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); 55 } 56 57 // Restore context register. 58 __ pop(cp); 59 } 60 __ Ret(); 61 } 62 63 64 void NamedStoreHandlerCompiler::GenerateStoreViaSetter( 65 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder, 66 int accessor_index, int expected_arguments, Register scratch) { 67 // ----------- S t a t e ------------- 68 // -- lr : return address 69 // ----------------------------------- 70 { 71 FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); 72 73 // Save context register 74 __ push(cp); 75 // Save value register, so we can restore it later. 76 __ push(value()); 77 78 if (accessor_index >= 0) { 79 DCHECK(!holder.is(scratch)); 80 DCHECK(!receiver.is(scratch)); 81 DCHECK(!value().is(scratch)); 82 // Call the JavaScript setter with receiver and value on the stack. 83 if (map->IsJSGlobalObjectMap()) { 84 // Swap in the global receiver. 85 __ ldr(scratch, 86 FieldMemOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 87 receiver = scratch; 88 } 89 __ Push(receiver, value()); 90 __ LoadAccessor(r1, holder, accessor_index, ACCESSOR_SETTER); 91 __ mov(r0, Operand(1)); 92 __ Call(masm->isolate()->builtins()->CallFunction( 93 ConvertReceiverMode::kNotNullOrUndefined), 94 RelocInfo::CODE_TARGET); 95 } else { 96 // If we generate a global code snippet for deoptimization only, remember 97 // the place to continue after deoptimization. 98 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); 99 } 100 101 // We have to return the passed value, not the return value of the setter. 102 __ pop(r0); 103 104 // Restore context register. 105 __ pop(cp); 106 } 107 __ Ret(); 108 } 109 110 111 void PropertyHandlerCompiler::PushVectorAndSlot(Register vector, 112 Register slot) { 113 MacroAssembler* masm = this->masm(); 114 STATIC_ASSERT(LoadWithVectorDescriptor::kSlot < 115 LoadWithVectorDescriptor::kVector); 116 STATIC_ASSERT(StoreWithVectorDescriptor::kSlot < 117 StoreWithVectorDescriptor::kVector); 118 STATIC_ASSERT(StoreTransitionDescriptor::kSlot < 119 StoreTransitionDescriptor::kVector); 120 __ push(slot); 121 __ push(vector); 122 } 123 124 125 void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) { 126 MacroAssembler* masm = this->masm(); 127 __ pop(vector); 128 __ pop(slot); 129 } 130 131 132 void PropertyHandlerCompiler::DiscardVectorAndSlot() { 133 MacroAssembler* masm = this->masm(); 134 // Remove vector and slot. 135 __ add(sp, sp, Operand(2 * kPointerSize)); 136 } 137 138 void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup( 139 MacroAssembler* masm, Label* miss_label, Register receiver, 140 Handle<Name> name, Register scratch0, Register scratch1) { 141 DCHECK(name->IsUniqueName()); 142 DCHECK(!receiver.is(scratch0)); 143 Counters* counters = masm->isolate()->counters(); 144 __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1); 145 __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); 146 147 Label done; 148 149 const int kInterceptorOrAccessCheckNeededMask = 150 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); 151 152 // Bail out if the receiver has a named interceptor or requires access checks. 153 Register map = scratch1; 154 __ ldr(map, FieldMemOperand(receiver, HeapObject::kMapOffset)); 155 __ ldrb(scratch0, FieldMemOperand(map, Map::kBitFieldOffset)); 156 __ tst(scratch0, Operand(kInterceptorOrAccessCheckNeededMask)); 157 __ b(ne, miss_label); 158 159 // Check that receiver is a JSObject. 160 __ ldrb(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset)); 161 __ cmp(scratch0, Operand(FIRST_JS_RECEIVER_TYPE)); 162 __ b(lt, miss_label); 163 164 // Load properties array. 165 Register properties = scratch0; 166 __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); 167 // Check that the properties array is a dictionary. 168 __ ldr(map, FieldMemOperand(properties, HeapObject::kMapOffset)); 169 Register tmp = properties; 170 __ LoadRoot(tmp, Heap::kHashTableMapRootIndex); 171 __ cmp(map, tmp); 172 __ b(ne, miss_label); 173 174 // Restore the temporarily used register. 175 __ ldr(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset)); 176 177 178 NameDictionaryLookupStub::GenerateNegativeLookup( 179 masm, miss_label, &done, receiver, properties, name, scratch1); 180 __ bind(&done); 181 __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1); 182 } 183 184 // Generate code to check that a global property cell is empty. Create 185 // the property cell at compilation time if no cell exists for the 186 // property. 187 void PropertyHandlerCompiler::GenerateCheckPropertyCell( 188 MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name, 189 Register scratch, Label* miss) { 190 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell( 191 global, name, PropertyCellType::kInvalidated); 192 Isolate* isolate = masm->isolate(); 193 DCHECK(cell->value()->IsTheHole(isolate)); 194 Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell); 195 __ LoadWeakValue(scratch, weak_cell, miss); 196 __ ldr(scratch, FieldMemOperand(scratch, PropertyCell::kValueOffset)); 197 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); 198 __ cmp(scratch, ip); 199 __ b(ne, miss); 200 } 201 202 static void CompileCallLoadPropertyWithInterceptor( 203 MacroAssembler* masm, Register receiver, Register holder, Register name, 204 Handle<JSObject> holder_obj, Runtime::FunctionId id) { 205 DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength == 206 Runtime::FunctionForId(id)->nargs); 207 208 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0); 209 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1); 210 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2); 211 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3); 212 __ push(name); 213 __ push(receiver); 214 __ push(holder); 215 216 __ CallRuntime(id); 217 } 218 219 220 // Generate call to api function. 221 void PropertyHandlerCompiler::GenerateApiAccessorCall( 222 MacroAssembler* masm, const CallOptimization& optimization, 223 Handle<Map> receiver_map, Register receiver, Register scratch_in, 224 bool is_store, Register store_parameter, Register accessor_holder, 225 int accessor_index) { 226 DCHECK(!accessor_holder.is(scratch_in)); 227 DCHECK(!receiver.is(scratch_in)); 228 __ push(receiver); 229 // Write the arguments to stack frame. 230 if (is_store) { 231 DCHECK(!receiver.is(store_parameter)); 232 DCHECK(!scratch_in.is(store_parameter)); 233 __ push(store_parameter); 234 } 235 DCHECK(optimization.is_simple_api_call()); 236 237 // Abi for CallApiCallbackStub. 238 Register callee = r0; 239 Register data = r4; 240 Register holder = r2; 241 Register api_function_address = r1; 242 243 // Put callee in place. 244 __ LoadAccessor(callee, accessor_holder, accessor_index, 245 is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER); 246 247 // Put holder in place. 248 CallOptimization::HolderLookup holder_lookup; 249 int holder_depth = 0; 250 optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup, 251 &holder_depth); 252 switch (holder_lookup) { 253 case CallOptimization::kHolderIsReceiver: 254 __ Move(holder, receiver); 255 break; 256 case CallOptimization::kHolderFound: 257 __ ldr(holder, FieldMemOperand(receiver, HeapObject::kMapOffset)); 258 __ ldr(holder, FieldMemOperand(holder, Map::kPrototypeOffset)); 259 for (int i = 1; i < holder_depth; i++) { 260 __ ldr(holder, FieldMemOperand(holder, HeapObject::kMapOffset)); 261 __ ldr(holder, FieldMemOperand(holder, Map::kPrototypeOffset)); 262 } 263 break; 264 case CallOptimization::kHolderNotFound: 265 UNREACHABLE(); 266 break; 267 } 268 269 Isolate* isolate = masm->isolate(); 270 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); 271 bool call_data_undefined = false; 272 // Put call data in place. 273 if (api_call_info->data()->IsUndefined(isolate)) { 274 call_data_undefined = true; 275 __ LoadRoot(data, Heap::kUndefinedValueRootIndex); 276 } else { 277 if (optimization.is_constant_call()) { 278 __ ldr(data, 279 FieldMemOperand(callee, JSFunction::kSharedFunctionInfoOffset)); 280 __ ldr(data, 281 FieldMemOperand(data, SharedFunctionInfo::kFunctionDataOffset)); 282 __ ldr(data, 283 FieldMemOperand(data, FunctionTemplateInfo::kCallCodeOffset)); 284 } else { 285 __ ldr(data, 286 FieldMemOperand(callee, FunctionTemplateInfo::kCallCodeOffset)); 287 } 288 __ ldr(data, FieldMemOperand(data, CallHandlerInfo::kDataOffset)); 289 } 290 291 if (api_call_info->fast_handler()->IsCode()) { 292 // Just tail call into the fast handler if present. 293 __ Jump(handle(Code::cast(api_call_info->fast_handler())), 294 RelocInfo::CODE_TARGET); 295 return; 296 } 297 298 // Put api_function_address in place. 299 Address function_address = v8::ToCData<Address>(api_call_info->callback()); 300 ApiFunction fun(function_address); 301 ExternalReference::Type type = ExternalReference::DIRECT_API_CALL; 302 ExternalReference ref = ExternalReference(&fun, type, masm->isolate()); 303 __ mov(api_function_address, Operand(ref)); 304 305 // Jump to stub. 306 CallApiCallbackStub stub(isolate, is_store, call_data_undefined, 307 !optimization.is_constant_call()); 308 __ TailCallStub(&stub); 309 } 310 311 #undef __ 312 #define __ ACCESS_MASM(masm()) 313 314 315 void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label, 316 Handle<Name> name) { 317 if (!label->is_unused()) { 318 __ bind(label); 319 __ mov(this->name(), Operand(name)); 320 } 321 } 322 323 void PropertyHandlerCompiler::GenerateAccessCheck( 324 Handle<WeakCell> native_context_cell, Register scratch1, Register scratch2, 325 Label* miss, bool compare_native_contexts_only) { 326 Label done; 327 // Load current native context. 328 __ ldr(scratch1, NativeContextMemOperand()); 329 // Load expected native context. 330 __ LoadWeakValue(scratch2, native_context_cell, miss); 331 __ cmp(scratch1, scratch2); 332 333 if (!compare_native_contexts_only) { 334 __ b(eq, &done); 335 336 // Compare security tokens of current and expected native contexts. 337 __ ldr(scratch1, 338 ContextMemOperand(scratch1, Context::SECURITY_TOKEN_INDEX)); 339 __ ldr(scratch2, 340 ContextMemOperand(scratch2, Context::SECURITY_TOKEN_INDEX)); 341 __ cmp(scratch1, scratch2); 342 } 343 __ b(ne, miss); 344 345 __ bind(&done); 346 } 347 348 Register PropertyHandlerCompiler::CheckPrototypes( 349 Register object_reg, Register holder_reg, Register scratch1, 350 Register scratch2, Handle<Name> name, Label* miss, 351 ReturnHolder return_what) { 352 Handle<Map> receiver_map = map(); 353 354 // Make sure there's no overlap between holder and object registers. 355 DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); 356 DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && 357 !scratch2.is(scratch1)); 358 359 Handle<Cell> validity_cell = 360 Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); 361 if (!validity_cell.is_null()) { 362 DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), validity_cell->value()); 363 __ mov(scratch1, Operand(validity_cell)); 364 __ ldr(scratch1, FieldMemOperand(scratch1, Cell::kValueOffset)); 365 __ cmp(scratch1, Operand(Smi::FromInt(Map::kPrototypeChainValid))); 366 __ b(ne, miss); 367 } 368 369 // Keep track of the current object in register reg. 370 Register reg = object_reg; 371 int depth = 0; 372 373 Handle<JSObject> current = Handle<JSObject>::null(); 374 if (receiver_map->IsJSGlobalObjectMap()) { 375 current = isolate()->global_object(); 376 } 377 378 Handle<Map> current_map(receiver_map->GetPrototypeChainRootMap(isolate()), 379 isolate()); 380 Handle<Map> holder_map(holder()->map()); 381 // Traverse the prototype chain and check the maps in the prototype chain for 382 // fast and global objects or do negative lookup for normal objects. 383 while (!current_map.is_identical_to(holder_map)) { 384 ++depth; 385 386 if (current_map->IsJSGlobalObjectMap()) { 387 GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), 388 name, scratch2, miss); 389 } else if (current_map->is_dictionary_map()) { 390 DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. 391 DCHECK(name->IsUniqueName()); 392 DCHECK(current.is_null() || 393 current->property_dictionary()->FindEntry(name) == 394 NameDictionary::kNotFound); 395 396 if (depth > 1) { 397 Handle<WeakCell> weak_cell = 398 Map::GetOrCreatePrototypeWeakCell(current, isolate()); 399 __ LoadWeakValue(reg, weak_cell, miss); 400 } 401 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, 402 scratch2); 403 } 404 405 reg = holder_reg; // From now on the object will be in holder_reg. 406 // Go to the next object in the prototype chain. 407 current = handle(JSObject::cast(current_map->prototype())); 408 current_map = handle(current->map()); 409 } 410 411 DCHECK(!current_map->IsJSGlobalProxyMap()); 412 413 // Log the check depth. 414 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); 415 416 bool return_holder = return_what == RETURN_HOLDER; 417 if (return_holder && depth != 0) { 418 Handle<WeakCell> weak_cell = 419 Map::GetOrCreatePrototypeWeakCell(current, isolate()); 420 __ LoadWeakValue(reg, weak_cell, miss); 421 } 422 423 // Return the register containing the holder. 424 return return_holder ? reg : no_reg; 425 } 426 427 428 void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 429 if (!miss->is_unused()) { 430 Label success; 431 __ b(&success); 432 __ bind(miss); 433 if (IC::ICUseVector(kind())) { 434 DCHECK(kind() == Code::LOAD_IC); 435 PopVectorAndSlot(); 436 } 437 TailCallBuiltin(masm(), MissBuiltin(kind())); 438 __ bind(&success); 439 } 440 } 441 442 443 void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 444 if (!miss->is_unused()) { 445 Label success; 446 __ b(&success); 447 GenerateRestoreName(miss, name); 448 if (IC::ICUseVector(kind())) PopVectorAndSlot(); 449 TailCallBuiltin(masm(), MissBuiltin(kind())); 450 __ bind(&success); 451 } 452 } 453 454 void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup( 455 LookupIterator* it, Register holder_reg) { 456 DCHECK(holder()->HasNamedInterceptor()); 457 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate())); 458 459 // Compile the interceptor call, followed by inline code to load the 460 // property from further up the prototype chain if the call fails. 461 // Check that the maps haven't changed. 462 DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1())); 463 464 // Preserve the receiver register explicitly whenever it is different from the 465 // holder and it is needed should the interceptor return without any result. 466 // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD 467 // case might cause a miss during the prototype check. 468 bool must_perform_prototype_check = 469 !holder().is_identical_to(it->GetHolder<JSObject>()); 470 bool must_preserve_receiver_reg = 471 !receiver().is(holder_reg) && 472 (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check); 473 474 // Save necessary data before invoking an interceptor. 475 // Requires a frame to make GC aware of pushed pointers. 476 { 477 FrameAndConstantPoolScope frame_scope(masm(), StackFrame::INTERNAL); 478 if (must_preserve_receiver_reg) { 479 __ Push(receiver(), holder_reg, this->name()); 480 } else { 481 __ Push(holder_reg, this->name()); 482 } 483 InterceptorVectorSlotPush(holder_reg); 484 // Invoke an interceptor. Note: map checks from receiver to 485 // interceptor's holder has been compiled before (see a caller 486 // of this method.) 487 CompileCallLoadPropertyWithInterceptor( 488 masm(), receiver(), holder_reg, this->name(), holder(), 489 Runtime::kLoadPropertyWithInterceptorOnly); 490 491 // Check if interceptor provided a value for property. If it's 492 // the case, return immediately. 493 Label interceptor_failed; 494 __ LoadRoot(scratch1(), Heap::kNoInterceptorResultSentinelRootIndex); 495 __ cmp(r0, scratch1()); 496 __ b(eq, &interceptor_failed); 497 frame_scope.GenerateLeaveFrame(); 498 __ Ret(); 499 500 __ bind(&interceptor_failed); 501 InterceptorVectorSlotPop(holder_reg); 502 __ pop(this->name()); 503 __ pop(holder_reg); 504 if (must_preserve_receiver_reg) { 505 __ pop(receiver()); 506 } 507 // Leave the internal frame. 508 } 509 510 GenerateLoadPostInterceptor(it, holder_reg); 511 } 512 513 514 void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) { 515 // Call the runtime system to load the interceptor. 516 DCHECK(holder()->HasNamedInterceptor()); 517 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined(isolate())); 518 519 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0); 520 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1); 521 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2); 522 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3); 523 __ Push(name(), receiver(), holder_reg); 524 // See NamedLoadHandlerCompiler::InterceptorVectorSlotPop() for details. 525 if (holder_reg.is(receiver())) { 526 __ Push(slot(), vector()); 527 } else { 528 __ Push(scratch3(), scratch2()); // slot, vector 529 } 530 531 __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor); 532 } 533 534 void NamedStoreHandlerCompiler::ZapStackArgumentsRegisterAliases() { 535 STATIC_ASSERT(!StoreWithVectorDescriptor::kPassLastArgsOnStack); 536 } 537 538 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( 539 Handle<JSObject> object, Handle<Name> name, Handle<AccessorInfo> callback, 540 LanguageMode language_mode) { 541 Register holder_reg = Frontend(name); 542 543 __ push(receiver()); // receiver 544 __ push(holder_reg); 545 546 // If the callback cannot leak, then push the callback directly, 547 // otherwise wrap it in a weak cell. 548 if (callback->data()->IsUndefined(isolate()) || callback->data()->IsSmi()) { 549 __ mov(ip, Operand(callback)); 550 } else { 551 Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback); 552 __ mov(ip, Operand(cell)); 553 } 554 __ push(ip); 555 __ mov(ip, Operand(name)); 556 __ Push(ip, value()); 557 __ Push(Smi::FromInt(language_mode)); 558 559 // Do tail-call to the runtime system. 560 __ TailCallRuntime(Runtime::kStoreCallbackProperty); 561 562 // Return the generated code. 563 return GetCode(kind(), name); 564 } 565 566 567 Register NamedStoreHandlerCompiler::value() { 568 return StoreDescriptor::ValueRegister(); 569 } 570 571 572 Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( 573 Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) { 574 Label miss; 575 if (IC::ICUseVector(kind())) { 576 PushVectorAndSlot(); 577 } 578 FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING); 579 580 // Get the value from the cell. 581 Register result = StoreDescriptor::ValueRegister(); 582 Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell); 583 __ LoadWeakValue(result, weak_cell, &miss); 584 __ ldr(result, FieldMemOperand(result, PropertyCell::kValueOffset)); 585 586 // Check for deleted property if property can actually be deleted. 587 if (is_configurable) { 588 __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); 589 __ cmp(result, ip); 590 __ b(eq, &miss); 591 } 592 593 Counters* counters = isolate()->counters(); 594 __ IncrementCounter(counters->ic_named_load_global_stub(), 1, r1, r3); 595 if (IC::ICUseVector(kind())) { 596 DiscardVectorAndSlot(); 597 } 598 __ Ret(); 599 600 FrontendFooter(name, &miss); 601 602 // Return the generated code. 603 return GetCode(kind(), name); 604 } 605 606 607 #undef __ 608 } // namespace internal 609 } // namespace v8 610 611 #endif // V8_TARGET_ARCH_ARM 612