blob: 91030e2d87f08e6acbdefb3d3ebc2fa30f1164fc [file] [log] [blame]
kmillikin@chromium.org31b12772011-02-02 16:08:26 +00001// Copyright 2011 the V8 project authors. All rights reserved.
kasperl@chromium.orga5551262010-12-07 12:49:48 +00002// 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
lrn@chromium.org1c092762011-05-09 09:42:16 +000033#include "allocation.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000034#include "macro-assembler.h"
35#include "zone-inl.h"
36
37
38namespace v8 {
39namespace internal {
40
41class FrameDescription;
42class TranslationIterator;
43class DeoptimizingCodeListNode;
44
45
karlklose@chromium.org44bc7082011-04-11 12:33:05 +000046class HeapNumberMaterializationDescriptor BASE_EMBEDDED {
kasperl@chromium.orga5551262010-12-07 12:49:48 +000047 public:
karlklose@chromium.org44bc7082011-04-11 12:33:05 +000048 HeapNumberMaterializationDescriptor(Address slot_address, double val)
49 : slot_address_(slot_address), val_(val) { }
50
51 Address slot_address() const { return slot_address_; }
52 double value() const { return val_; }
kasperl@chromium.orga5551262010-12-07 12:49:48 +000053
54 private:
karlklose@chromium.org44bc7082011-04-11 12:33:05 +000055 Address slot_address_;
56 double val_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000057};
58
59
60class OptimizedFunctionVisitor BASE_EMBEDDED {
61 public:
62 virtual ~OptimizedFunctionVisitor() {}
63
64 // Function which is called before iteration of any optimized functions
65 // from given global context.
66 virtual void EnterContext(Context* context) = 0;
67
68 virtual void VisitFunction(JSFunction* function) = 0;
69
70 // Function which is called after iteration of all optimized functions
71 // from given global context.
72 virtual void LeaveContext(Context* context) = 0;
73};
74
75
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000076class Deoptimizer;
77
78
79class DeoptimizerData {
80 public:
81 DeoptimizerData();
82 ~DeoptimizerData();
83
84 private:
85 LargeObjectChunk* eager_deoptimization_entry_code_;
86 LargeObjectChunk* lazy_deoptimization_entry_code_;
87 Deoptimizer* current_;
88
89 // List of deoptimized code which still have references from active stack
90 // frames. These code objects are needed by the deoptimizer when deoptimizing
91 // a frame for which the code object for the function function has been
92 // changed from the code present when deoptimizing was done.
93 DeoptimizingCodeListNode* deoptimizing_code_list_;
94
95 friend class Deoptimizer;
96
97 DISALLOW_COPY_AND_ASSIGN(DeoptimizerData);
98};
99
100
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000101class Deoptimizer : public Malloced {
102 public:
103 enum BailoutType {
104 EAGER,
105 LAZY,
106 OSR
107 };
108
109 int output_count() const { return output_count_; }
110
111 static Deoptimizer* New(JSFunction* function,
112 BailoutType type,
113 unsigned bailout_id,
114 Address from,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000115 int fp_to_sp_delta,
116 Isolate* isolate);
117 static Deoptimizer* Grab(Isolate* isolate);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000118
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000119 // Makes sure that there is enough room in the relocation
120 // information of a code object to perform lazy deoptimization
121 // patching. If there is not enough room a new relocation
122 // information object is allocated and comments are added until it
123 // is big enough.
124 static void EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code);
125
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000126 // Deoptimize the function now. Its current optimized code will never be run
127 // again and any activations of the optimized code will get deoptimized when
128 // execution returns.
129 static void DeoptimizeFunction(JSFunction* function);
130
131 // Deoptimize all functions in the heap.
132 static void DeoptimizeAll();
133
134 static void DeoptimizeGlobalObject(JSObject* object);
135
136 static void VisitAllOptimizedFunctionsForContext(
137 Context* context, OptimizedFunctionVisitor* visitor);
138
139 static void VisitAllOptimizedFunctionsForGlobalObject(
140 JSObject* object, OptimizedFunctionVisitor* visitor);
141
142 static void VisitAllOptimizedFunctions(OptimizedFunctionVisitor* visitor);
143
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000144 // The size in bytes of the code required at a lazy deopt patch site.
145 static int patch_size();
146
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000147 // Patch all stack guard checks in the unoptimized code to
148 // unconditionally call replacement_code.
149 static void PatchStackCheckCode(Code* unoptimized_code,
150 Code* check_code,
151 Code* replacement_code);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000152
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000153 // Patch stack guard check at instruction before pc_after in
154 // the unoptimized code to unconditionally call replacement_code.
155 static void PatchStackCheckCodeAt(Address pc_after,
156 Code* check_code,
157 Code* replacement_code);
158
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000159 // Change all patched stack guard checks in the unoptimized code
160 // back to a normal stack guard check.
161 static void RevertStackCheckCode(Code* unoptimized_code,
162 Code* check_code,
163 Code* replacement_code);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000164
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000165 // Change all patched stack guard checks in the unoptimized code
166 // back to a normal stack guard check.
167 static void RevertStackCheckCodeAt(Address pc_after,
168 Code* check_code,
169 Code* replacement_code);
170
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000171 ~Deoptimizer();
172
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000173 void MaterializeHeapNumbers();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000174
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000175 static void ComputeOutputFrames(Deoptimizer* deoptimizer);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000176
177 static Address GetDeoptimizationEntry(int id, BailoutType type);
178 static int GetDeoptimizationId(Address addr, BailoutType type);
fschneider@chromium.org9e3e0b62011-01-03 10:16:46 +0000179 static int GetOutputInfo(DeoptimizationOutputData* data,
180 unsigned node_id,
181 SharedFunctionInfo* shared);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000182
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000183 // Code generation support.
184 static int input_offset() { return OFFSET_OF(Deoptimizer, input_); }
185 static int output_count_offset() {
186 return OFFSET_OF(Deoptimizer, output_count_);
187 }
188 static int output_offset() { return OFFSET_OF(Deoptimizer, output_); }
189
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000190 static int GetDeoptimizedCodeCount(Isolate* isolate);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000191
192 static const int kNotDeoptimizationEntry = -1;
193
194 // Generators for the deoptimization entry code.
195 class EntryGenerator BASE_EMBEDDED {
196 public:
197 EntryGenerator(MacroAssembler* masm, BailoutType type)
198 : masm_(masm), type_(type) { }
199 virtual ~EntryGenerator() { }
200
201 void Generate();
202
203 protected:
204 MacroAssembler* masm() const { return masm_; }
205 BailoutType type() const { return type_; }
206
207 virtual void GeneratePrologue() { }
208
209 private:
210 MacroAssembler* masm_;
211 Deoptimizer::BailoutType type_;
212 };
213
214 class TableEntryGenerator : public EntryGenerator {
215 public:
216 TableEntryGenerator(MacroAssembler* masm, BailoutType type, int count)
217 : EntryGenerator(masm, type), count_(count) { }
218
219 protected:
220 virtual void GeneratePrologue();
221
222 private:
223 int count() const { return count_; }
224
225 int count_;
226 };
227
228 private:
229 static const int kNumberOfEntries = 4096;
230
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000231 Deoptimizer(Isolate* isolate,
232 JSFunction* function,
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000233 BailoutType type,
234 unsigned bailout_id,
235 Address from,
236 int fp_to_sp_delta);
237 void DeleteFrameDescriptions();
238
239 void DoComputeOutputFrames();
240 void DoComputeOsrOutputFrame();
241 void DoComputeFrame(TranslationIterator* iterator, int frame_index);
242 void DoTranslateCommand(TranslationIterator* iterator,
243 int frame_index,
244 unsigned output_offset);
245 // Translate a command for OSR. Updates the input offset to be used for
246 // the next command. Returns false if translation of the command failed
247 // (e.g., a number conversion failed) and may or may not have updated the
248 // input offset.
249 bool DoOsrTranslateCommand(TranslationIterator* iterator,
250 int* input_offset);
251
252 unsigned ComputeInputFrameSize() const;
253 unsigned ComputeFixedSize(JSFunction* function) const;
254
255 unsigned ComputeIncomingArgumentSize(JSFunction* function) const;
256 unsigned ComputeOutgoingArgumentSize() const;
257
258 Object* ComputeLiteral(int index) const;
259
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000260 void AddDoubleValue(intptr_t slot_address, double value);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000261
262 static LargeObjectChunk* CreateCode(BailoutType type);
263 static void GenerateDeoptimizationEntries(
264 MacroAssembler* masm, int count, BailoutType type);
265
266 // Weak handle callback for deoptimizing code objects.
267 static void HandleWeakDeoptimizedCode(
268 v8::Persistent<v8::Value> obj, void* data);
269 static Code* FindDeoptimizingCodeFromAddress(Address addr);
270 static void RemoveDeoptimizingCode(Code* code);
271
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000272 Isolate* isolate_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000273 JSFunction* function_;
274 Code* optimized_code_;
275 unsigned bailout_id_;
276 BailoutType bailout_type_;
277 Address from_;
278 int fp_to_sp_delta_;
279
280 // Input frame description.
281 FrameDescription* input_;
282 // Number of output frames.
283 int output_count_;
284 // Array of output frame descriptions.
285 FrameDescription** output_;
286
karlklose@chromium.org44bc7082011-04-11 12:33:05 +0000287 List<HeapNumberMaterializationDescriptor> deferred_heap_numbers_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000288
289 static int table_entry_size_;
290
291 friend class FrameDescription;
292 friend class DeoptimizingCodeListNode;
293};
294
295
296class FrameDescription {
297 public:
298 FrameDescription(uint32_t frame_size,
299 JSFunction* function);
300
301 void* operator new(size_t size, uint32_t frame_size) {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000302 // Subtracts kPointerSize, as the member frame_content_ already supplies
303 // the first element of the area to store the frame.
304 return malloc(size + frame_size - kPointerSize);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000305 }
306
307 void operator delete(void* description) {
308 free(description);
309 }
310
311 intptr_t GetFrameSize() const { return frame_size_; }
312
313 JSFunction* GetFunction() const { return function_; }
314
315 unsigned GetOffsetFromSlotIndex(Deoptimizer* deoptimizer, int slot_index);
316
317 intptr_t GetFrameSlot(unsigned offset) {
318 return *GetFrameSlotPointer(offset);
319 }
320
321 double GetDoubleFrameSlot(unsigned offset) {
322 return *reinterpret_cast<double*>(GetFrameSlotPointer(offset));
323 }
324
325 void SetFrameSlot(unsigned offset, intptr_t value) {
326 *GetFrameSlotPointer(offset) = value;
327 }
328
329 intptr_t GetRegister(unsigned n) const {
330 ASSERT(n < ARRAY_SIZE(registers_));
331 return registers_[n];
332 }
333
334 double GetDoubleRegister(unsigned n) const {
335 ASSERT(n < ARRAY_SIZE(double_registers_));
336 return double_registers_[n];
337 }
338
339 void SetRegister(unsigned n, intptr_t value) {
340 ASSERT(n < ARRAY_SIZE(registers_));
341 registers_[n] = value;
342 }
343
344 void SetDoubleRegister(unsigned n, double value) {
345 ASSERT(n < ARRAY_SIZE(double_registers_));
346 double_registers_[n] = value;
347 }
348
349 intptr_t GetTop() const { return top_; }
350 void SetTop(intptr_t top) { top_ = top; }
351
352 intptr_t GetPc() const { return pc_; }
353 void SetPc(intptr_t pc) { pc_ = pc; }
354
355 intptr_t GetFp() const { return fp_; }
356 void SetFp(intptr_t fp) { fp_ = fp; }
357
358 Smi* GetState() const { return state_; }
359 void SetState(Smi* state) { state_ = state; }
360
361 void SetContinuation(intptr_t pc) { continuation_ = pc; }
362
363 static int registers_offset() {
364 return OFFSET_OF(FrameDescription, registers_);
365 }
366
367 static int double_registers_offset() {
368 return OFFSET_OF(FrameDescription, double_registers_);
369 }
370
371 static int frame_size_offset() {
372 return OFFSET_OF(FrameDescription, frame_size_);
373 }
374
375 static int pc_offset() {
376 return OFFSET_OF(FrameDescription, pc_);
377 }
378
379 static int state_offset() {
380 return OFFSET_OF(FrameDescription, state_);
381 }
382
383 static int continuation_offset() {
384 return OFFSET_OF(FrameDescription, continuation_);
385 }
386
387 static int frame_content_offset() {
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000388 return OFFSET_OF(FrameDescription, frame_content_);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000389 }
390
391 private:
392 static const uint32_t kZapUint32 = 0xbeeddead;
393
394 uintptr_t frame_size_; // Number of bytes.
395 JSFunction* function_;
396 intptr_t registers_[Register::kNumRegisters];
397 double double_registers_[DoubleRegister::kNumAllocatableRegisters];
398 intptr_t top_;
399 intptr_t pc_;
400 intptr_t fp_;
401 Smi* state_;
402
403 // Continuation is the PC where the execution continues after
404 // deoptimizing.
405 intptr_t continuation_;
406
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000407 // This must be at the end of the object as the object is allocated larger
408 // than it's definition indicate to extend this array.
409 intptr_t frame_content_[1];
410
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000411 intptr_t* GetFrameSlotPointer(unsigned offset) {
412 ASSERT(offset < frame_size_);
413 return reinterpret_cast<intptr_t*>(
414 reinterpret_cast<Address>(this) + frame_content_offset() + offset);
415 }
416};
417
418
419class TranslationBuffer BASE_EMBEDDED {
420 public:
421 TranslationBuffer() : contents_(256) { }
422
423 int CurrentIndex() const { return contents_.length(); }
424 void Add(int32_t value);
425
426 Handle<ByteArray> CreateByteArray();
427
428 private:
429 ZoneList<uint8_t> contents_;
430};
431
432
433class TranslationIterator BASE_EMBEDDED {
434 public:
435 TranslationIterator(ByteArray* buffer, int index)
436 : buffer_(buffer), index_(index) {
437 ASSERT(index >= 0 && index < buffer->length());
438 }
439
440 int32_t Next();
441
442 bool HasNext() const { return index_ >= 0; }
443
444 void Done() { index_ = -1; }
445
446 void Skip(int n) {
447 for (int i = 0; i < n; i++) Next();
448 }
449
450 private:
451 ByteArray* buffer_;
452 int index_;
453};
454
455
456class Translation BASE_EMBEDDED {
457 public:
458 enum Opcode {
459 BEGIN,
460 FRAME,
461 REGISTER,
462 INT32_REGISTER,
463 DOUBLE_REGISTER,
464 STACK_SLOT,
465 INT32_STACK_SLOT,
466 DOUBLE_STACK_SLOT,
467 LITERAL,
468 ARGUMENTS_OBJECT,
469
470 // A prefix indicating that the next command is a duplicate of the one
471 // that follows it.
472 DUPLICATE
473 };
474
475 Translation(TranslationBuffer* buffer, int frame_count)
476 : buffer_(buffer),
477 index_(buffer->CurrentIndex()) {
478 buffer_->Add(BEGIN);
479 buffer_->Add(frame_count);
480 }
481
482 int index() const { return index_; }
483
484 // Commands.
485 void BeginFrame(int node_id, int literal_id, unsigned height);
486 void StoreRegister(Register reg);
487 void StoreInt32Register(Register reg);
488 void StoreDoubleRegister(DoubleRegister reg);
489 void StoreStackSlot(int index);
490 void StoreInt32StackSlot(int index);
491 void StoreDoubleStackSlot(int index);
492 void StoreLiteral(int literal_id);
493 void StoreArgumentsObject();
494 void MarkDuplicate();
495
496 static int NumberOfOperandsFor(Opcode opcode);
497
whesse@chromium.org7b260152011-06-20 15:33:18 +0000498#if defined(OBJECT_PRINT) || defined(ENABLE_DISASSEMBLER)
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000499 static const char* StringFor(Opcode opcode);
500#endif
501
502 private:
503 TranslationBuffer* buffer_;
504 int index_;
505};
506
507
508// Linked list holding deoptimizing code objects. The deoptimizing code objects
509// are kept as weak handles until they are no longer activated on the stack.
510class DeoptimizingCodeListNode : public Malloced {
511 public:
512 explicit DeoptimizingCodeListNode(Code* code);
513 ~DeoptimizingCodeListNode();
514
515 DeoptimizingCodeListNode* next() const { return next_; }
516 void set_next(DeoptimizingCodeListNode* next) { next_ = next; }
517 Handle<Code> code() const { return code_; }
518
519 private:
520 // Global (weak) handle to the deoptimizing code object.
521 Handle<Code> code_;
522
523 // Next pointer for linked list.
524 DeoptimizingCodeListNode* next_;
525};
526
527
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000528class SlotRef BASE_EMBEDDED {
529 public:
530 enum SlotRepresentation {
531 UNKNOWN,
532 TAGGED,
533 INT32,
534 DOUBLE,
535 LITERAL
536 };
537
538 SlotRef()
539 : addr_(NULL), representation_(UNKNOWN) { }
540
541 SlotRef(Address addr, SlotRepresentation representation)
542 : addr_(addr), representation_(representation) { }
543
544 explicit SlotRef(Object* literal)
545 : literal_(literal), representation_(LITERAL) { }
546
547 Handle<Object> GetValue() {
548 switch (representation_) {
549 case TAGGED:
550 return Handle<Object>(Memory::Object_at(addr_));
551
552 case INT32: {
553 int value = Memory::int32_at(addr_);
554 if (Smi::IsValid(value)) {
555 return Handle<Object>(Smi::FromInt(value));
556 } else {
557 return Isolate::Current()->factory()->NewNumberFromInt(value);
558 }
559 }
560
561 case DOUBLE: {
562 double value = Memory::double_at(addr_);
563 return Isolate::Current()->factory()->NewNumber(value);
564 }
565
566 case LITERAL:
567 return literal_;
568
569 default:
570 UNREACHABLE();
571 return Handle<Object>::null();
572 }
573 }
574
575 static void ComputeSlotMappingForArguments(JavaScriptFrame* frame,
576 int inlined_frame_index,
577 Vector<SlotRef>* args_slots);
578
579 private:
580 Address addr_;
581 Handle<Object> literal_;
582 SlotRepresentation representation_;
583
584 static Address SlotAddress(JavaScriptFrame* frame, int slot_index) {
585 if (slot_index >= 0) {
586 const int offset = JavaScriptFrameConstants::kLocal0Offset;
587 return frame->fp() + offset - (slot_index * kPointerSize);
588 } else {
589 const int offset = JavaScriptFrameConstants::kLastParameterOffset;
590 return frame->fp() + offset - ((slot_index + 1) * kPointerSize);
591 }
592 }
593
594 static SlotRef ComputeSlotForNextArgument(TranslationIterator* iterator,
595 DeoptimizationInputData* data,
596 JavaScriptFrame* frame);
597};
598
599
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000600} } // namespace v8::internal
601
602#endif // V8_DEOPTIMIZER_H_