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_X64 6 7#include "src/ic/call-optimization.h" 8#include "src/ic/handler-compiler.h" 9#include "src/ic/ic.h" 10#include "src/isolate-inl.h" 11 12namespace v8 { 13namespace internal { 14 15#define __ ACCESS_MASM(masm) 16 17void PropertyHandlerCompiler::PushVectorAndSlot(Register vector, 18 Register slot) { 19 MacroAssembler* masm = this->masm(); 20 __ Push(vector); 21 __ Push(slot); 22} 23 24 25void PropertyHandlerCompiler::PopVectorAndSlot(Register vector, Register slot) { 26 MacroAssembler* masm = this->masm(); 27 __ Pop(slot); 28 __ Pop(vector); 29} 30 31 32void PropertyHandlerCompiler::DiscardVectorAndSlot() { 33 MacroAssembler* masm = this->masm(); 34 // Remove vector and slot. 35 __ addp(rsp, Immediate(2 * kPointerSize)); 36} 37 38 39void PropertyHandlerCompiler::GenerateDictionaryNegativeLookup( 40 MacroAssembler* masm, Label* miss_label, Register receiver, 41 Handle<Name> name, Register scratch0, Register scratch1) { 42 DCHECK(name->IsUniqueName()); 43 DCHECK(!receiver.is(scratch0)); 44 Counters* counters = masm->isolate()->counters(); 45 __ IncrementCounter(counters->negative_lookups(), 1); 46 __ IncrementCounter(counters->negative_lookups_miss(), 1); 47 48 __ movp(scratch0, FieldOperand(receiver, HeapObject::kMapOffset)); 49 50 const int kInterceptorOrAccessCheckNeededMask = 51 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); 52 53 // Bail out if the receiver has a named interceptor or requires access checks. 54 __ testb(FieldOperand(scratch0, Map::kBitFieldOffset), 55 Immediate(kInterceptorOrAccessCheckNeededMask)); 56 __ j(not_zero, miss_label); 57 58 // Check that receiver is a JSObject. 59 __ CmpInstanceType(scratch0, FIRST_JS_RECEIVER_TYPE); 60 __ j(below, miss_label); 61 62 // Load properties array. 63 Register properties = scratch0; 64 __ movp(properties, FieldOperand(receiver, JSObject::kPropertiesOffset)); 65 66 // Check that the properties array is a dictionary. 67 __ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset), 68 Heap::kHashTableMapRootIndex); 69 __ j(not_equal, miss_label); 70 71 Label done; 72 NameDictionaryLookupStub::GenerateNegativeLookup(masm, miss_label, &done, 73 properties, name, scratch1); 74 __ bind(&done); 75 __ DecrementCounter(counters->negative_lookups_miss(), 1); 76} 77 78 79void NamedLoadHandlerCompiler::GenerateDirectLoadGlobalFunctionPrototype( 80 MacroAssembler* masm, int index, Register result, Label* miss) { 81 __ LoadNativeContextSlot(index, result); 82 // Load its initial map. The global functions all have initial maps. 83 __ movp(result, 84 FieldOperand(result, JSFunction::kPrototypeOrInitialMapOffset)); 85 // Load the prototype from the initial map. 86 __ movp(result, FieldOperand(result, Map::kPrototypeOffset)); 87} 88 89 90void NamedLoadHandlerCompiler::GenerateLoadFunctionPrototype( 91 MacroAssembler* masm, Register receiver, Register result, Register scratch, 92 Label* miss_label) { 93 __ TryGetFunctionPrototype(receiver, result, miss_label); 94 if (!result.is(rax)) __ movp(rax, result); 95 __ ret(0); 96} 97 98 99static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, 100 Register holder, Register name, 101 Handle<JSObject> holder_obj) { 102 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex == 0); 103 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex == 1); 104 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex == 2); 105 STATIC_ASSERT(NamedLoadHandlerCompiler::kInterceptorArgsLength == 3); 106 __ Push(name); 107 __ Push(receiver); 108 __ Push(holder); 109} 110 111 112static void CompileCallLoadPropertyWithInterceptor( 113 MacroAssembler* masm, Register receiver, Register holder, Register name, 114 Handle<JSObject> holder_obj, Runtime::FunctionId id) { 115 DCHECK(NamedLoadHandlerCompiler::kInterceptorArgsLength == 116 Runtime::FunctionForId(id)->nargs); 117 PushInterceptorArguments(masm, receiver, holder, name, holder_obj); 118 __ CallRuntime(id); 119} 120 121 122// Generate call to api function. 123void PropertyHandlerCompiler::GenerateApiAccessorCall( 124 MacroAssembler* masm, const CallOptimization& optimization, 125 Handle<Map> receiver_map, Register receiver, Register scratch, 126 bool is_store, Register store_parameter, Register accessor_holder, 127 int accessor_index) { 128 DCHECK(!accessor_holder.is(scratch)); 129 DCHECK(optimization.is_simple_api_call()); 130 131 __ PopReturnAddressTo(scratch); 132 // receiver 133 __ Push(receiver); 134 // Write the arguments to stack frame. 135 if (is_store) { 136 DCHECK(!receiver.is(store_parameter)); 137 DCHECK(!scratch.is(store_parameter)); 138 __ Push(store_parameter); 139 } 140 __ PushReturnAddressFrom(scratch); 141 // Stack now matches JSFunction abi. 142 143 // Abi for CallApiFunctionStub. 144 Register callee = rdi; 145 Register data = rbx; 146 Register holder = rcx; 147 Register api_function_address = rdx; 148 scratch = no_reg; 149 150 // Put callee in place. 151 __ LoadAccessor(callee, accessor_holder, accessor_index, 152 is_store ? ACCESSOR_SETTER : ACCESSOR_GETTER); 153 154 // Put holder in place. 155 CallOptimization::HolderLookup holder_lookup; 156 int holder_depth = 0; 157 optimization.LookupHolderOfExpectedType(receiver_map, &holder_lookup, 158 &holder_depth); 159 switch (holder_lookup) { 160 case CallOptimization::kHolderIsReceiver: 161 __ Move(holder, receiver); 162 break; 163 case CallOptimization::kHolderFound: 164 __ movp(holder, FieldOperand(receiver, HeapObject::kMapOffset)); 165 __ movp(holder, FieldOperand(holder, Map::kPrototypeOffset)); 166 for (int i = 1; i < holder_depth; i++) { 167 __ movp(holder, FieldOperand(holder, HeapObject::kMapOffset)); 168 __ movp(holder, FieldOperand(holder, Map::kPrototypeOffset)); 169 } 170 break; 171 case CallOptimization::kHolderNotFound: 172 UNREACHABLE(); 173 break; 174 } 175 176 Isolate* isolate = masm->isolate(); 177 Handle<CallHandlerInfo> api_call_info = optimization.api_call_info(); 178 bool call_data_undefined = false; 179 // Put call data in place. 180 if (api_call_info->data()->IsUndefined()) { 181 call_data_undefined = true; 182 __ LoadRoot(data, Heap::kUndefinedValueRootIndex); 183 } else { 184 __ movp(data, FieldOperand(callee, JSFunction::kSharedFunctionInfoOffset)); 185 __ movp(data, FieldOperand(data, SharedFunctionInfo::kFunctionDataOffset)); 186 __ movp(data, FieldOperand(data, FunctionTemplateInfo::kCallCodeOffset)); 187 __ movp(data, FieldOperand(data, CallHandlerInfo::kDataOffset)); 188 } 189 190 if (api_call_info->fast_handler()->IsCode()) { 191 // Just tail call into the fast handler if present. 192 __ Jump(handle(Code::cast(api_call_info->fast_handler())), 193 RelocInfo::CODE_TARGET); 194 return; 195 } 196 197 // Put api_function_address in place. 198 Address function_address = v8::ToCData<Address>(api_call_info->callback()); 199 __ Move(api_function_address, function_address, 200 RelocInfo::EXTERNAL_REFERENCE); 201 202 // Jump to stub. 203 CallApiAccessorStub stub(isolate, is_store, call_data_undefined); 204 __ TailCallStub(&stub); 205} 206 207 208void PropertyHandlerCompiler::GenerateCheckPropertyCell( 209 MacroAssembler* masm, Handle<JSGlobalObject> global, Handle<Name> name, 210 Register scratch, Label* miss) { 211 Handle<PropertyCell> cell = JSGlobalObject::EnsurePropertyCell(global, name); 212 DCHECK(cell->value()->IsTheHole()); 213 Factory* factory = masm->isolate()->factory(); 214 Handle<WeakCell> weak_cell = factory->NewWeakCell(cell); 215 __ LoadWeakValue(scratch, weak_cell, miss); 216 __ Cmp(FieldOperand(scratch, PropertyCell::kValueOffset), 217 factory->the_hole_value()); 218 __ j(not_equal, miss); 219} 220 221 222void NamedStoreHandlerCompiler::GenerateStoreViaSetter( 223 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder, 224 int accessor_index, int expected_arguments, Register scratch) { 225 // ----------- S t a t e ------------- 226 // -- rsp[0] : return address 227 // ----------------------------------- 228 { 229 FrameScope scope(masm, StackFrame::INTERNAL); 230 231 // Save value register, so we can restore it later. 232 __ Push(value()); 233 234 if (accessor_index >= 0) { 235 DCHECK(!holder.is(scratch)); 236 DCHECK(!receiver.is(scratch)); 237 DCHECK(!value().is(scratch)); 238 // Call the JavaScript setter with receiver and value on the stack. 239 if (map->IsJSGlobalObjectMap()) { 240 // Swap in the global receiver. 241 __ movp(scratch, 242 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 243 receiver = scratch; 244 } 245 __ Push(receiver); 246 __ Push(value()); 247 ParameterCount actual(1); 248 ParameterCount expected(expected_arguments); 249 __ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_SETTER); 250 __ InvokeFunction(rdi, no_reg, expected, actual, CALL_FUNCTION, 251 CheckDebugStepCallWrapper()); 252 } else { 253 // If we generate a global code snippet for deoptimization only, remember 254 // the place to continue after deoptimization. 255 masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset()); 256 } 257 258 // We have to return the passed value, not the return value of the setter. 259 __ Pop(rax); 260 261 // Restore context register. 262 __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 263 } 264 __ ret(0); 265} 266 267 268void NamedLoadHandlerCompiler::GenerateLoadViaGetter( 269 MacroAssembler* masm, Handle<Map> map, Register receiver, Register holder, 270 int accessor_index, int expected_arguments, Register scratch) { 271 // ----------- S t a t e ------------- 272 // -- rax : receiver 273 // -- rcx : name 274 // -- rsp[0] : return address 275 // ----------------------------------- 276 { 277 FrameScope scope(masm, StackFrame::INTERNAL); 278 279 if (accessor_index >= 0) { 280 DCHECK(!holder.is(scratch)); 281 DCHECK(!receiver.is(scratch)); 282 // Call the JavaScript getter with the receiver on the stack. 283 if (map->IsJSGlobalObjectMap()) { 284 // Swap in the global receiver. 285 __ movp(scratch, 286 FieldOperand(receiver, JSGlobalObject::kGlobalProxyOffset)); 287 receiver = scratch; 288 } 289 __ Push(receiver); 290 ParameterCount actual(0); 291 ParameterCount expected(expected_arguments); 292 __ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_GETTER); 293 __ InvokeFunction(rdi, no_reg, expected, actual, CALL_FUNCTION, 294 CheckDebugStepCallWrapper()); 295 } else { 296 // If we generate a global code snippet for deoptimization only, remember 297 // the place to continue after deoptimization. 298 masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset()); 299 } 300 301 // Restore context register. 302 __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 303 } 304 __ ret(0); 305} 306 307 308static void StoreIC_PushArgs(MacroAssembler* masm) { 309 Register receiver = StoreDescriptor::ReceiverRegister(); 310 Register name = StoreDescriptor::NameRegister(); 311 Register value = StoreDescriptor::ValueRegister(); 312 313 Register slot = VectorStoreICDescriptor::SlotRegister(); 314 Register vector = VectorStoreICDescriptor::VectorRegister(); 315 316 __ PopReturnAddressTo(r11); 317 __ Push(receiver); 318 __ Push(name); 319 __ Push(value); 320 __ Push(slot); 321 __ Push(vector); 322 __ PushReturnAddressFrom(r11); 323} 324 325 326void NamedStoreHandlerCompiler::GenerateSlow(MacroAssembler* masm) { 327 // Return address is on the stack. 328 StoreIC_PushArgs(masm); 329 330 // Do tail-call to runtime routine. 331 __ TailCallRuntime(Runtime::kStoreIC_Slow); 332} 333 334 335void ElementHandlerCompiler::GenerateStoreSlow(MacroAssembler* masm) { 336 // Return address is on the stack. 337 StoreIC_PushArgs(masm); 338 339 // Do tail-call to runtime routine. 340 __ TailCallRuntime(Runtime::kKeyedStoreIC_Slow); 341} 342 343 344#undef __ 345#define __ ACCESS_MASM((masm())) 346 347 348void NamedStoreHandlerCompiler::GenerateRestoreName(Label* label, 349 Handle<Name> name) { 350 if (!label->is_unused()) { 351 __ bind(label); 352 __ Move(this->name(), name); 353 } 354} 355 356 357void NamedStoreHandlerCompiler::GenerateRestoreName(Handle<Name> name) { 358 __ Move(this->name(), name); 359} 360 361 362void NamedStoreHandlerCompiler::RearrangeVectorAndSlot( 363 Register current_map, Register destination_map) { 364 DCHECK(false); // Not implemented. 365} 366 367 368void NamedStoreHandlerCompiler::GenerateRestoreMap(Handle<Map> transition, 369 Register map_reg, 370 Register scratch, 371 Label* miss) { 372 Handle<WeakCell> cell = Map::WeakCellForMap(transition); 373 DCHECK(!map_reg.is(scratch)); 374 __ LoadWeakValue(map_reg, cell, miss); 375 if (transition->CanBeDeprecated()) { 376 __ movl(scratch, FieldOperand(map_reg, Map::kBitField3Offset)); 377 __ andl(scratch, Immediate(Map::Deprecated::kMask)); 378 __ j(not_zero, miss); 379 } 380} 381 382 383void NamedStoreHandlerCompiler::GenerateConstantCheck(Register map_reg, 384 int descriptor, 385 Register value_reg, 386 Register scratch, 387 Label* miss_label) { 388 DCHECK(!map_reg.is(scratch)); 389 DCHECK(!map_reg.is(value_reg)); 390 DCHECK(!value_reg.is(scratch)); 391 __ LoadInstanceDescriptors(map_reg, scratch); 392 __ movp(scratch, 393 FieldOperand(scratch, DescriptorArray::GetValueOffset(descriptor))); 394 __ cmpp(value_reg, scratch); 395 __ j(not_equal, miss_label); 396} 397 398 399void NamedStoreHandlerCompiler::GenerateFieldTypeChecks(HeapType* field_type, 400 Register value_reg, 401 Label* miss_label) { 402 Register map_reg = scratch1(); 403 Register scratch = scratch2(); 404 DCHECK(!value_reg.is(map_reg)); 405 DCHECK(!value_reg.is(scratch)); 406 __ JumpIfSmi(value_reg, miss_label); 407 HeapType::Iterator<Map> it = field_type->Classes(); 408 if (!it.Done()) { 409 Label do_store; 410 __ movp(map_reg, FieldOperand(value_reg, HeapObject::kMapOffset)); 411 while (true) { 412 __ CmpWeakValue(map_reg, Map::WeakCellForMap(it.Current()), scratch); 413 it.Advance(); 414 if (it.Done()) { 415 __ j(not_equal, miss_label); 416 break; 417 } 418 __ j(equal, &do_store, Label::kNear); 419 } 420 __ bind(&do_store); 421 } 422} 423 424 425Register PropertyHandlerCompiler::CheckPrototypes( 426 Register object_reg, Register holder_reg, Register scratch1, 427 Register scratch2, Handle<Name> name, Label* miss, PrototypeCheckType check, 428 ReturnHolder return_what) { 429 Handle<Map> receiver_map = map(); 430 431 // Make sure there's no overlap between holder and object registers. 432 DCHECK(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); 433 DCHECK(!scratch2.is(object_reg) && !scratch2.is(holder_reg) && 434 !scratch2.is(scratch1)); 435 436 if (FLAG_eliminate_prototype_chain_checks) { 437 Handle<Cell> validity_cell = 438 Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); 439 if (!validity_cell.is_null()) { 440 DCHECK_EQ(Smi::FromInt(Map::kPrototypeChainValid), 441 validity_cell->value()); 442 __ Move(scratch1, validity_cell, RelocInfo::CELL); 443 // Move(..., CELL) loads the payload's address! 444 __ SmiCompare(Operand(scratch1, 0), 445 Smi::FromInt(Map::kPrototypeChainValid)); 446 __ j(not_equal, miss); 447 } 448 449 // The prototype chain of primitives (and their JSValue wrappers) depends 450 // on the native context, which can't be guarded by validity cells. 451 // |object_reg| holds the native context specific prototype in this case; 452 // we need to check its map. 453 if (check == CHECK_ALL_MAPS) { 454 __ movp(scratch1, FieldOperand(object_reg, HeapObject::kMapOffset)); 455 Handle<WeakCell> cell = Map::WeakCellForMap(receiver_map); 456 __ CmpWeakValue(scratch1, cell, scratch2); 457 __ j(not_equal, miss); 458 } 459 } 460 461 // Keep track of the current object in register reg. On the first 462 // iteration, reg is an alias for object_reg, on later iterations, 463 // it is an alias for holder_reg. 464 Register reg = object_reg; 465 int depth = 0; 466 467 Handle<JSObject> current = Handle<JSObject>::null(); 468 if (receiver_map->IsJSGlobalObjectMap()) { 469 current = isolate()->global_object(); 470 } 471 472 // Check access rights to the global object. This has to happen after 473 // the map check so that we know that the object is actually a global 474 // object. 475 // This allows us to install generated handlers for accesses to the 476 // global proxy (as opposed to using slow ICs). See corresponding code 477 // in LookupForRead(). 478 if (receiver_map->IsJSGlobalProxyMap()) { 479 __ CheckAccessGlobalProxy(reg, scratch2, miss); 480 } 481 482 Handle<JSObject> prototype = Handle<JSObject>::null(); 483 Handle<Map> current_map = receiver_map; 484 Handle<Map> holder_map(holder()->map()); 485 // Traverse the prototype chain and check the maps in the prototype chain for 486 // fast and global objects or do negative lookup for normal objects. 487 while (!current_map.is_identical_to(holder_map)) { 488 ++depth; 489 490 // Only global objects and objects that do not require access 491 // checks are allowed in stubs. 492 DCHECK(current_map->IsJSGlobalProxyMap() || 493 !current_map->is_access_check_needed()); 494 495 prototype = handle(JSObject::cast(current_map->prototype())); 496 if (current_map->is_dictionary_map() && 497 !current_map->IsJSGlobalObjectMap()) { 498 DCHECK(!current_map->IsJSGlobalProxyMap()); // Proxy maps are fast. 499 if (!name->IsUniqueName()) { 500 DCHECK(name->IsString()); 501 name = factory()->InternalizeString(Handle<String>::cast(name)); 502 } 503 DCHECK(current.is_null() || 504 current->property_dictionary()->FindEntry(name) == 505 NameDictionary::kNotFound); 506 507 if (FLAG_eliminate_prototype_chain_checks && depth > 1) { 508 // TODO(jkummerow): Cache and re-use weak cell. 509 __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss); 510 } 511 GenerateDictionaryNegativeLookup(masm(), miss, reg, name, scratch1, 512 scratch2); 513 514 if (!FLAG_eliminate_prototype_chain_checks) { 515 __ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 516 __ movp(holder_reg, FieldOperand(scratch1, Map::kPrototypeOffset)); 517 } 518 } else { 519 Register map_reg = scratch1; 520 if (!FLAG_eliminate_prototype_chain_checks) { 521 __ movp(map_reg, FieldOperand(reg, HeapObject::kMapOffset)); 522 } 523 if (current_map->IsJSGlobalObjectMap()) { 524 GenerateCheckPropertyCell(masm(), Handle<JSGlobalObject>::cast(current), 525 name, scratch2, miss); 526 } else if (!FLAG_eliminate_prototype_chain_checks && 527 (depth != 1 || check == CHECK_ALL_MAPS)) { 528 Handle<WeakCell> cell = Map::WeakCellForMap(current_map); 529 __ CmpWeakValue(map_reg, cell, scratch2); 530 __ j(not_equal, miss); 531 } 532 if (!FLAG_eliminate_prototype_chain_checks) { 533 __ movp(holder_reg, FieldOperand(map_reg, Map::kPrototypeOffset)); 534 } 535 } 536 537 reg = holder_reg; // From now on the object will be in holder_reg. 538 // Go to the next object in the prototype chain. 539 current = prototype; 540 current_map = handle(current->map()); 541 } 542 543 DCHECK(!current_map->IsJSGlobalProxyMap()); 544 545 // Log the check depth. 546 LOG(isolate(), IntEvent("check-maps-depth", depth + 1)); 547 548 if (!FLAG_eliminate_prototype_chain_checks && 549 (depth != 0 || check == CHECK_ALL_MAPS)) { 550 // Check the holder map. 551 __ movp(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); 552 Handle<WeakCell> cell = Map::WeakCellForMap(current_map); 553 __ CmpWeakValue(scratch1, cell, scratch2); 554 __ j(not_equal, miss); 555 } 556 557 bool return_holder = return_what == RETURN_HOLDER; 558 if (FLAG_eliminate_prototype_chain_checks && return_holder && depth != 0) { 559 __ LoadWeakValue(reg, isolate()->factory()->NewWeakCell(current), miss); 560 } 561 562 // Return the register containing the holder. 563 return return_holder ? reg : no_reg; 564} 565 566 567void NamedLoadHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 568 if (!miss->is_unused()) { 569 Label success; 570 __ jmp(&success); 571 __ bind(miss); 572 if (IC::ICUseVector(kind())) { 573 DCHECK(kind() == Code::LOAD_IC); 574 PopVectorAndSlot(); 575 } 576 TailCallBuiltin(masm(), MissBuiltin(kind())); 577 __ bind(&success); 578 } 579} 580 581 582void NamedStoreHandlerCompiler::FrontendFooter(Handle<Name> name, Label* miss) { 583 if (!miss->is_unused()) { 584 Label success; 585 __ jmp(&success); 586 GenerateRestoreName(miss, name); 587 if (IC::ICUseVector(kind())) PopVectorAndSlot(); 588 TailCallBuiltin(masm(), MissBuiltin(kind())); 589 __ bind(&success); 590 } 591} 592 593 594void NamedLoadHandlerCompiler::GenerateLoadCallback( 595 Register reg, Handle<ExecutableAccessorInfo> callback) { 596 // Insert additional parameters into the stack frame above return address. 597 DCHECK(!scratch4().is(reg)); 598 __ PopReturnAddressTo(scratch4()); 599 600 STATIC_ASSERT(PropertyCallbackArguments::kHolderIndex == 0); 601 STATIC_ASSERT(PropertyCallbackArguments::kIsolateIndex == 1); 602 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueDefaultValueIndex == 2); 603 STATIC_ASSERT(PropertyCallbackArguments::kReturnValueOffset == 3); 604 STATIC_ASSERT(PropertyCallbackArguments::kDataIndex == 4); 605 STATIC_ASSERT(PropertyCallbackArguments::kThisIndex == 5); 606 STATIC_ASSERT(PropertyCallbackArguments::kArgsLength == 6); 607 __ Push(receiver()); // receiver 608 Handle<Object> data(callback->data(), isolate()); 609 if (data->IsUndefined() || data->IsSmi()) { 610 __ Push(data); 611 } else { 612 DCHECK(!scratch2().is(reg)); 613 Handle<WeakCell> cell = 614 isolate()->factory()->NewWeakCell(Handle<HeapObject>::cast(data)); 615 // The callback is alive if this instruction is executed, 616 // so the weak cell is not cleared and points to data. 617 __ GetWeakValue(scratch2(), cell); 618 __ Push(scratch2()); 619 } 620 DCHECK(!kScratchRegister.is(reg)); 621 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); 622 __ Push(kScratchRegister); // return value 623 __ Push(kScratchRegister); // return value default 624 __ PushAddress(ExternalReference::isolate_address(isolate())); 625 __ Push(reg); // holder 626 __ Push(name()); // name 627 // Save a pointer to where we pushed the arguments pointer. This will be 628 // passed as the const PropertyAccessorInfo& to the C++ callback. 629 630 __ PushReturnAddressFrom(scratch4()); 631 632 // Abi for CallApiGetter 633 Register api_function_address = ApiGetterDescriptor::function_address(); 634 Address getter_address = v8::ToCData<Address>(callback->getter()); 635 __ Move(api_function_address, getter_address, RelocInfo::EXTERNAL_REFERENCE); 636 637 CallApiGetterStub stub(isolate()); 638 __ TailCallStub(&stub); 639} 640 641 642void NamedLoadHandlerCompiler::GenerateLoadConstant(Handle<Object> value) { 643 // Return the constant value. 644 __ Move(rax, value); 645 __ ret(0); 646} 647 648 649void NamedLoadHandlerCompiler::GenerateLoadInterceptorWithFollowup( 650 LookupIterator* it, Register holder_reg) { 651 DCHECK(holder()->HasNamedInterceptor()); 652 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined()); 653 654 // Compile the interceptor call, followed by inline code to load the 655 // property from further up the prototype chain if the call fails. 656 // Check that the maps haven't changed. 657 DCHECK(holder_reg.is(receiver()) || holder_reg.is(scratch1())); 658 659 // Preserve the receiver register explicitly whenever it is different from the 660 // holder and it is needed should the interceptor return without any result. 661 // The ACCESSOR case needs the receiver to be passed into C++ code, the FIELD 662 // case might cause a miss during the prototype check. 663 bool must_perform_prototype_check = 664 !holder().is_identical_to(it->GetHolder<JSObject>()); 665 bool must_preserve_receiver_reg = 666 !receiver().is(holder_reg) && 667 (it->state() == LookupIterator::ACCESSOR || must_perform_prototype_check); 668 669 // Save necessary data before invoking an interceptor. 670 // Requires a frame to make GC aware of pushed pointers. 671 { 672 FrameScope frame_scope(masm(), StackFrame::INTERNAL); 673 674 if (must_preserve_receiver_reg) { 675 __ Push(receiver()); 676 } 677 __ Push(holder_reg); 678 __ Push(this->name()); 679 InterceptorVectorSlotPush(holder_reg); 680 681 // Invoke an interceptor. Note: map checks from receiver to 682 // interceptor's holder has been compiled before (see a caller 683 // of this method.) 684 CompileCallLoadPropertyWithInterceptor( 685 masm(), receiver(), holder_reg, this->name(), holder(), 686 Runtime::kLoadPropertyWithInterceptorOnly); 687 688 // Check if interceptor provided a value for property. If it's 689 // the case, return immediately. 690 Label interceptor_failed; 691 __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); 692 __ j(equal, &interceptor_failed); 693 frame_scope.GenerateLeaveFrame(); 694 __ ret(0); 695 696 __ bind(&interceptor_failed); 697 InterceptorVectorSlotPop(holder_reg); 698 __ Pop(this->name()); 699 __ Pop(holder_reg); 700 if (must_preserve_receiver_reg) { 701 __ Pop(receiver()); 702 } 703 704 // Leave the internal frame. 705 } 706 707 GenerateLoadPostInterceptor(it, holder_reg); 708} 709 710 711void NamedLoadHandlerCompiler::GenerateLoadInterceptor(Register holder_reg) { 712 // Call the runtime system to load the interceptor. 713 DCHECK(holder()->HasNamedInterceptor()); 714 DCHECK(!holder()->GetNamedInterceptor()->getter()->IsUndefined()); 715 __ PopReturnAddressTo(scratch2()); 716 PushInterceptorArguments(masm(), receiver(), holder_reg, this->name(), 717 holder()); 718 __ PushReturnAddressFrom(scratch2()); 719 720 __ TailCallRuntime(Runtime::kLoadPropertyWithInterceptor); 721} 722 723 724Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( 725 Handle<JSObject> object, Handle<Name> name, 726 Handle<ExecutableAccessorInfo> callback) { 727 Register holder_reg = Frontend(name); 728 729 __ PopReturnAddressTo(scratch1()); 730 __ Push(receiver()); 731 __ Push(holder_reg); 732 // If the callback cannot leak, then push the callback directly, 733 // otherwise wrap it in a weak cell. 734 if (callback->data()->IsUndefined() || callback->data()->IsSmi()) { 735 __ Push(callback); 736 } else { 737 Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback); 738 __ Push(cell); 739 } 740 __ Push(name); 741 __ Push(value()); 742 __ PushReturnAddressFrom(scratch1()); 743 744 // Do tail-call to the runtime system. 745 __ TailCallRuntime(Runtime::kStoreCallbackProperty); 746 747 // Return the generated code. 748 return GetCode(kind(), Code::FAST, name); 749} 750 751 752Handle<Code> NamedStoreHandlerCompiler::CompileStoreInterceptor( 753 Handle<Name> name) { 754 __ PopReturnAddressTo(scratch1()); 755 __ Push(receiver()); 756 __ Push(this->name()); 757 __ Push(value()); 758 __ PushReturnAddressFrom(scratch1()); 759 760 // Do tail-call to the runtime system. 761 __ TailCallRuntime(Runtime::kStorePropertyWithInterceptor); 762 763 // Return the generated code. 764 return GetCode(kind(), Code::FAST, name); 765} 766 767 768Register NamedStoreHandlerCompiler::value() { 769 return StoreDescriptor::ValueRegister(); 770} 771 772 773Handle<Code> NamedLoadHandlerCompiler::CompileLoadGlobal( 774 Handle<PropertyCell> cell, Handle<Name> name, bool is_configurable) { 775 Label miss; 776 if (IC::ICUseVector(kind())) { 777 PushVectorAndSlot(); 778 } 779 FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING); 780 781 // Get the value from the cell. 782 Register result = StoreDescriptor::ValueRegister(); 783 Handle<WeakCell> weak_cell = factory()->NewWeakCell(cell); 784 __ LoadWeakValue(result, weak_cell, &miss); 785 __ movp(result, FieldOperand(result, PropertyCell::kValueOffset)); 786 787 // Check for deleted property if property can actually be deleted. 788 if (is_configurable) { 789 __ CompareRoot(result, Heap::kTheHoleValueRootIndex); 790 __ j(equal, &miss); 791 } else if (FLAG_debug_code) { 792 __ CompareRoot(result, Heap::kTheHoleValueRootIndex); 793 __ Check(not_equal, kDontDeleteCellsCannotContainTheHole); 794 } 795 796 Counters* counters = isolate()->counters(); 797 __ IncrementCounter(counters->named_load_global_stub(), 1); 798 if (IC::ICUseVector(kind())) { 799 DiscardVectorAndSlot(); 800 } 801 __ ret(0); 802 803 FrontendFooter(name, &miss); 804 805 // Return the generated code. 806 return GetCode(kind(), Code::NORMAL, name); 807} 808 809 810#undef __ 811} // namespace internal 812} // namespace v8 813 814#endif // V8_TARGET_ARCH_X64 815