blob: 9f3c4e97f6b516479072577356aa21a2b0287c94 [file] [log] [blame]
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00001// Copyright 2012 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#include "v8.h"
29
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +000030#if defined(V8_TARGET_ARCH_IA32)
31
kasperl@chromium.orga5551262010-12-07 12:49:48 +000032#include "codegen.h"
33#include "deoptimizer.h"
34#include "full-codegen.h"
35#include "safepoint-table.h"
36
37namespace v8 {
38namespace internal {
39
kmillikin@chromium.org7c2628c2011-08-10 11:27:35 +000040const int Deoptimizer::table_entry_size_ = 10;
kasperl@chromium.orga5551262010-12-07 12:49:48 +000041
kmillikin@chromium.org31b12772011-02-02 16:08:26 +000042
43int Deoptimizer::patch_size() {
44 return Assembler::kCallInstructionLength;
45}
46
47
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000048void Deoptimizer::EnsureRelocSpaceForLazyDeoptimization(Handle<Code> code) {
49 Isolate* isolate = code->GetIsolate();
50 HandleScope scope(isolate);
kasperl@chromium.orga5551262010-12-07 12:49:48 +000051
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000052 // Compute the size of relocation information needed for the code
53 // patching in Deoptimizer::DeoptimizeFunction.
54 int min_reloc_size = 0;
ricow@chromium.org27bf2882011-11-17 08:34:43 +000055 int prev_pc_offset = 0;
56 DeoptimizationInputData* deopt_data =
57 DeoptimizationInputData::cast(code->deoptimization_data());
58 for (int i = 0; i < deopt_data->DeoptCount(); i++) {
59 int pc_offset = deopt_data->Pc(i)->value();
60 if (pc_offset == -1) continue;
61 ASSERT_GE(pc_offset, prev_pc_offset);
62 int pc_delta = pc_offset - prev_pc_offset;
63 // We use RUNTIME_ENTRY reloc info which has a size of 2 bytes
64 // if encodable with small pc delta encoding and up to 6 bytes
65 // otherwise.
66 if (pc_delta <= RelocInfo::kMaxSmallPCDelta) {
67 min_reloc_size += 2;
68 } else {
69 min_reloc_size += 6;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000070 }
ricow@chromium.org27bf2882011-11-17 08:34:43 +000071 prev_pc_offset = pc_offset;
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000072 }
73
74 // If the relocation information is not big enough we create a new
75 // relocation info object that is padded with comments to make it
76 // big enough for lazy doptimization.
77 int reloc_length = code->relocation_info()->length();
78 if (min_reloc_size > reloc_length) {
79 int comment_reloc_size = RelocInfo::kMinRelocCommentSize;
80 // Padding needed.
81 int min_padding = min_reloc_size - reloc_length;
82 // Number of comments needed to take up at least that much space.
83 int additional_comments =
84 (min_padding + comment_reloc_size - 1) / comment_reloc_size;
85 // Actual padding size.
86 int padding = additional_comments * comment_reloc_size;
87 // Allocate new relocation info and copy old relocation to the end
88 // of the new relocation info array because relocation info is
89 // written and read backwards.
90 Factory* factory = isolate->factory();
91 Handle<ByteArray> new_reloc =
92 factory->NewByteArray(reloc_length + padding, TENURED);
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000093 OS::MemCopy(new_reloc->GetDataStartAddress() + padding,
94 code->relocation_info()->GetDataStartAddress(),
95 reloc_length);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +000096 // Create a relocation writer to write the comments in the padding
97 // space. Use position 0 for everything to ensure short encoding.
98 RelocInfoWriter reloc_info_writer(
99 new_reloc->GetDataStartAddress() + padding, 0);
100 intptr_t comment_string
101 = reinterpret_cast<intptr_t>(RelocInfo::kFillerCommentString);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000102 RelocInfo rinfo(0, RelocInfo::COMMENT, comment_string, NULL);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000103 for (int i = 0; i < additional_comments; ++i) {
104#ifdef DEBUG
105 byte* pos_before = reloc_info_writer.pos();
106#endif
107 reloc_info_writer.Write(&rinfo);
108 ASSERT(RelocInfo::kMinRelocCommentSize ==
109 pos_before - reloc_info_writer.pos());
110 }
111 // Replace relocation information on the code object.
112 code->set_relocation_info(*new_reloc);
113 }
114}
115
116
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000117void Deoptimizer::DeoptimizeFunctionWithPreparedFunctionList(
118 JSFunction* function) {
119 Isolate* isolate = function->GetIsolate();
120 HandleScope scope(isolate);
121 AssertNoAllocation no_allocation;
122
123 ASSERT(function->IsOptimized());
124 ASSERT(function->FunctionsInFunctionListShareSameCode());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000125
126 // Get the optimized code.
127 Code* code = function->code();
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000128 Address code_start_address = code->instruction_start();
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000129
ulan@chromium.org906e2fb2013-05-14 08:14:38 +0000130 // The optimized code is going to be patched, so we cannot use it any more.
131 function->shared()->EvictFromOptimizedCodeMap(code, "deoptimized function");
132
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000133 // We will overwrite the code's relocation info in-place. Relocation info
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000134 // is written backward. The relocation info is the payload of a byte
135 // array. Later on we will slide this to the start of the byte array and
136 // create a filler object in the remaining space.
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000137 ByteArray* reloc_info = code->relocation_info();
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000138 Address reloc_end_address = reloc_info->address() + reloc_info->Size();
139 RelocInfoWriter reloc_info_writer(reloc_end_address, code_start_address);
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000140
ricow@chromium.org27bf2882011-11-17 08:34:43 +0000141 // For each LLazyBailout instruction insert a call to the corresponding
142 // deoptimization entry.
143
144 // Since the call is a relative encoding, write new
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000145 // reloc info. We do not need any of the existing reloc info because the
146 // existing code will not be used again (we zap it in debug builds).
ricow@chromium.org27bf2882011-11-17 08:34:43 +0000147 //
148 // Emit call to lazy deoptimization at all lazy deopt points.
149 DeoptimizationInputData* deopt_data =
150 DeoptimizationInputData::cast(code->deoptimization_data());
151#ifdef DEBUG
152 Address prev_call_address = NULL;
153#endif
154 for (int i = 0; i < deopt_data->DeoptCount(); i++) {
155 if (deopt_data->Pc(i)->value() == -1) continue;
156 // Patch lazy deoptimization entry.
157 Address call_address = code_start_address + deopt_data->Pc(i)->value();
158 CodePatcher patcher(call_address, patch_size());
hpayer@chromium.org8432c912013-02-28 15:55:26 +0000159 Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
jkummerow@chromium.org59297c72013-01-09 16:32:23 +0000160 patcher.masm()->call(deopt_entry, RelocInfo::NONE32);
ricow@chromium.org27bf2882011-11-17 08:34:43 +0000161 // We use RUNTIME_ENTRY for deoptimization bailouts.
162 RelocInfo rinfo(call_address + 1, // 1 after the call opcode.
163 RelocInfo::RUNTIME_ENTRY,
164 reinterpret_cast<intptr_t>(deopt_entry),
165 NULL);
166 reloc_info_writer.Write(&rinfo);
167 ASSERT_GE(reloc_info_writer.pos(),
168 reloc_info->address() + ByteArray::kHeaderSize);
169 ASSERT(prev_call_address == NULL ||
170 call_address >= prev_call_address + patch_size());
171 ASSERT(call_address + patch_size() <= code->instruction_end());
172#ifdef DEBUG
173 prev_call_address = call_address;
174#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000175 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000176
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000177 // Move the relocation info to the beginning of the byte array.
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000178 int new_reloc_size = reloc_end_address - reloc_info_writer.pos();
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000179 OS::MemMove(
180 code->relocation_start(), reloc_info_writer.pos(), new_reloc_size);
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000181
182 // The relocation info is in place, update the size.
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000183 reloc_info->set_length(new_reloc_size);
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000184
185 // Handle the junk part after the new relocation info. We will create
186 // a non-live object in the extra space at the end of the former reloc info.
ricow@chromium.org83aa5492011-02-07 12:42:56 +0000187 Address junk_address = reloc_info->address() + reloc_info->Size();
188 ASSERT(junk_address <= reloc_end_address);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000189 isolate->heap()->CreateFillerObjectAt(junk_address,
190 reloc_end_address - junk_address);
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000191
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000192 // Add the deoptimizing code to the list.
193 DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code);
fschneider@chromium.org7979bbb2011-03-28 10:47:03 +0000194 DeoptimizerData* data = isolate->deoptimizer_data();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000195 node->set_next(data->deoptimizing_code_list_);
196 data->deoptimizing_code_list_ = node;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000197
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000198 // We might be in the middle of incremental marking with compaction.
199 // Tell collector to treat this code object in a special way and
200 // ignore all slots that might have been recorded on it.
201 isolate->heap()->mark_compact_collector()->InvalidateCode(code);
202
rossberg@chromium.org89e18f52012-10-22 13:09:53 +0000203 ReplaceCodeForRelatedFunctions(function, code);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000204
205 if (FLAG_trace_deopt) {
206 PrintF("[forced deoptimization: ");
207 function->PrintName();
208 PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function));
209 }
210}
211
212
yangguo@chromium.org56454712012-02-16 15:33:53 +0000213static const byte kJnsInstruction = 0x79;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000214static const byte kJnsOffset = 0x11;
yangguo@chromium.org56454712012-02-16 15:33:53 +0000215static const byte kCallInstruction = 0xe8;
216static const byte kNopByteOne = 0x66;
217static const byte kNopByteTwo = 0x90;
218
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000219// The back edge bookkeeping code matches the pattern:
220//
221// sub <profiling_counter>, <delta>
222// jns ok
223// call <interrupt stub>
224// ok:
225//
226// The patched back edge looks like this:
227//
228// sub <profiling_counter>, <delta> ;; Not changed
229// nop
230// nop
231// call <on-stack replacment>
232// ok:
yangguo@chromium.org56454712012-02-16 15:33:53 +0000233
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000234void Deoptimizer::PatchInterruptCodeAt(Code* unoptimized_code,
235 Address pc_after,
236 Code* interrupt_code,
237 Code* replacement_code) {
238 ASSERT(!InterruptCodeIsPatched(unoptimized_code,
239 pc_after,
240 interrupt_code,
241 replacement_code));
242 // Turn the jump into nops.
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000243 Address call_target_address = pc_after - kIntSize;
yangguo@chromium.org56454712012-02-16 15:33:53 +0000244 *(call_target_address - 3) = kNopByteOne;
245 *(call_target_address - 2) = kNopByteTwo;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000246 // Replace the call address.
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000247 Assembler::set_target_address_at(call_target_address,
248 replacement_code->entry());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000249
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +0000250 unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
251 unoptimized_code, call_target_address, replacement_code);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000252}
253
254
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000255void Deoptimizer::RevertInterruptCodeAt(Code* unoptimized_code,
256 Address pc_after,
257 Code* interrupt_code,
258 Code* replacement_code) {
259 ASSERT(InterruptCodeIsPatched(unoptimized_code,
260 pc_after,
261 interrupt_code,
262 replacement_code));
263 // Restore the original jump.
kmillikin@chromium.org49edbdf2011-02-16 12:32:18 +0000264 Address call_target_address = pc_after - kIntSize;
rossberg@chromium.orgcddc71f2012-12-07 12:40:13 +0000265 *(call_target_address - 3) = kJnsInstruction;
266 *(call_target_address - 2) = kJnsOffset;
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000267 // Restore the original call address.
kmillikin@chromium.org31b12772011-02-02 16:08:26 +0000268 Assembler::set_target_address_at(call_target_address,
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000269 interrupt_code->entry());
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000270
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000271 interrupt_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
272 unoptimized_code, call_target_address, interrupt_code);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000273}
274
275
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000276#ifdef DEBUG
277bool Deoptimizer::InterruptCodeIsPatched(Code* unoptimized_code,
278 Address pc_after,
279 Code* interrupt_code,
280 Code* replacement_code) {
281 Address call_target_address = pc_after - kIntSize;
282 ASSERT_EQ(kCallInstruction, *(call_target_address - 1));
283 if (*(call_target_address - 3) == kNopByteOne) {
284 ASSERT_EQ(replacement_code->entry(),
285 Assembler::target_address_at(call_target_address));
286 ASSERT_EQ(kNopByteTwo, *(call_target_address - 2));
287 return true;
288 } else {
289 ASSERT_EQ(interrupt_code->entry(),
290 Assembler::target_address_at(call_target_address));
291 ASSERT_EQ(kJnsInstruction, *(call_target_address - 3));
292 ASSERT_EQ(kJnsOffset, *(call_target_address - 2));
293 return false;
294 }
295}
296#endif // DEBUG
297
298
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000299static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000300 ByteArray* translations = data->TranslationByteArray();
301 int length = data->DeoptCount();
302 for (int i = 0; i < length; i++) {
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000303 if (data->AstId(i) == ast_id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000304 TranslationIterator it(translations, data->TranslationIndex(i)->value());
305 int value = it.Next();
306 ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value));
307 // Read the number of frames.
308 value = it.Next();
309 if (value == 1) return i;
310 }
311 }
312 UNREACHABLE();
313 return -1;
314}
315
316
317void Deoptimizer::DoComputeOsrOutputFrame() {
318 DeoptimizationInputData* data = DeoptimizationInputData::cast(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000319 compiled_code_->deoptimization_data());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000320 unsigned ast_id = data->OsrAstId()->value();
321 // TODO(kasperl): This should not be the bailout_id_. It should be
322 // the ast id. Confusing.
323 ASSERT(bailout_id_ == ast_id);
324
mstarzinger@chromium.org471f2f12012-08-10 14:46:33 +0000325 int bailout_id = LookupBailoutId(data, BailoutId(ast_id));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000326 unsigned translation_index = data->TranslationIndex(bailout_id)->value();
327 ByteArray* translations = data->TranslationByteArray();
328
329 TranslationIterator iterator(translations, translation_index);
330 Translation::Opcode opcode =
331 static_cast<Translation::Opcode>(iterator.Next());
332 ASSERT(Translation::BEGIN == opcode);
333 USE(opcode);
334 int count = iterator.Next();
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000335 iterator.Next(); // Drop JS frames count.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000336 ASSERT(count == 1);
337 USE(count);
338
339 opcode = static_cast<Translation::Opcode>(iterator.Next());
340 USE(opcode);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000341 ASSERT(Translation::JS_FRAME == opcode);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000342 unsigned node_id = iterator.Next();
343 USE(node_id);
344 ASSERT(node_id == ast_id);
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000345 int closure_id = iterator.Next();
346 USE(closure_id);
347 ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000348 unsigned height = iterator.Next();
349 unsigned height_in_bytes = height * kPointerSize;
350 USE(height_in_bytes);
351
352 unsigned fixed_size = ComputeFixedSize(function_);
353 unsigned input_frame_size = input_->GetFrameSize();
354 ASSERT(fixed_size + height_in_bytes == input_frame_size);
355
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000356 unsigned stack_slot_size = compiled_code_->stack_slots() * kPointerSize;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000357 unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value();
358 unsigned outgoing_size = outgoing_height * kPointerSize;
359 unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size;
360 ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call.
361
362 if (FLAG_trace_osr) {
363 PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ",
364 reinterpret_cast<intptr_t>(function_));
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000365 PrintFunctionName();
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000366 PrintF(" => node=%u, frame=%d->%d, ebp:esp=0x%08x:0x%08x]\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000367 ast_id,
368 input_frame_size,
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000369 output_frame_size,
370 input_->GetRegister(ebp.code()),
371 input_->GetRegister(esp.code()));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000372 }
373
374 // There's only one output frame in the OSR case.
375 output_count_ = 1;
376 output_ = new FrameDescription*[1];
377 output_[0] = new(output_frame_size) FrameDescription(
378 output_frame_size, function_);
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000379 output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000380
381 // Clear the incoming parameters in the optimized frame to avoid
382 // confusing the garbage collector.
383 unsigned output_offset = output_frame_size - kPointerSize;
384 int parameter_count = function_->shared()->formal_parameter_count() + 1;
385 for (int i = 0; i < parameter_count; ++i) {
386 output_[0]->SetFrameSlot(output_offset, 0);
387 output_offset -= kPointerSize;
388 }
389
390 // Translate the incoming parameters. This may overwrite some of the
391 // incoming argument slots we've just cleared.
392 int input_offset = input_frame_size - kPointerSize;
393 bool ok = true;
394 int limit = input_offset - (parameter_count * kPointerSize);
395 while (ok && input_offset > limit) {
396 ok = DoOsrTranslateCommand(&iterator, &input_offset);
397 }
398
399 // There are no translation commands for the caller's pc and fp, the
400 // context, and the function. Set them up explicitly.
lrn@chromium.org7516f052011-03-30 08:52:27 +0000401 for (int i = StandardFrameConstants::kCallerPCOffset;
402 ok && i >= StandardFrameConstants::kMarkerOffset;
403 i -= kPointerSize) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000404 uint32_t input_value = input_->GetFrameSlot(input_offset);
405 if (FLAG_trace_osr) {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000406 const char* name = "UNKNOWN";
407 switch (i) {
408 case StandardFrameConstants::kCallerPCOffset:
409 name = "caller's pc";
410 break;
411 case StandardFrameConstants::kCallerFPOffset:
412 name = "fp";
413 break;
414 case StandardFrameConstants::kContextOffset:
415 name = "context";
416 break;
417 case StandardFrameConstants::kMarkerOffset:
418 name = "function";
419 break;
420 }
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000421 PrintF(" [sp + %d] <- 0x%08x ; [sp + %d] (fixed part - %s)\n",
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000422 output_offset,
423 input_value,
lrn@chromium.org7516f052011-03-30 08:52:27 +0000424 input_offset,
425 name);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000426 }
427 output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset));
428 input_offset -= kPointerSize;
429 output_offset -= kPointerSize;
430 }
431
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000432 // All OSR stack frames are dynamically aligned to an 8-byte boundary.
433 int frame_pointer = input_->GetRegister(ebp.code());
434 if ((frame_pointer & kPointerSize) != 0) {
435 frame_pointer -= kPointerSize;
436 has_alignment_padding_ = 1;
437 }
438
439 int32_t alignment_state = (has_alignment_padding_ == 1) ?
440 kAlignmentPaddingPushed :
441 kNoAlignmentPadding;
442 if (FLAG_trace_osr) {
443 PrintF(" [sp + %d] <- 0x%08x ; (alignment state)\n",
444 output_offset,
445 alignment_state);
446 }
447 output_[0]->SetFrameSlot(output_offset, alignment_state);
448 output_offset -= kPointerSize;
449
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000450 // Translate the rest of the frame.
451 while (ok && input_offset >= 0) {
452 ok = DoOsrTranslateCommand(&iterator, &input_offset);
453 }
454
455 // If translation of any command failed, continue using the input frame.
456 if (!ok) {
457 delete output_[0];
458 output_[0] = input_;
459 output_[0]->SetPc(reinterpret_cast<uint32_t>(from_));
460 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000461 // Set up the frame pointer and the context pointer.
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000462 output_[0]->SetRegister(ebp.code(), frame_pointer);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000463 output_[0]->SetRegister(esi.code(), input_->GetRegister(esi.code()));
464
465 unsigned pc_offset = data->OsrPcOffset()->value();
466 uint32_t pc = reinterpret_cast<uint32_t>(
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000467 compiled_code_->entry() + pc_offset);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000468 output_[0]->SetPc(pc);
469 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000470 Code* continuation =
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000471 function_->GetIsolate()->builtins()->builtin(Builtins::kNotifyOSR);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000472 output_[0]->SetContinuation(
473 reinterpret_cast<uint32_t>(continuation->entry()));
474
475 if (FLAG_trace_osr) {
476 PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR " ",
477 ok ? "finished" : "aborted",
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000478 reinterpret_cast<intptr_t>(function_));
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000479 PrintFunctionName();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000480 PrintF(" => pc=0x%0x]\n", output_[0]->GetPc());
481 }
482}
483
484
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000485void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
486 // Set the register values. The values are not important as there are no
487 // callee saved registers in JavaScript frames, so all registers are
488 // spilled. Registers ebp and esp are set to the correct values though.
489
490 for (int i = 0; i < Register::kNumRegisters; i++) {
491 input_->SetRegister(i, i * 4);
492 }
493 input_->SetRegister(esp.code(), reinterpret_cast<intptr_t>(frame->sp()));
494 input_->SetRegister(ebp.code(), reinterpret_cast<intptr_t>(frame->fp()));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000495 for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000496 input_->SetDoubleRegister(i, 0.0);
497 }
498
499 // Fill the frame content from the actual data on the frame.
500 for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
501 input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
502 }
503}
504
505
ulan@chromium.org6e196bf2013-03-13 09:38:22 +0000506void Deoptimizer::SetPlatformCompiledStubRegisters(
507 FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
508 intptr_t handler =
509 reinterpret_cast<intptr_t>(descriptor->deoptimization_handler_);
510 int params = descriptor->register_param_count_;
511 if (descriptor->stack_parameter_count_ != NULL) {
512 params++;
513 }
514 output_frame->SetRegister(eax.code(), params);
515 output_frame->SetRegister(ebx.code(), handler);
516}
517
518
519void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
520 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
521 double double_value = input_->GetDoubleRegister(i);
522 output_frame->SetDoubleRegister(i, double_value);
523 }
524}
525
526
jkummerow@chromium.org4e308cf2013-05-17 13:39:16 +0000527bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
528 int parameter_count = function->shared()->formal_parameter_count() + 1;
529 unsigned input_frame_size = input_->GetFrameSize();
530 unsigned alignment_state_offset =
531 input_frame_size - parameter_count * kPointerSize -
532 StandardFrameConstants::kFixedFrameSize -
533 kPointerSize;
534 ASSERT(JavaScriptFrameConstants::kDynamicAlignmentStateOffset ==
535 JavaScriptFrameConstants::kLocal0Offset);
536 int32_t alignment_state = input_->GetFrameSlot(alignment_state_offset);
537 return (alignment_state == kAlignmentPaddingPushed);
538}
539
540
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000541#define __ masm()->
542
543void Deoptimizer::EntryGenerator::Generate() {
544 GeneratePrologue();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000545
546 // Save all general purpose registers before messing with them.
547 const int kNumberOfRegisters = Register::kNumRegisters;
548
549 const int kDoubleRegsSize = kDoubleSize *
550 XMMRegister::kNumAllocatableRegisters;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000551 __ sub(esp, Immediate(kDoubleRegsSize));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000552 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000553 CpuFeatureScope scope(masm(), SSE2);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000554 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
555 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
556 int offset = i * kDoubleSize;
557 __ movdbl(Operand(esp, offset), xmm_reg);
558 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000559 }
560
561 __ pushad();
562
563 const int kSavedRegistersAreaSize = kNumberOfRegisters * kPointerSize +
564 kDoubleRegsSize;
565
566 // Get the bailout id from the stack.
567 __ mov(ebx, Operand(esp, kSavedRegistersAreaSize));
568
569 // Get the address of the location in the code object if possible
570 // and compute the fp-to-sp delta in register edx.
danno@chromium.orgaefd6072013-05-14 14:11:47 +0000571 if (type() == EAGER || type() == SOFT) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000572 __ Set(ecx, Immediate(0));
573 __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
574 } else {
575 __ mov(ecx, Operand(esp, kSavedRegistersAreaSize + 1 * kPointerSize));
576 __ lea(edx, Operand(esp, kSavedRegistersAreaSize + 2 * kPointerSize));
577 }
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000578 __ sub(edx, ebp);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000579 __ neg(edx);
580
581 // Allocate a new deoptimizer object.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000582 __ PrepareCallCFunction(6, eax);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000583 __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
584 __ mov(Operand(esp, 0 * kPointerSize), eax); // Function.
585 __ mov(Operand(esp, 1 * kPointerSize), Immediate(type())); // Bailout type.
586 __ mov(Operand(esp, 2 * kPointerSize), ebx); // Bailout id.
587 __ mov(Operand(esp, 3 * kPointerSize), ecx); // Code address or 0.
588 __ mov(Operand(esp, 4 * kPointerSize), edx); // Fp-to-sp delta.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000589 __ mov(Operand(esp, 5 * kPointerSize),
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000590 Immediate(ExternalReference::isolate_address(isolate())));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000591 {
592 AllowExternalCallThatCantCauseGC scope(masm());
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000593 __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000594 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000595
596 // Preserve deoptimizer object in register eax and get the input
597 // frame descriptor pointer.
598 __ mov(ebx, Operand(eax, Deoptimizer::input_offset()));
599
600 // Fill in the input registers.
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000601 for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
602 int offset = (i * kPointerSize) + FrameDescription::registers_offset();
603 __ pop(Operand(ebx, offset));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000604 }
605
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000606 int double_regs_offset = FrameDescription::double_registers_offset();
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000607 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000608 CpuFeatureScope scope(masm(), SSE2);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000609 // Fill in the double input registers.
610 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
611 int dst_offset = i * kDoubleSize + double_regs_offset;
612 int src_offset = i * kDoubleSize;
613 __ movdbl(xmm0, Operand(esp, src_offset));
614 __ movdbl(Operand(ebx, dst_offset), xmm0);
615 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000616 }
yangguo@chromium.org46a2a512013-01-18 16:29:40 +0000617
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000618 // Clear FPU all exceptions.
619 // TODO(ulan): Find out why the TOP register is not zero here in some cases,
620 // and check that the generated code never deoptimizes with unbalanced stack.
yangguo@chromium.org46a2a512013-01-18 16:29:40 +0000621 __ fnclex();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000622
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000623 // Remove the bailout id and the double registers from the stack.
danno@chromium.orgaefd6072013-05-14 14:11:47 +0000624 if (type() == EAGER || type() == SOFT) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000625 __ add(esp, Immediate(kDoubleRegsSize + kPointerSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000626 } else {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000627 __ add(esp, Immediate(kDoubleRegsSize + 2 * kPointerSize));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000628 }
629
630 // Compute a pointer to the unwinding limit in register ecx; that is
631 // the first stack slot not part of the input frame.
632 __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000633 __ add(ecx, esp);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000634
635 // Unwind the stack down to - but not including - the unwinding
636 // limit and copy the contents of the activation frame to the input
637 // frame description.
638 __ lea(edx, Operand(ebx, FrameDescription::frame_content_offset()));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000639 Label pop_loop_header;
640 __ jmp(&pop_loop_header);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000641 Label pop_loop;
642 __ bind(&pop_loop);
643 __ pop(Operand(edx, 0));
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000644 __ add(edx, Immediate(sizeof(uint32_t)));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000645 __ bind(&pop_loop_header);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000646 __ cmp(ecx, esp);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000647 __ j(not_equal, &pop_loop);
648
649 // Compute the output frame in the deoptimizer.
650 __ push(eax);
651 __ PrepareCallCFunction(1, ebx);
652 __ mov(Operand(esp, 0 * kPointerSize), eax);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000653 {
654 AllowExternalCallThatCantCauseGC scope(masm());
655 __ CallCFunction(
ulan@chromium.org32d7dba2013-04-24 10:59:06 +0000656 ExternalReference::compute_output_frames_function(isolate()), 1);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000657 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000658 __ pop(eax);
659
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000660 if (type() != OSR) {
661 // If frame was dynamically aligned, pop padding.
662 Label no_padding;
663 __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
664 Immediate(0));
665 __ j(equal, &no_padding);
666 __ pop(ecx);
667 if (FLAG_debug_code) {
668 __ cmp(ecx, Immediate(kAlignmentZapValue));
669 __ Assert(equal, "alignment marker expected");
670 }
671 __ bind(&no_padding);
672 } else {
673 // If frame needs dynamic alignment push padding.
674 Label no_padding;
675 __ cmp(Operand(eax, Deoptimizer::has_alignment_padding_offset()),
676 Immediate(0));
677 __ j(equal, &no_padding);
678 __ push(Immediate(kAlignmentZapValue));
679 __ bind(&no_padding);
680 }
681
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000682 // Replace the current frame with the output frames.
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000683 Label outer_push_loop, inner_push_loop,
684 outer_loop_header, inner_loop_header;
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000685 // Outer loop state: eax = current FrameDescription**, edx = one past the
686 // last FrameDescription**.
687 __ mov(edx, Operand(eax, Deoptimizer::output_count_offset()));
688 __ mov(eax, Operand(eax, Deoptimizer::output_offset()));
689 __ lea(edx, Operand(eax, edx, times_4, 0));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000690 __ jmp(&outer_loop_header);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000691 __ bind(&outer_push_loop);
692 // Inner loop state: ebx = current FrameDescription*, ecx = loop index.
693 __ mov(ebx, Operand(eax, 0));
694 __ mov(ecx, Operand(ebx, FrameDescription::frame_size_offset()));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000695 __ jmp(&inner_loop_header);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000696 __ bind(&inner_push_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000697 __ sub(ecx, Immediate(sizeof(uint32_t)));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000698 __ push(Operand(ebx, ecx, times_1, FrameDescription::frame_content_offset()));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000699 __ bind(&inner_loop_header);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000700 __ test(ecx, ecx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000701 __ j(not_zero, &inner_push_loop);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000702 __ add(eax, Immediate(kPointerSize));
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000703 __ bind(&outer_loop_header);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000704 __ cmp(eax, edx);
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000705 __ j(below, &outer_push_loop);
706
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000707 // In case of OSR or a failed STUB, we have to restore the XMM registers.
708 if (CpuFeatures::IsSupported(SSE2)) {
ulan@chromium.org750145a2013-03-07 15:14:13 +0000709 CpuFeatureScope scope(masm(), SSE2);
danno@chromium.org94b0d6f2013-02-04 13:33:20 +0000710 for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; ++i) {
711 XMMRegister xmm_reg = XMMRegister::FromAllocationIndex(i);
712 int src_offset = i * kDoubleSize + double_regs_offset;
713 __ movdbl(xmm_reg, Operand(ebx, src_offset));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000714 }
715 }
716
717 // Push state, pc, and continuation from the last output frame.
718 if (type() != OSR) {
719 __ push(Operand(ebx, FrameDescription::state_offset()));
720 }
721 __ push(Operand(ebx, FrameDescription::pc_offset()));
722 __ push(Operand(ebx, FrameDescription::continuation_offset()));
723
724
725 // Push the registers from the last output frame.
726 for (int i = 0; i < kNumberOfRegisters; i++) {
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000727 int offset = (i * kPointerSize) + FrameDescription::registers_offset();
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000728 __ push(Operand(ebx, offset));
729 }
730
731 // Restore the registers from the stack.
732 __ popad();
733
734 // Return to the continuation point.
735 __ ret(0);
736}
737
738
739void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
740 // Create a sequence of deoptimization entries.
741 Label done;
742 for (int i = 0; i < count(); i++) {
743 int start = masm()->pc_offset();
744 USE(start);
745 __ push_imm32(i);
746 __ jmp(&done);
747 ASSERT(masm()->pc_offset() - start == table_entry_size_);
748 }
749 __ bind(&done);
750}
751
752#undef __
753
754
755} } // namespace v8::internal
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000756
757#endif // V8_TARGET_ARCH_IA32