1
// Copyright 2012 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
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.
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.
30
#if defined(V8_TARGET_ARCH_X64)
33
#include "deoptimizer.h"
34
#include "full-codegen.h"
35
#include "safepoint-table.h"
41
const int Deoptimizer::table_entry_size_ = 10;
44
int Deoptimizer::patch_size() {
45
return Assembler::kCallInstructionLength;
49
void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
51
AssertNoAllocation no_allocation;
53
if (!function->IsOptimized()) return;
55
// The optimized code is going to be patched, so we cannot use it
56
// any more. Play safe and reset the whole cache.
57
function->shared()->ClearOptimizedCodeMap();
59
// Get the optimized code.
60
Code* code = function->code();
62
// Invalidate the relocation information, as it will become invalid by the
63
// code patching below, and is not needed any more.
64
code->InvalidateRelocation();
66
// For each LLazyBailout instruction insert a absolute call to the
67
// corresponding deoptimization entry, or a short call to an absolute
68
// jump if space is short. The absolute jumps are put in a table just
69
// before the safepoint table (space was allocated there when the Code
70
// object was created, if necessary).
72
Address instruction_start = function->code()->instruction_start();
74
Address prev_call_address = NULL;
76
DeoptimizationInputData* deopt_data =
77
DeoptimizationInputData::cast(code->deoptimization_data());
78
for (int i = 0; i < deopt_data->DeoptCount(); i++) {
79
if (deopt_data->Pc(i)->value() == -1) continue;
80
// Position where Call will be patched in.
81
Address call_address = instruction_start + deopt_data->Pc(i)->value();
82
// There is room enough to write a long call instruction because we pad
83
// LLazyBailout instructions with nops if necessary.
84
CodePatcher patcher(call_address, Assembler::kCallInstructionLength);
85
patcher.masm()->Call(GetDeoptimizationEntry(i, LAZY), RelocInfo::NONE);
86
ASSERT(prev_call_address == NULL ||
87
call_address >= prev_call_address + patch_size());
88
ASSERT(call_address + patch_size() <= code->instruction_end());
90
prev_call_address = call_address;
94
Isolate* isolate = code->GetIsolate();
96
// Add the deoptimizing code to the list.
97
DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code);
98
DeoptimizerData* data = isolate->deoptimizer_data();
99
node->set_next(data->deoptimizing_code_list_);
100
data->deoptimizing_code_list_ = node;
102
// We might be in the middle of incremental marking with compaction.
103
// Tell collector to treat this code object in a special way and
104
// ignore all slots that might have been recorded on it.
105
isolate->heap()->mark_compact_collector()->InvalidateCode(code);
107
// Iterate over all the functions which share the same code object
108
// and make them use unoptimized version.
109
Context* context = function->context()->global_context();
110
Object* element = context->get(Context::OPTIMIZED_FUNCTIONS_LIST);
111
SharedFunctionInfo* shared = function->shared();
112
while (!element->IsUndefined()) {
113
JSFunction* func = JSFunction::cast(element);
114
// Grab element before code replacement as ReplaceCode alters the list.
115
element = func->next_function_link();
116
if (func->code() == code) {
117
func->ReplaceCode(shared->code());
121
if (FLAG_trace_deopt) {
122
PrintF("[forced deoptimization: ");
123
function->PrintName();
124
PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
129
static const byte kJnsInstruction = 0x79;
130
static const byte kJnsOffset = 0x1f;
131
static const byte kJaeInstruction = 0x73;
132
static const byte kJaeOffset = 0x07;
133
static const byte kCallInstruction = 0xe8;
134
static const byte kNopByteOne = 0x66;
135
static const byte kNopByteTwo = 0x90;
137
void Deoptimizer::PatchStackCheckCodeAt(Code* unoptimized_code,
140
Code* replacement_code) {
141
Address call_target_address = pc_after - kIntSize;
142
ASSERT_EQ(check_code->entry(),
143
Assembler::target_address_at(call_target_address));
144
// The stack check code matches the pattern:
148
// call <stack guard>
149
// test rax, <loop nesting depth>
152
// We will patch away the branch so the code is:
154
// cmp rsp, <limit> ;; Not changed
157
// call <on-stack replacment>
158
// test rax, <loop nesting depth>
161
if (FLAG_count_based_interrupts) {
162
ASSERT_EQ(kJnsInstruction, *(call_target_address - 3));
163
ASSERT_EQ(kJnsOffset, *(call_target_address - 2));
165
ASSERT_EQ(kJaeInstruction, *(call_target_address - 3));
166
ASSERT_EQ(kJaeOffset, *(call_target_address - 2));
168
ASSERT_EQ(kCallInstruction, *(call_target_address - 1));
169
*(call_target_address - 3) = kNopByteOne;
170
*(call_target_address - 2) = kNopByteTwo;
171
Assembler::set_target_address_at(call_target_address,
172
replacement_code->entry());
174
unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
175
unoptimized_code, call_target_address, replacement_code);
179
void Deoptimizer::RevertStackCheckCodeAt(Code* unoptimized_code,
182
Code* replacement_code) {
183
Address call_target_address = pc_after - kIntSize;
184
ASSERT(replacement_code->entry() ==
185
Assembler::target_address_at(call_target_address));
186
// Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to
187
// restore the conditional branch.
188
ASSERT_EQ(kNopByteOne, *(call_target_address - 3));
189
ASSERT_EQ(kNopByteTwo, *(call_target_address - 2));
190
ASSERT_EQ(kCallInstruction, *(call_target_address - 1));
191
if (FLAG_count_based_interrupts) {
192
*(call_target_address - 3) = kJnsInstruction;
193
*(call_target_address - 2) = kJnsOffset;
195
*(call_target_address - 3) = kJaeInstruction;
196
*(call_target_address - 2) = kJaeOffset;
198
Assembler::set_target_address_at(call_target_address,
199
check_code->entry());
201
check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
202
unoptimized_code, call_target_address, check_code);
206
static int LookupBailoutId(DeoptimizationInputData* data, unsigned ast_id) {
207
ByteArray* translations = data->TranslationByteArray();
208
int length = data->DeoptCount();
209
for (int i = 0; i < length; i++) {
210
if (static_cast<unsigned>(data->AstId(i)->value()) == ast_id) {
211
TranslationIterator it(translations, data->TranslationIndex(i)->value());
212
int value = it.Next();
213
ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value));
214
// Read the number of frames.
216
if (value == 1) return i;
224
void Deoptimizer::DoComputeOsrOutputFrame() {
225
DeoptimizationInputData* data = DeoptimizationInputData::cast(
226
optimized_code_->deoptimization_data());
227
unsigned ast_id = data->OsrAstId()->value();
228
// TODO(kasperl): This should not be the bailout_id_. It should be
229
// the ast id. Confusing.
230
ASSERT(bailout_id_ == ast_id);
232
int bailout_id = LookupBailoutId(data, ast_id);
233
unsigned translation_index = data->TranslationIndex(bailout_id)->value();
234
ByteArray* translations = data->TranslationByteArray();
236
TranslationIterator iterator(translations, translation_index);
237
Translation::Opcode opcode =
238
static_cast<Translation::Opcode>(iterator.Next());
239
ASSERT(Translation::BEGIN == opcode);
241
int count = iterator.Next();
242
iterator.Skip(1); // Drop JS frame count.
246
opcode = static_cast<Translation::Opcode>(iterator.Next());
248
ASSERT(Translation::JS_FRAME == opcode);
249
unsigned node_id = iterator.Next();
251
ASSERT(node_id == ast_id);
252
int closure_id = iterator.Next();
254
ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
255
unsigned height = iterator.Next();
256
unsigned height_in_bytes = height * kPointerSize;
257
USE(height_in_bytes);
259
unsigned fixed_size = ComputeFixedSize(function_);
260
unsigned input_frame_size = input_->GetFrameSize();
261
ASSERT(fixed_size + height_in_bytes == input_frame_size);
263
unsigned stack_slot_size = optimized_code_->stack_slots() * kPointerSize;
264
unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value();
265
unsigned outgoing_size = outgoing_height * kPointerSize;
266
unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size;
267
ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call.
269
if (FLAG_trace_osr) {
270
PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ",
271
reinterpret_cast<intptr_t>(function_));
272
function_->PrintName();
273
PrintF(" => node=%u, frame=%d->%d]\n",
279
// There's only one output frame in the OSR case.
281
output_ = new FrameDescription*[1];
282
output_[0] = new(output_frame_size) FrameDescription(
283
output_frame_size, function_);
284
output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
286
// Clear the incoming parameters in the optimized frame to avoid
287
// confusing the garbage collector.
288
unsigned output_offset = output_frame_size - kPointerSize;
289
int parameter_count = function_->shared()->formal_parameter_count() + 1;
290
for (int i = 0; i < parameter_count; ++i) {
291
output_[0]->SetFrameSlot(output_offset, 0);
292
output_offset -= kPointerSize;
295
// Translate the incoming parameters. This may overwrite some of the
296
// incoming argument slots we've just cleared.
297
int input_offset = input_frame_size - kPointerSize;
299
int limit = input_offset - (parameter_count * kPointerSize);
300
while (ok && input_offset > limit) {
301
ok = DoOsrTranslateCommand(&iterator, &input_offset);
304
// There are no translation commands for the caller's pc and fp, the
305
// context, and the function. Set them up explicitly.
306
for (int i = StandardFrameConstants::kCallerPCOffset;
307
ok && i >= StandardFrameConstants::kMarkerOffset;
309
intptr_t input_value = input_->GetFrameSlot(input_offset);
310
if (FLAG_trace_osr) {
311
const char* name = "UNKNOWN";
313
case StandardFrameConstants::kCallerPCOffset:
314
name = "caller's pc";
316
case StandardFrameConstants::kCallerFPOffset:
319
case StandardFrameConstants::kContextOffset:
322
case StandardFrameConstants::kMarkerOffset:
326
PrintF(" [rsp + %d] <- 0x%08" V8PRIxPTR " ; [rsp + %d] "
327
"(fixed part - %s)\n",
333
output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset));
334
input_offset -= kPointerSize;
335
output_offset -= kPointerSize;
338
// Translate the rest of the frame.
339
while (ok && input_offset >= 0) {
340
ok = DoOsrTranslateCommand(&iterator, &input_offset);
343
// If translation of any command failed, continue using the input frame.
347
output_[0]->SetPc(reinterpret_cast<intptr_t>(from_));
349
// Set up the frame pointer and the context pointer.
350
output_[0]->SetRegister(rbp.code(), input_->GetRegister(rbp.code()));
351
output_[0]->SetRegister(rsi.code(), input_->GetRegister(rsi.code()));
353
unsigned pc_offset = data->OsrPcOffset()->value();
354
intptr_t pc = reinterpret_cast<intptr_t>(
355
optimized_code_->entry() + pc_offset);
356
output_[0]->SetPc(pc);
359
function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
360
output_[0]->SetContinuation(
361
reinterpret_cast<intptr_t>(continuation->entry()));
363
if (FLAG_trace_osr) {
364
PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
365
ok ? "finished" : "aborted",
366
reinterpret_cast<intptr_t>(function_));
367
function_->PrintName();
368
PrintF(" => pc=0x%0" V8PRIxPTR "]\n", output_[0]->GetPc());
373
void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
375
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
376
unsigned height = iterator->Next();
377
unsigned height_in_bytes = height * kPointerSize;
378
if (FLAG_trace_deopt) {
379
PrintF(" translating arguments adaptor => height=%d\n", height_in_bytes);
382
unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
383
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
385
// Allocate and store the output frame description.
386
FrameDescription* output_frame =
387
new(output_frame_size) FrameDescription(output_frame_size, function);
388
output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
390
// Arguments adaptor can not be topmost or bottommost.
391
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
392
ASSERT(output_[frame_index] == NULL);
393
output_[frame_index] = output_frame;
395
// The top address of the frame is computed from the previous
396
// frame's top and this frame's size.
397
intptr_t top_address;
398
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
399
output_frame->SetTop(top_address);
401
// Compute the incoming parameter translation.
402
int parameter_count = height;
403
unsigned output_offset = output_frame_size;
404
for (int i = 0; i < parameter_count; ++i) {
405
output_offset -= kPointerSize;
406
DoTranslateCommand(iterator, frame_index, output_offset);
409
// Read caller's PC from the previous frame.
410
output_offset -= kPointerSize;
411
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
412
output_frame->SetFrameSlot(output_offset, callers_pc);
413
if (FLAG_trace_deopt) {
414
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
415
V8PRIxPTR " ; caller's pc\n",
416
top_address + output_offset, output_offset, callers_pc);
419
// Read caller's FP from the previous frame, and set this frame's FP.
420
output_offset -= kPointerSize;
421
intptr_t value = output_[frame_index - 1]->GetFp();
422
output_frame->SetFrameSlot(output_offset, value);
423
intptr_t fp_value = top_address + output_offset;
424
output_frame->SetFp(fp_value);
425
if (FLAG_trace_deopt) {
426
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
427
V8PRIxPTR " ; caller's fp\n",
428
fp_value, output_offset, value);
431
// A marker value is used in place of the context.
432
output_offset -= kPointerSize;
433
intptr_t context = reinterpret_cast<intptr_t>(
434
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
435
output_frame->SetFrameSlot(output_offset, context);
436
if (FLAG_trace_deopt) {
437
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
438
V8PRIxPTR " ; context (adaptor sentinel)\n",
439
top_address + output_offset, output_offset, context);
442
// The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
443
output_offset -= kPointerSize;
444
value = reinterpret_cast<intptr_t>(function);
445
output_frame->SetFrameSlot(output_offset, value);
446
if (FLAG_trace_deopt) {
447
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
448
V8PRIxPTR " ; function\n",
449
top_address + output_offset, output_offset, value);
452
// Number of incoming arguments.
453
output_offset -= kPointerSize;
454
value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
455
output_frame->SetFrameSlot(output_offset, value);
456
if (FLAG_trace_deopt) {
457
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
458
V8PRIxPTR " ; argc (%d)\n",
459
top_address + output_offset, output_offset, value, height - 1);
462
ASSERT(0 == output_offset);
464
Builtins* builtins = isolate_->builtins();
465
Code* adaptor_trampoline =
466
builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
467
intptr_t pc_value = reinterpret_cast<intptr_t>(
468
adaptor_trampoline->instruction_start() +
469
isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
470
output_frame->SetPc(pc_value);
474
void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
476
Builtins* builtins = isolate_->builtins();
477
Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric);
478
JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
479
unsigned height = iterator->Next();
480
unsigned height_in_bytes = height * kPointerSize;
481
if (FLAG_trace_deopt) {
482
PrintF(" translating construct stub => height=%d\n", height_in_bytes);
485
unsigned fixed_frame_size = 7 * kPointerSize;
486
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
488
// Allocate and store the output frame description.
489
FrameDescription* output_frame =
490
new(output_frame_size) FrameDescription(output_frame_size, function);
491
output_frame->SetFrameType(StackFrame::CONSTRUCT);
493
// Construct stub can not be topmost or bottommost.
494
ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
495
ASSERT(output_[frame_index] == NULL);
496
output_[frame_index] = output_frame;
498
// The top address of the frame is computed from the previous
499
// frame's top and this frame's size.
500
intptr_t top_address;
501
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
502
output_frame->SetTop(top_address);
504
// Compute the incoming parameter translation.
505
int parameter_count = height;
506
unsigned output_offset = output_frame_size;
507
for (int i = 0; i < parameter_count; ++i) {
508
output_offset -= kPointerSize;
509
DoTranslateCommand(iterator, frame_index, output_offset);
512
// Read caller's PC from the previous frame.
513
output_offset -= kPointerSize;
514
intptr_t callers_pc = output_[frame_index - 1]->GetPc();
515
output_frame->SetFrameSlot(output_offset, callers_pc);
516
if (FLAG_trace_deopt) {
517
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
518
V8PRIxPTR " ; caller's pc\n",
519
top_address + output_offset, output_offset, callers_pc);
522
// Read caller's FP from the previous frame, and set this frame's FP.
523
output_offset -= kPointerSize;
524
intptr_t value = output_[frame_index - 1]->GetFp();
525
output_frame->SetFrameSlot(output_offset, value);
526
intptr_t fp_value = top_address + output_offset;
527
output_frame->SetFp(fp_value);
528
if (FLAG_trace_deopt) {
529
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
530
V8PRIxPTR " ; caller's fp\n",
531
fp_value, output_offset, value);
534
// The context can be gotten from the previous frame.
535
output_offset -= kPointerSize;
536
value = output_[frame_index - 1]->GetContext();
537
output_frame->SetFrameSlot(output_offset, value);
538
if (FLAG_trace_deopt) {
539
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
540
V8PRIxPTR " ; context\n",
541
top_address + output_offset, output_offset, value);
544
// A marker value is used in place of the function.
545
output_offset -= kPointerSize;
546
value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::CONSTRUCT));
547
output_frame->SetFrameSlot(output_offset, value);
548
if (FLAG_trace_deopt) {
549
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
550
V8PRIxPTR " ; function (construct sentinel)\n",
551
top_address + output_offset, output_offset, value);
554
// The output frame reflects a JSConstructStubGeneric frame.
555
output_offset -= kPointerSize;
556
value = reinterpret_cast<intptr_t>(construct_stub);
557
output_frame->SetFrameSlot(output_offset, value);
558
if (FLAG_trace_deopt) {
559
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
560
V8PRIxPTR " ; code object\n",
561
top_address + output_offset, output_offset, value);
564
// Number of incoming arguments.
565
output_offset -= kPointerSize;
566
value = reinterpret_cast<intptr_t>(Smi::FromInt(height - 1));
567
output_frame->SetFrameSlot(output_offset, value);
568
if (FLAG_trace_deopt) {
569
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
570
V8PRIxPTR " ; argc (%d)\n",
571
top_address + output_offset, output_offset, value, height - 1);
574
// The newly allocated object was passed as receiver in the artificial
575
// constructor stub environment created by HEnvironment::CopyForInlining().
576
output_offset -= kPointerSize;
577
value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
578
output_frame->SetFrameSlot(output_offset, value);
579
if (FLAG_trace_deopt) {
580
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
581
V8PRIxPTR " ; allocated receiver\n",
582
top_address + output_offset, output_offset, value);
585
ASSERT(0 == output_offset);
587
intptr_t pc = reinterpret_cast<intptr_t>(
588
construct_stub->instruction_start() +
589
isolate_->heap()->construct_stub_deopt_pc_offset()->value());
590
output_frame->SetPc(pc);
594
void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
596
int node_id = iterator->Next();
597
JSFunction* function;
598
if (frame_index != 0) {
599
function = JSFunction::cast(ComputeLiteral(iterator->Next()));
601
int closure_id = iterator->Next();
603
ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
604
function = function_;
606
unsigned height = iterator->Next();
607
unsigned height_in_bytes = height * kPointerSize;
608
if (FLAG_trace_deopt) {
609
PrintF(" translating ");
610
function->PrintName();
611
PrintF(" => node=%d, height=%d\n", node_id, height_in_bytes);
614
// The 'fixed' part of the frame consists of the incoming parameters and
615
// the part described by JavaScriptFrameConstants.
616
unsigned fixed_frame_size = ComputeFixedSize(function);
617
unsigned input_frame_size = input_->GetFrameSize();
618
unsigned output_frame_size = height_in_bytes + fixed_frame_size;
620
// Allocate and store the output frame description.
621
FrameDescription* output_frame =
622
new(output_frame_size) FrameDescription(output_frame_size, function);
623
output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
625
bool is_bottommost = (0 == frame_index);
626
bool is_topmost = (output_count_ - 1 == frame_index);
627
ASSERT(frame_index >= 0 && frame_index < output_count_);
628
ASSERT(output_[frame_index] == NULL);
629
output_[frame_index] = output_frame;
631
// The top address for the bottommost output frame can be computed from
632
// the input frame pointer and the output frame's height. For all
633
// subsequent output frames, it can be computed from the previous one's
634
// top address and the current frame's size.
635
intptr_t top_address;
637
// 2 = context and function in the frame.
639
input_->GetRegister(rbp.code()) - (2 * kPointerSize) - height_in_bytes;
641
top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
643
output_frame->SetTop(top_address);
645
// Compute the incoming parameter translation.
646
int parameter_count = function->shared()->formal_parameter_count() + 1;
647
unsigned output_offset = output_frame_size;
648
unsigned input_offset = input_frame_size;
649
for (int i = 0; i < parameter_count; ++i) {
650
output_offset -= kPointerSize;
651
DoTranslateCommand(iterator, frame_index, output_offset);
653
input_offset -= (parameter_count * kPointerSize);
655
// There are no translation commands for the caller's pc and fp, the
656
// context, and the function. Synthesize their values and set them up
659
// The caller's pc for the bottommost output frame is the same as in the
660
// input frame. For all subsequent output frames, it can be read from the
661
// previous one. This frame's pc can be computed from the non-optimized
662
// function code and AST id of the bailout.
663
output_offset -= kPointerSize;
664
input_offset -= kPointerSize;
667
value = input_->GetFrameSlot(input_offset);
669
value = output_[frame_index - 1]->GetPc();
671
output_frame->SetFrameSlot(output_offset, value);
672
if (FLAG_trace_deopt) {
673
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
674
V8PRIxPTR " ; caller's pc\n",
675
top_address + output_offset, output_offset, value);
678
// The caller's frame pointer for the bottommost output frame is the same
679
// as in the input frame. For all subsequent output frames, it can be
680
// read from the previous one. Also compute and set this frame's frame
682
output_offset -= kPointerSize;
683
input_offset -= kPointerSize;
685
value = input_->GetFrameSlot(input_offset);
687
value = output_[frame_index - 1]->GetFp();
689
output_frame->SetFrameSlot(output_offset, value);
690
intptr_t fp_value = top_address + output_offset;
691
ASSERT(!is_bottommost || input_->GetRegister(rbp.code()) == fp_value);
692
output_frame->SetFp(fp_value);
693
if (is_topmost) output_frame->SetRegister(rbp.code(), fp_value);
694
if (FLAG_trace_deopt) {
695
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
696
V8PRIxPTR " ; caller's fp\n",
697
fp_value, output_offset, value);
700
// For the bottommost output frame the context can be gotten from the input
701
// frame. For all subsequent output frames it can be gotten from the function
702
// so long as we don't inline functions that need local contexts.
703
output_offset -= kPointerSize;
704
input_offset -= kPointerSize;
706
value = input_->GetFrameSlot(input_offset);
708
value = reinterpret_cast<intptr_t>(function->context());
710
output_frame->SetFrameSlot(output_offset, value);
711
output_frame->SetContext(value);
712
if (is_topmost) output_frame->SetRegister(rsi.code(), value);
713
if (FLAG_trace_deopt) {
714
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
715
V8PRIxPTR "; context\n",
716
top_address + output_offset, output_offset, value);
719
// The function was mentioned explicitly in the BEGIN_FRAME.
720
output_offset -= kPointerSize;
721
input_offset -= kPointerSize;
722
value = reinterpret_cast<intptr_t>(function);
723
// The function for the bottommost output frame should also agree with the
725
ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value);
726
output_frame->SetFrameSlot(output_offset, value);
727
if (FLAG_trace_deopt) {
728
PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
729
V8PRIxPTR "; function\n",
730
top_address + output_offset, output_offset, value);
733
// Translate the rest of the frame.
734
for (unsigned i = 0; i < height; ++i) {
735
output_offset -= kPointerSize;
736
DoTranslateCommand(iterator, frame_index, output_offset);
738
ASSERT(0 == output_offset);
740
// Compute this frame's PC, state, and continuation.
741
Code* non_optimized_code = function->shared()->code();
742
FixedArray* raw_data = non_optimized_code->deoptimization_data();
743
DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
744
Address start = non_optimized_code->instruction_start();
745
unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared());
746
unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
747
intptr_t pc_value = reinterpret_cast<intptr_t>(start + pc_offset);
748
output_frame->SetPc(pc_value);
750
FullCodeGenerator::State state =
751
FullCodeGenerator::StateField::decode(pc_and_state);
752
output_frame->SetState(Smi::FromInt(state));
754
// Set the continuation for the topmost frame.
755
if (is_topmost && bailout_type_ != DEBUGGER) {
756
Code* continuation = (bailout_type_ == EAGER)
757
? isolate_->builtins()->builtin(Builtins::kNotifyDeoptimized)
758
: isolate_->builtins()->builtin(Builtins::kNotifyLazyDeoptimized);
759
output_frame->SetContinuation(
760
reinterpret_cast<intptr_t>(continuation->entry()));
765
void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
766
// Set the register values. The values are not important as there are no
767
// callee saved registers in JavaScript frames, so all registers are
768
// spilled. Registers rbp and rsp are set to the correct values though.
769
for (int i = 0; i < Register::kNumRegisters; i++) {
770
input_->SetRegister(i, i * 4);
772
input_->SetRegister(rsp.code(), reinterpret_cast<intptr_t>(frame->sp()));
773
input_->SetRegister(rbp.code(), reinterpret_cast<intptr_t>(frame->fp()));
774
for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) {
775
input_->SetDoubleRegister(i, 0.0);
778
// Fill the frame content from the actual data on the frame.
779
for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
780
input_->SetFrameSlot(i, Memory::uint64_at(tos + i));
787
void Deoptimizer::EntryGenerator::Generate() {
790
// Save all general purpose registers before messing with them.
791
const int kNumberOfRegisters = Register::kNumRegisters;
793
const int kDoubleRegsSize = kDoubleSize *
794
XMMRegister::kNumAllocatableRegisters;
795
__ subq(rsp, Immediate(kDoubleRegsSize));
797
for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
798
XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
799
int offset = i * kDoubleSize;
800
__ movsd(Operand(rsp, offset), xmm_reg);
803
// We push all registers onto the stack, even though we do not need
804
// to restore all later.
805
for (int i = 0; i < kNumberOfRegisters; i++) {
806
Register r = Register::from_code(i);
810
const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
813
// When calling new_deoptimizer_function we need to pass the last argument
814
// on the stack on windows and in r8 on linux. The remaining arguments are
815
// all passed in registers (different ones on linux and windows though).
829
// We use this to keep the value of the fifth argument temporarily.
830
// Unfortunately we can't store it directly in r8 (used for passing
831
// this on linux), since it is another parameter passing register on windows.
834
// Get the bailout id from the stack.
835
__ movq(arg3, Operand(rsp, kSavedRegistersAreaSize));
837
// Get the address of the location in the code object if possible
838
// and compute the fp-to-sp delta in register arg5.
839
if (type() == EAGER) {
841
__ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize));
843
__ movq(arg4, Operand(rsp, kSavedRegistersAreaSize + 1 * kPointerSize));
844
__ lea(arg5, Operand(rsp, kSavedRegistersAreaSize + 2 * kPointerSize));
850
// Allocate a new deoptimizer object.
851
__ PrepareCallCFunction(6);
852
__ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
854
__ Set(arg2, type());
855
// Args 3 and 4 are already in the right registers.
857
// On windows put the arguments on the stack (PrepareCallCFunction
858
// has created space for this). On linux pass the arguments in r8 and r9.
860
__ movq(Operand(rsp, 4 * kPointerSize), arg5);
861
__ LoadAddress(arg5, ExternalReference::isolate_address());
862
__ movq(Operand(rsp, 5 * kPointerSize), arg5);
865
__ LoadAddress(r9, ExternalReference::isolate_address());
868
Isolate* isolate = masm()->isolate();
871
AllowExternalCallThatCantCauseGC scope(masm());
872
__ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6);
874
// Preserve deoptimizer object in register rax and get the input
875
// frame descriptor pointer.
876
__ movq(rbx, Operand(rax, Deoptimizer::input_offset()));
878
// Fill in the input registers.
879
for (int i = kNumberOfRegisters -1; i >= 0; i--) {
880
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
881
__ pop(Operand(rbx, offset));
884
// Fill in the double input registers.
885
int double_regs_offset = FrameDescription::double_registers_offset();
886
for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; i++) {
887
int dst_offset = i * kDoubleSize + double_regs_offset;
888
__ pop(Operand(rbx, dst_offset));
891
// Remove the bailout id from the stack.
892
if (type() == EAGER) {
893
__ addq(rsp, Immediate(kPointerSize));
895
__ addq(rsp, Immediate(2 * kPointerSize));
898
// Compute a pointer to the unwinding limit in register rcx; that is
899
// the first stack slot not part of the input frame.
900
__ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
903
// Unwind the stack down to - but not including - the unwinding
904
// limit and copy the contents of the activation frame to the input
905
// frame description.
906
__ lea(rdx, Operand(rbx, FrameDescription::frame_content_offset()));
909
__ pop(Operand(rdx, 0));
910
__ addq(rdx, Immediate(sizeof(intptr_t)));
912
__ j(not_equal, &pop_loop);
914
// Compute the output frame in the deoptimizer.
916
__ PrepareCallCFunction(2);
918
__ LoadAddress(arg2, ExternalReference::isolate_address());
920
AllowExternalCallThatCantCauseGC scope(masm());
922
ExternalReference::compute_output_frames_function(isolate), 2);
926
// Replace the current frame with the output frames.
927
Label outer_push_loop, inner_push_loop;
928
// Outer loop state: rax = current FrameDescription**, rdx = one past the
929
// last FrameDescription**.
930
__ movl(rdx, Operand(rax, Deoptimizer::output_count_offset()));
931
__ movq(rax, Operand(rax, Deoptimizer::output_offset()));
932
__ lea(rdx, Operand(rax, rdx, times_8, 0));
933
__ bind(&outer_push_loop);
934
// Inner loop state: rbx = current FrameDescription*, rcx = loop index.
935
__ movq(rbx, Operand(rax, 0));
936
__ movq(rcx, Operand(rbx, FrameDescription::frame_size_offset()));
937
__ bind(&inner_push_loop);
938
__ subq(rcx, Immediate(sizeof(intptr_t)));
939
__ push(Operand(rbx, rcx, times_1, FrameDescription::frame_content_offset()));
941
__ j(not_zero, &inner_push_loop);
942
__ addq(rax, Immediate(kPointerSize));
944
__ j(below, &outer_push_loop);
946
// In case of OSR, we have to restore the XMM registers.
948
for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
949
XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
950
int src_offset = i * kDoubleSize + double_regs_offset;
951
__ movsd(xmm_reg, Operand(rbx, src_offset));
955
// Push state, pc, and continuation from the last output frame.
957
__ push(Operand(rbx, FrameDescription::state_offset()));
959
__ push(Operand(rbx, FrameDescription::pc_offset()));
960
__ push(Operand(rbx, FrameDescription::continuation_offset()));
962
// Push the registers from the last output frame.
963
for (int i = 0; i < kNumberOfRegisters; i++) {
964
int offset = (i * kPointerSize) + FrameDescription::registers_offset();
965
__ push(Operand(rbx, offset));
968
// Restore the registers from the stack.
969
for (int i = kNumberOfRegisters - 1; i >= 0 ; i--) {
970
Register r = Register::from_code(i);
971
// Do not restore rsp, simply pop the value into the next register
972
// and overwrite this afterwards.
975
r = Register::from_code(i - 1);
980
// Set up the roots register.
981
__ InitializeRootRegister();
982
__ InitializeSmiConstantRegister();
984
// Return to the continuation point.
989
void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
990
// Create a sequence of deoptimization entries.
992
for (int i = 0; i < count(); i++) {
993
int start = masm()->pc_offset();
997
ASSERT(masm()->pc_offset() - start == table_entry_size_);
1005
} } // namespace v8::internal
1007
#endif // V8_TARGET_ARCH_X64