1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #ifndef V8_DEOPTIMIZER_H_ 29 #define V8_DEOPTIMIZER_H_ 30 31 #include "v8.h" 32 33 #include "macro-assembler.h" 34 #include "zone-inl.h" 35 36 37 namespace v8 { 38 namespace internal { 39 40 class FrameDescription; 41 class TranslationIterator; 42 class DeoptimizingCodeListNode; 43 44 45 class HeapNumberMaterializationDescriptor BASE_EMBEDDED { 46 public: HeapNumberMaterializationDescriptor(Address slot_address,double val)47 HeapNumberMaterializationDescriptor(Address slot_address, double val) 48 : slot_address_(slot_address), val_(val) { } 49 slot_address()50 Address slot_address() const { return slot_address_; } value()51 double value() const { return val_; } 52 53 private: 54 Address slot_address_; 55 double val_; 56 }; 57 58 59 class OptimizedFunctionVisitor BASE_EMBEDDED { 60 public: ~OptimizedFunctionVisitor()61 virtual ~OptimizedFunctionVisitor() {} 62 63 // Function which is called before iteration of any optimized functions 64 // from given global context. 65 virtual void EnterContext(Context* context) = 0; 66 67 virtual void VisitFunction(JSFunction* function) = 0; 68 69 // Function which is called after iteration of all optimized functions 70 // from given global context. 71 virtual void LeaveContext(Context* context) = 0; 72 }; 73 74 75 class Deoptimizer; 76 77 78 class DeoptimizerData { 79 public: 80 DeoptimizerData(); 81 ~DeoptimizerData(); 82 83 private: 84 LargeObjectChunk* eager_deoptimization_entry_code_; 85 LargeObjectChunk* lazy_deoptimization_entry_code_; 86 Deoptimizer* current_; 87 88 // List of deoptimized code which still have references from active stack 89 // frames. These code objects are needed by the deoptimizer when deoptimizing 90 // a frame for which the code object for the function function has been 91 // changed from the code present when deoptimizing was done. 92 DeoptimizingCodeListNode* deoptimizing_code_list_; 93 94 friend class Deoptimizer; 95 96 DISALLOW_COPY_AND_ASSIGN(DeoptimizerData); 97 }; 98 99 100 class Deoptimizer : public Malloced { 101 public: 102 enum BailoutType { 103 EAGER, 104 LAZY, 105 OSR 106 }; 107 output_count()108 int output_count() const { return output_count_; } 109 110 static Deoptimizer* New(JSFunction* function, 111 BailoutType type, 112 unsigned bailout_id, 113 Address from, 114 int fp_to_sp_delta, 115 Isolate* isolate); 116 static Deoptimizer* Grab(Isolate* isolate); 117 118 // Makes sure that there is enough room in the relocation 119 // information of a code object to perform lazy deoptimization 120 // patching. If there is not enough room a new relocation 121 // information object is allocated and comments are added until it 122 // is big enough. 123 static void EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code); 124 125 // Deoptimize the function now. Its current optimized code will never be run 126 // again and any activations of the optimized code will get deoptimized when 127 // execution returns. 128 static void DeoptimizeFunction(JSFunction* function); 129 130 // Deoptimize all functions in the heap. 131 static void DeoptimizeAll(); 132 133 static void DeoptimizeGlobalObject(JSObject* object); 134 135 static void VisitAllOptimizedFunctionsForContext( 136 Context* context, OptimizedFunctionVisitor* visitor); 137 138 static void VisitAllOptimizedFunctionsForGlobalObject( 139 JSObject* object, OptimizedFunctionVisitor* visitor); 140 141 static void VisitAllOptimizedFunctions(OptimizedFunctionVisitor* visitor); 142 143 // The size in bytes of the code required at a lazy deopt patch site. 144 static int patch_size(); 145 146 // Patch all stack guard checks in the unoptimized code to 147 // unconditionally call replacement_code. 148 static void PatchStackCheckCode(Code* unoptimized_code, 149 Code* check_code, 150 Code* replacement_code); 151 152 // Patch stack guard check at instruction before pc_after in 153 // the unoptimized code to unconditionally call replacement_code. 154 static void PatchStackCheckCodeAt(Address pc_after, 155 Code* check_code, 156 Code* replacement_code); 157 158 // Change all patched stack guard checks in the unoptimized code 159 // back to a normal stack guard check. 160 static void RevertStackCheckCode(Code* unoptimized_code, 161 Code* check_code, 162 Code* replacement_code); 163 164 // Change all patched stack guard checks in the unoptimized code 165 // back to a normal stack guard check. 166 static void RevertStackCheckCodeAt(Address pc_after, 167 Code* check_code, 168 Code* replacement_code); 169 170 ~Deoptimizer(); 171 172 void MaterializeHeapNumbers(); 173 174 static void ComputeOutputFrames(Deoptimizer* deoptimizer); 175 176 static Address GetDeoptimizationEntry(int id, BailoutType type); 177 static int GetDeoptimizationId(Address addr, BailoutType type); 178 static int GetOutputInfo(DeoptimizationOutputData* data, 179 unsigned node_id, 180 SharedFunctionInfo* shared); 181 182 // Code generation support. input_offset()183 static int input_offset() { return OFFSET_OF(Deoptimizer, input_); } output_count_offset()184 static int output_count_offset() { 185 return OFFSET_OF(Deoptimizer, output_count_); 186 } output_offset()187 static int output_offset() { return OFFSET_OF(Deoptimizer, output_); } 188 189 static int GetDeoptimizedCodeCount(Isolate* isolate); 190 191 static const int kNotDeoptimizationEntry = -1; 192 193 // Generators for the deoptimization entry code. 194 class EntryGenerator BASE_EMBEDDED { 195 public: EntryGenerator(MacroAssembler * masm,BailoutType type)196 EntryGenerator(MacroAssembler* masm, BailoutType type) 197 : masm_(masm), type_(type) { } ~EntryGenerator()198 virtual ~EntryGenerator() { } 199 200 void Generate(); 201 202 protected: masm()203 MacroAssembler* masm() const { return masm_; } type()204 BailoutType type() const { return type_; } 205 GeneratePrologue()206 virtual void GeneratePrologue() { } 207 208 private: 209 MacroAssembler* masm_; 210 Deoptimizer::BailoutType type_; 211 }; 212 213 class TableEntryGenerator : public EntryGenerator { 214 public: TableEntryGenerator(MacroAssembler * masm,BailoutType type,int count)215 TableEntryGenerator(MacroAssembler* masm, BailoutType type, int count) 216 : EntryGenerator(masm, type), count_(count) { } 217 218 protected: 219 virtual void GeneratePrologue(); 220 221 private: count()222 int count() const { return count_; } 223 224 int count_; 225 }; 226 227 private: 228 static const int kNumberOfEntries = 4096; 229 230 Deoptimizer(Isolate* isolate, 231 JSFunction* function, 232 BailoutType type, 233 unsigned bailout_id, 234 Address from, 235 int fp_to_sp_delta); 236 void DeleteFrameDescriptions(); 237 238 void DoComputeOutputFrames(); 239 void DoComputeOsrOutputFrame(); 240 void DoComputeFrame(TranslationIterator* iterator, int frame_index); 241 void DoTranslateCommand(TranslationIterator* iterator, 242 int frame_index, 243 unsigned output_offset); 244 // Translate a command for OSR. Updates the input offset to be used for 245 // the next command. Returns false if translation of the command failed 246 // (e.g., a number conversion failed) and may or may not have updated the 247 // input offset. 248 bool DoOsrTranslateCommand(TranslationIterator* iterator, 249 int* input_offset); 250 251 unsigned ComputeInputFrameSize() const; 252 unsigned ComputeFixedSize(JSFunction* function) const; 253 254 unsigned ComputeIncomingArgumentSize(JSFunction* function) const; 255 unsigned ComputeOutgoingArgumentSize() const; 256 257 Object* ComputeLiteral(int index) const; 258 259 void AddDoubleValue(intptr_t slot_address, double value); 260 261 static LargeObjectChunk* CreateCode(BailoutType type); 262 static void GenerateDeoptimizationEntries( 263 MacroAssembler* masm, int count, BailoutType type); 264 265 // Weak handle callback for deoptimizing code objects. 266 static void HandleWeakDeoptimizedCode( 267 v8::Persistent<v8::Value> obj, void* data); 268 static Code* FindDeoptimizingCodeFromAddress(Address addr); 269 static void RemoveDeoptimizingCode(Code* code); 270 271 Isolate* isolate_; 272 JSFunction* function_; 273 Code* optimized_code_; 274 unsigned bailout_id_; 275 BailoutType bailout_type_; 276 Address from_; 277 int fp_to_sp_delta_; 278 279 // Input frame description. 280 FrameDescription* input_; 281 // Number of output frames. 282 int output_count_; 283 // Array of output frame descriptions. 284 FrameDescription** output_; 285 286 List<HeapNumberMaterializationDescriptor> deferred_heap_numbers_; 287 288 static int table_entry_size_; 289 290 friend class FrameDescription; 291 friend class DeoptimizingCodeListNode; 292 }; 293 294 295 class FrameDescription { 296 public: 297 FrameDescription(uint32_t frame_size, 298 JSFunction* function); 299 new(size_t size,uint32_t frame_size)300 void* operator new(size_t size, uint32_t frame_size) { 301 // Subtracts kPointerSize, as the member frame_content_ already supplies 302 // the first element of the area to store the frame. 303 return malloc(size + frame_size - kPointerSize); 304 } 305 delete(void * description)306 void operator delete(void* description) { 307 free(description); 308 } 309 GetFrameSize()310 intptr_t GetFrameSize() const { return frame_size_; } 311 GetFunction()312 JSFunction* GetFunction() const { return function_; } 313 314 unsigned GetOffsetFromSlotIndex(Deoptimizer* deoptimizer, int slot_index); 315 GetFrameSlot(unsigned offset)316 intptr_t GetFrameSlot(unsigned offset) { 317 return *GetFrameSlotPointer(offset); 318 } 319 GetDoubleFrameSlot(unsigned offset)320 double GetDoubleFrameSlot(unsigned offset) { 321 return *reinterpret_cast<double*>(GetFrameSlotPointer(offset)); 322 } 323 SetFrameSlot(unsigned offset,intptr_t value)324 void SetFrameSlot(unsigned offset, intptr_t value) { 325 *GetFrameSlotPointer(offset) = value; 326 } 327 GetRegister(unsigned n)328 intptr_t GetRegister(unsigned n) const { 329 ASSERT(n < ARRAY_SIZE(registers_)); 330 return registers_[n]; 331 } 332 GetDoubleRegister(unsigned n)333 double GetDoubleRegister(unsigned n) const { 334 ASSERT(n < ARRAY_SIZE(double_registers_)); 335 return double_registers_[n]; 336 } 337 SetRegister(unsigned n,intptr_t value)338 void SetRegister(unsigned n, intptr_t value) { 339 ASSERT(n < ARRAY_SIZE(registers_)); 340 registers_[n] = value; 341 } 342 SetDoubleRegister(unsigned n,double value)343 void SetDoubleRegister(unsigned n, double value) { 344 ASSERT(n < ARRAY_SIZE(double_registers_)); 345 double_registers_[n] = value; 346 } 347 GetTop()348 intptr_t GetTop() const { return top_; } SetTop(intptr_t top)349 void SetTop(intptr_t top) { top_ = top; } 350 GetPc()351 intptr_t GetPc() const { return pc_; } SetPc(intptr_t pc)352 void SetPc(intptr_t pc) { pc_ = pc; } 353 GetFp()354 intptr_t GetFp() const { return fp_; } SetFp(intptr_t fp)355 void SetFp(intptr_t fp) { fp_ = fp; } 356 GetState()357 Smi* GetState() const { return state_; } SetState(Smi * state)358 void SetState(Smi* state) { state_ = state; } 359 SetContinuation(intptr_t pc)360 void SetContinuation(intptr_t pc) { continuation_ = pc; } 361 registers_offset()362 static int registers_offset() { 363 return OFFSET_OF(FrameDescription, registers_); 364 } 365 double_registers_offset()366 static int double_registers_offset() { 367 return OFFSET_OF(FrameDescription, double_registers_); 368 } 369 frame_size_offset()370 static int frame_size_offset() { 371 return OFFSET_OF(FrameDescription, frame_size_); 372 } 373 pc_offset()374 static int pc_offset() { 375 return OFFSET_OF(FrameDescription, pc_); 376 } 377 state_offset()378 static int state_offset() { 379 return OFFSET_OF(FrameDescription, state_); 380 } 381 continuation_offset()382 static int continuation_offset() { 383 return OFFSET_OF(FrameDescription, continuation_); 384 } 385 frame_content_offset()386 static int frame_content_offset() { 387 return OFFSET_OF(FrameDescription, frame_content_); 388 } 389 390 private: 391 static const uint32_t kZapUint32 = 0xbeeddead; 392 393 uintptr_t frame_size_; // Number of bytes. 394 JSFunction* function_; 395 intptr_t registers_[Register::kNumRegisters]; 396 double double_registers_[DoubleRegister::kNumAllocatableRegisters]; 397 intptr_t top_; 398 intptr_t pc_; 399 intptr_t fp_; 400 Smi* state_; 401 402 // Continuation is the PC where the execution continues after 403 // deoptimizing. 404 intptr_t continuation_; 405 406 // This must be at the end of the object as the object is allocated larger 407 // than it's definition indicate to extend this array. 408 intptr_t frame_content_[1]; 409 GetFrameSlotPointer(unsigned offset)410 intptr_t* GetFrameSlotPointer(unsigned offset) { 411 ASSERT(offset < frame_size_); 412 return reinterpret_cast<intptr_t*>( 413 reinterpret_cast<Address>(this) + frame_content_offset() + offset); 414 } 415 }; 416 417 418 class TranslationBuffer BASE_EMBEDDED { 419 public: TranslationBuffer()420 TranslationBuffer() : contents_(256) { } 421 CurrentIndex()422 int CurrentIndex() const { return contents_.length(); } 423 void Add(int32_t value); 424 425 Handle<ByteArray> CreateByteArray(); 426 427 private: 428 ZoneList<uint8_t> contents_; 429 }; 430 431 432 class TranslationIterator BASE_EMBEDDED { 433 public: TranslationIterator(ByteArray * buffer,int index)434 TranslationIterator(ByteArray* buffer, int index) 435 : buffer_(buffer), index_(index) { 436 ASSERT(index >= 0 && index < buffer->length()); 437 } 438 439 int32_t Next(); 440 HasNext()441 bool HasNext() const { return index_ >= 0; } 442 Done()443 void Done() { index_ = -1; } 444 Skip(int n)445 void Skip(int n) { 446 for (int i = 0; i < n; i++) Next(); 447 } 448 449 private: 450 ByteArray* buffer_; 451 int index_; 452 }; 453 454 455 class Translation BASE_EMBEDDED { 456 public: 457 enum Opcode { 458 BEGIN, 459 FRAME, 460 REGISTER, 461 INT32_REGISTER, 462 DOUBLE_REGISTER, 463 STACK_SLOT, 464 INT32_STACK_SLOT, 465 DOUBLE_STACK_SLOT, 466 LITERAL, 467 ARGUMENTS_OBJECT, 468 469 // A prefix indicating that the next command is a duplicate of the one 470 // that follows it. 471 DUPLICATE 472 }; 473 Translation(TranslationBuffer * buffer,int frame_count)474 Translation(TranslationBuffer* buffer, int frame_count) 475 : buffer_(buffer), 476 index_(buffer->CurrentIndex()) { 477 buffer_->Add(BEGIN); 478 buffer_->Add(frame_count); 479 } 480 index()481 int index() const { return index_; } 482 483 // Commands. 484 void BeginFrame(int node_id, int literal_id, unsigned height); 485 void StoreRegister(Register reg); 486 void StoreInt32Register(Register reg); 487 void StoreDoubleRegister(DoubleRegister reg); 488 void StoreStackSlot(int index); 489 void StoreInt32StackSlot(int index); 490 void StoreDoubleStackSlot(int index); 491 void StoreLiteral(int literal_id); 492 void StoreArgumentsObject(); 493 void MarkDuplicate(); 494 495 static int NumberOfOperandsFor(Opcode opcode); 496 497 #ifdef OBJECT_PRINT 498 static const char* StringFor(Opcode opcode); 499 #endif 500 501 private: 502 TranslationBuffer* buffer_; 503 int index_; 504 }; 505 506 507 // Linked list holding deoptimizing code objects. The deoptimizing code objects 508 // are kept as weak handles until they are no longer activated on the stack. 509 class DeoptimizingCodeListNode : public Malloced { 510 public: 511 explicit DeoptimizingCodeListNode(Code* code); 512 ~DeoptimizingCodeListNode(); 513 next()514 DeoptimizingCodeListNode* next() const { return next_; } set_next(DeoptimizingCodeListNode * next)515 void set_next(DeoptimizingCodeListNode* next) { next_ = next; } code()516 Handle<Code> code() const { return code_; } 517 518 private: 519 // Global (weak) handle to the deoptimizing code object. 520 Handle<Code> code_; 521 522 // Next pointer for linked list. 523 DeoptimizingCodeListNode* next_; 524 }; 525 526 527 class SlotRef BASE_EMBEDDED { 528 public: 529 enum SlotRepresentation { 530 UNKNOWN, 531 TAGGED, 532 INT32, 533 DOUBLE, 534 LITERAL 535 }; 536 SlotRef()537 SlotRef() 538 : addr_(NULL), representation_(UNKNOWN) { } 539 SlotRef(Address addr,SlotRepresentation representation)540 SlotRef(Address addr, SlotRepresentation representation) 541 : addr_(addr), representation_(representation) { } 542 SlotRef(Object * literal)543 explicit SlotRef(Object* literal) 544 : literal_(literal), representation_(LITERAL) { } 545 GetValue()546 Handle<Object> GetValue() { 547 switch (representation_) { 548 case TAGGED: 549 return Handle<Object>(Memory::Object_at(addr_)); 550 551 case INT32: { 552 int value = Memory::int32_at(addr_); 553 if (Smi::IsValid(value)) { 554 return Handle<Object>(Smi::FromInt(value)); 555 } else { 556 return Isolate::Current()->factory()->NewNumberFromInt(value); 557 } 558 } 559 560 case DOUBLE: { 561 double value = Memory::double_at(addr_); 562 return Isolate::Current()->factory()->NewNumber(value); 563 } 564 565 case LITERAL: 566 return literal_; 567 568 default: 569 UNREACHABLE(); 570 return Handle<Object>::null(); 571 } 572 } 573 574 static void ComputeSlotMappingForArguments(JavaScriptFrame* frame, 575 int inlined_frame_index, 576 Vector<SlotRef>* args_slots); 577 578 private: 579 Address addr_; 580 Handle<Object> literal_; 581 SlotRepresentation representation_; 582 SlotAddress(JavaScriptFrame * frame,int slot_index)583 static Address SlotAddress(JavaScriptFrame* frame, int slot_index) { 584 if (slot_index >= 0) { 585 const int offset = JavaScriptFrameConstants::kLocal0Offset; 586 return frame->fp() + offset - (slot_index * kPointerSize); 587 } else { 588 const int offset = JavaScriptFrameConstants::kLastParameterOffset; 589 return frame->fp() + offset - ((slot_index + 1) * kPointerSize); 590 } 591 } 592 593 static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator, 594 DeoptimizationInputData* data, 595 JavaScriptFrame* frame); 596 }; 597 598 599 } } // namespace v8::internal 600 601 #endif // V8_DEOPTIMIZER_H_ 602