v8  3.25.30(node0.11.13)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
deoptimizer-arm.cc
Go to the documentation of this file.
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
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 
30 #include "codegen.h"
31 #include "deoptimizer.h"
32 #include "full-codegen.h"
33 #include "safepoint-table.h"
34 
35 namespace v8 {
36 namespace internal {
37 
38 const int Deoptimizer::table_entry_size_ = 12;
39 
40 
42  const int kCallInstructionSizeInWords = 3;
43  return kCallInstructionSizeInWords * Assembler::kInstrSize;
44 }
45 
46 
47 void Deoptimizer::PatchCodeForDeoptimization(Isolate* isolate, Code* code) {
48  Address code_start_address = code->instruction_start();
49  // Invalidate the relocation information, as it will become invalid by the
50  // code patching below, and is not needed any more.
51  code->InvalidateRelocation();
52 
53  if (FLAG_zap_code_space) {
54  // Fail hard and early if we enter this code object again.
55  byte* pointer = code->FindCodeAgeSequence();
56  if (pointer != NULL) {
57  pointer += kNoCodeAgeSequenceLength * Assembler::kInstrSize;
58  } else {
59  pointer = code->instruction_start();
60  }
61  CodePatcher patcher(pointer, 1);
62  patcher.masm()->bkpt(0);
63 
64  DeoptimizationInputData* data =
65  DeoptimizationInputData::cast(code->deoptimization_data());
66  int osr_offset = data->OsrPcOffset()->value();
67  if (osr_offset > 0) {
68  CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
69  osr_patcher.masm()->bkpt(0);
70  }
71  }
72 
73  DeoptimizationInputData* deopt_data =
74  DeoptimizationInputData::cast(code->deoptimization_data());
75  SharedFunctionInfo* shared =
76  SharedFunctionInfo::cast(deopt_data->SharedFunctionInfo());
77  shared->EvictFromOptimizedCodeMap(code, "deoptimized code");
78 #ifdef DEBUG
79  Address prev_call_address = NULL;
80 #endif
81  // For each LLazyBailout instruction insert a call to the corresponding
82  // deoptimization entry.
83  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
84  if (deopt_data->Pc(i)->value() == -1) continue;
85  Address call_address = code_start_address + deopt_data->Pc(i)->value();
86  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
87  // We need calls to have a predictable size in the unoptimized code, but
88  // this is optimized code, so we don't have to have a predictable size.
89  int call_size_in_bytes =
91  RelocInfo::NONE32);
92  int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
93  ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0);
94  ASSERT(call_size_in_bytes <= patch_size());
95  CodePatcher patcher(call_address, call_size_in_words);
96  patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
97  ASSERT(prev_call_address == NULL ||
98  call_address >= prev_call_address + patch_size());
99  ASSERT(call_address + patch_size() <= code->instruction_end());
100 #ifdef DEBUG
101  prev_call_address = call_address;
102 #endif
103  }
104 }
105 
106 
107 void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
108  // Set the register values. The values are not important as there are no
109  // callee saved registers in JavaScript frames, so all registers are
110  // spilled. Registers fp and sp are set to the correct values though.
111 
112  for (int i = 0; i < Register::kNumRegisters; i++) {
113  input_->SetRegister(i, i * 4);
114  }
115  input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
116  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
117  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
118  input_->SetDoubleRegister(i, 0.0);
119  }
120 
121  // Fill the frame content from the actual data on the frame.
122  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
123  input_->SetFrameSlot(i, Memory::uint32_at(tos + i));
124  }
125 }
126 
127 
128 void Deoptimizer::SetPlatformCompiledStubRegisters(
129  FrameDescription* output_frame, CodeStubInterfaceDescriptor* descriptor) {
130  ApiFunction function(descriptor->deoptimization_handler_);
131  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
132  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
133  int params = descriptor->GetHandlerParameterCount();
134  output_frame->SetRegister(r0.code(), params);
135  output_frame->SetRegister(r1.code(), handler);
136 }
137 
138 
139 void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
140  for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) {
141  double double_value = input_->GetDoubleRegister(i);
142  output_frame->SetDoubleRegister(i, double_value);
143  }
144 }
145 
146 
147 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
148  // There is no dynamic alignment padding on ARM in the input frame.
149  return false;
150 }
151 
152 
153 Code* Deoptimizer::NotifyStubFailureBuiltin() {
154  return isolate_->builtins()->builtin(Builtins::kNotifyStubFailureSaveDoubles);
155 }
156 
157 
158 #define __ masm()->
159 
160 // This code tries to be close to ia32 code so that any changes can be
161 // easily ported.
162 void Deoptimizer::EntryGenerator::Generate() {
163  GeneratePrologue();
164 
165  // Save all general purpose registers before messing with them.
167 
168  // Everything but pc, lr and ip which will be saved but not restored.
169  RegList restored_regs = kJSCallerSaved | kCalleeSaved | ip.bit();
170 
171  const int kDoubleRegsSize =
173 
174  // Save all allocatable VFP registers before messing with them.
175  ASSERT(kDoubleRegZero.code() == 14);
176  ASSERT(kScratchDoubleReg.code() == 15);
177 
178  // Check CPU flags for number of registers, setting the Z condition flag.
179  __ CheckFor32DRegs(ip);
180 
181  // Push registers d0-d13, and possibly d16-d31, on the stack.
182  // If d16-d31 are not pushed, decrease the stack pointer instead.
183  __ vstm(db_w, sp, d16, d31, ne);
184  __ sub(sp, sp, Operand(16 * kDoubleSize), LeaveCC, eq);
185  __ vstm(db_w, sp, d0, d13);
186 
187  // Push all 16 registers (needed to populate FrameDescription::registers_).
188  // TODO(1588) Note that using pc with stm is deprecated, so we should perhaps
189  // handle this a bit differently.
190  __ stm(db_w, sp, restored_regs | sp.bit() | lr.bit() | pc.bit());
191 
192  const int kSavedRegistersAreaSize =
193  (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
194 
195  // Get the bailout id from the stack.
196  __ ldr(r2, MemOperand(sp, kSavedRegistersAreaSize));
197 
198  // Get the address of the location in the code object (r3) (return
199  // address for lazy deoptimization) and compute the fp-to-sp delta in
200  // register r4.
201  __ mov(r3, lr);
202  // Correct one word for bailout id.
203  __ add(r4, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
204  __ sub(r4, fp, r4);
205 
206  // Allocate a new deoptimizer object.
207  // Pass four arguments in r0 to r3 and fifth argument on stack.
208  __ PrepareCallCFunction(6, r5);
210  __ mov(r1, Operand(type())); // bailout type,
211  // r2: bailout id already loaded.
212  // r3: code address or 0 already loaded.
213  __ str(r4, MemOperand(sp, 0 * kPointerSize)); // Fp-to-sp delta.
214  __ mov(r5, Operand(ExternalReference::isolate_address(isolate())));
215  __ str(r5, MemOperand(sp, 1 * kPointerSize)); // Isolate.
216  // Call Deoptimizer::New().
217  {
218  AllowExternalCallThatCantCauseGC scope(masm());
219  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
220  }
221 
222  // Preserve "deoptimizer" object in register r0 and get the input
223  // frame descriptor pointer to r1 (deoptimizer->input_);
225 
226  // Copy core registers into FrameDescription::registers_[kNumRegisters].
227  ASSERT(Register::kNumRegisters == kNumberOfRegisters);
228  for (int i = 0; i < kNumberOfRegisters; i++) {
229  int offset = (i * kPointerSize) + FrameDescription::registers_offset();
230  __ ldr(r2, MemOperand(sp, i * kPointerSize));
231  __ str(r2, MemOperand(r1, offset));
232  }
233 
234  // Copy VFP registers to
235  // double_registers_[DoubleRegister::kMaxNumAllocatableRegisters]
236  int double_regs_offset = FrameDescription::double_registers_offset();
237  for (int i = 0; i < DwVfpRegister::kMaxNumAllocatableRegisters; ++i) {
238  int dst_offset = i * kDoubleSize + double_regs_offset;
239  int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
240  __ vldr(d0, sp, src_offset);
241  __ vstr(d0, r1, dst_offset);
242  }
243 
244  // Remove the bailout id and the saved registers from the stack.
245  __ add(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
246 
247  // Compute a pointer to the unwinding limit in register r2; that is
248  // the first stack slot not part of the input frame.
250  __ add(r2, r2, sp);
251 
252  // Unwind the stack down to - but not including - the unwinding
253  // limit and copy the contents of the activation frame to the input
254  // frame description.
256  Label pop_loop;
257  Label pop_loop_header;
258  __ b(&pop_loop_header);
259  __ bind(&pop_loop);
260  __ pop(r4);
261  __ str(r4, MemOperand(r3, 0));
262  __ add(r3, r3, Operand(sizeof(uint32_t)));
263  __ bind(&pop_loop_header);
264  __ cmp(r2, sp);
265  __ b(ne, &pop_loop);
266 
267  // Compute the output frame in the deoptimizer.
268  __ push(r0); // Preserve deoptimizer object across call.
269  // r0: deoptimizer object; r1: scratch.
270  __ PrepareCallCFunction(1, r1);
271  // Call Deoptimizer::ComputeOutputFrames().
272  {
273  AllowExternalCallThatCantCauseGC scope(masm());
274  __ CallCFunction(
275  ExternalReference::compute_output_frames_function(isolate()), 1);
276  }
277  __ pop(r0); // Restore deoptimizer object (class Deoptimizer).
278 
279  // Replace the current (input) frame with the output frames.
280  Label outer_push_loop, inner_push_loop,
281  outer_loop_header, inner_loop_header;
282  // Outer loop state: r4 = current "FrameDescription** output_",
283  // r1 = one past the last FrameDescription**.
285  __ ldr(r4, MemOperand(r0, Deoptimizer::output_offset())); // r4 is output_.
286  __ add(r1, r4, Operand(r1, LSL, 2));
287  __ jmp(&outer_loop_header);
288  __ bind(&outer_push_loop);
289  // Inner loop state: r2 = current FrameDescription*, r3 = loop index.
290  __ ldr(r2, MemOperand(r4, 0)); // output_[ix]
292  __ jmp(&inner_loop_header);
293  __ bind(&inner_push_loop);
294  __ sub(r3, r3, Operand(sizeof(uint32_t)));
295  __ add(r6, r2, Operand(r3));
297  __ push(r6);
298  __ bind(&inner_loop_header);
299  __ cmp(r3, Operand::Zero());
300  __ b(ne, &inner_push_loop); // test for gt?
301  __ add(r4, r4, Operand(kPointerSize));
302  __ bind(&outer_loop_header);
303  __ cmp(r4, r1);
304  __ b(lt, &outer_push_loop);
305 
306  // Check CPU flags for number of registers, setting the Z condition flag.
307  __ CheckFor32DRegs(ip);
308 
311  for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) {
312  if (i == kDoubleRegZero.code()) continue;
313  if (i == kScratchDoubleReg.code()) continue;
314 
315  const DwVfpRegister reg = DwVfpRegister::from_code(i);
316  __ vldr(reg, r1, src_offset, i < 16 ? al : ne);
317  src_offset += kDoubleSize;
318  }
319 
320  // Push state, pc, and continuation from the last output frame.
322  __ push(r6);
324  __ push(r6);
326  __ push(r6);
327 
328  // Push the registers from the last output frame.
329  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
330  int offset = (i * kPointerSize) + FrameDescription::registers_offset();
331  __ ldr(r6, MemOperand(r2, offset));
332  __ push(r6);
333  }
334 
335  // Restore the registers from the stack.
336  __ ldm(ia_w, sp, restored_regs); // all but pc registers.
337  __ pop(ip); // remove sp
338  __ pop(ip); // remove lr
339 
340  __ InitializeRootRegister();
341 
342  __ pop(ip); // remove pc
343  __ pop(ip); // get continuation, leave pc on stack
344  __ pop(lr);
345  __ Jump(ip);
346  __ stop("Unreachable.");
347 }
348 
349 
351  // Create a sequence of deoptimization entries.
352  // Note that registers are still live when jumping to an entry.
353  Label done;
354  for (int i = 0; i < count(); i++) {
355  int start = masm()->pc_offset();
356  USE(start);
357  __ mov(ip, Operand(i));
358  __ push(ip);
359  __ b(&done);
360  ASSERT(masm()->pc_offset() - start == table_entry_size_);
361  }
362  __ bind(&done);
363 }
364 
365 
366 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
367  SetFrameSlot(offset, value);
368 }
369 
370 
371 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
372  SetFrameSlot(offset, value);
373 }
374 
375 
376 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
377  ASSERT(FLAG_enable_ool_constant_pool);
378  SetFrameSlot(offset, value);
379 }
380 
381 
382 #undef __
383 
384 } } // namespace v8::internal
byte * Address
Definition: globals.h:186
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter NULL
Definition: flags.cc:269
void SetCallerPc(unsigned offset, intptr_t value)
Code * builtin(Name name)
Definition: builtins.h:322
void InvalidateRelocation()
Definition: objects.cc:10298
const Register r3
const LowDwVfpRegister d0
const DwVfpRegister d31
static const int kMaxNumAllocatableRegisters
void SetFrameSlot(unsigned offset, intptr_t value)
Definition: deoptimizer.h:503
const Register r6
Builtins * builtins()
Definition: isolate.h:948
uint32_t RegList
Definition: frames.h:41
byte * instruction_end()
Definition: objects-inl.h:5862
#define ASSERT(condition)
Definition: checks.h:329
const RegList kJSCallerSaved
Definition: frames-arm.h:47
static SharedFunctionInfo * cast(Object *obj)
double GetDoubleRegister(unsigned n) const
Definition: deoptimizer.h:526
const Register r2
static DwVfpRegister from_code(int code)
static const int kNumRegisters
static int double_registers_offset()
Definition: deoptimizer.h:582
uint8_t byte
Definition: globals.h:185
const DwVfpRegister d16
const Register sp
static int output_offset()
Definition: deoptimizer.h:250
const int kDoubleSize
Definition: globals.h:266
const LowDwVfpRegister d13
const Register ip
static Address GetDeoptimizationEntry(Isolate *isolate, int id, BailoutType type, GetEntryMode mode=ENSURE_ENTRY_CODE)
Definition: deoptimizer.cc:683
byte * instruction_start()
Definition: objects-inl.h:5857
const int kPointerSize
Definition: globals.h:268
#define kScratchDoubleReg
const RegList kCalleeSaved
Definition: frames-arm.h:63
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter trace hydrogen to given file name trace inlining decisions trace store elimination trace all use positions trace global value numbering trace hydrogen escape analysis trace the tracking of allocation sites trace map generalization environment for every instruction deoptimize every n garbage collections put a break point before deoptimizing deoptimize uncommon cases use on stack replacement trace array bounds check elimination perform array index dehoisting use load elimination use store elimination use constant folding eliminate unreachable code number of stress runs when picking a function to watch for shared function not JSFunction itself flushes the cache of optimized code for closures on every GC functions with arguments object maximum number of escape analysis fix point iterations allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms concurrent on stack replacement do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes number of stack frames inspected by the profiler percentage of ICs that must have type info to allow optimization extra verbose compilation tracing generate extra code(assertions) for debugging") DEFINE_bool(code_comments
#define __
void SetRegister(unsigned n, intptr_t value)
Definition: deoptimizer.h:531
static const int kMaxNumRegisters
const Register pc
const Register r0
void SetCallerConstantPool(unsigned offset, intptr_t value)
uint32_t GetFrameSize() const
Definition: deoptimizer.h:485
static int output_count_offset()
Definition: deoptimizer.h:247
const Register lr
const unsigned kNumberOfRegisters
const Register r1
#define kDoubleRegZero
void USE(T)
Definition: globals.h:341
virtual void GeneratePrologue()
static int CallSizeNotPredictableCodeSize(Address target, RelocInfo::Mode rmode, Condition cond=al)
static const int kInstrSize
static uint32_t & uint32_at(Address addr)
Definition: v8memory.h:47
const Register fp
void SetDoubleRegister(unsigned n, double value)
Definition: deoptimizer.h:536
void SetCallerFp(unsigned offset, intptr_t value)
void EvictFromOptimizedCodeMap(Code *optimized_code, const char *reason)
Definition: objects.cc:9593
const Register r5
static DeoptimizationInputData * cast(Object *obj)
const Register r4