V8 Project
deoptimizer-arm.cc
Go to the documentation of this file.
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/v8.h"
6 
7 #include "src/codegen.h"
8 #include "src/deoptimizer.h"
9 #include "src/full-codegen.h"
10 #include "src/safepoint-table.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 const int Deoptimizer::table_entry_size_ = 8;
16 
17 
19  const int kCallInstructionSizeInWords = 3;
20  return kCallInstructionSizeInWords * Assembler::kInstrSize;
21 }
22 
23 
25  Address code_start_address = code->instruction_start();
26  // Invalidate the relocation information, as it will become invalid by the
27  // code patching below, and is not needed any more.
28  code->InvalidateRelocation();
29 
30  if (FLAG_zap_code_space) {
31  // Fail hard and early if we enter this code object again.
32  byte* pointer = code->FindCodeAgeSequence();
33  if (pointer != NULL) {
34  pointer += kNoCodeAgeSequenceLength;
35  } else {
36  pointer = code->instruction_start();
37  }
38  CodePatcher patcher(pointer, 1);
39  patcher.masm()->bkpt(0);
40 
42  DeoptimizationInputData::cast(code->deoptimization_data());
43  int osr_offset = data->OsrPcOffset()->value();
44  if (osr_offset > 0) {
45  CodePatcher osr_patcher(code->instruction_start() + osr_offset, 1);
46  osr_patcher.masm()->bkpt(0);
47  }
48  }
49 
50  DeoptimizationInputData* deopt_data =
51  DeoptimizationInputData::cast(code->deoptimization_data());
52 #ifdef DEBUG
53  Address prev_call_address = NULL;
54 #endif
55  // For each LLazyBailout instruction insert a call to the corresponding
56  // deoptimization entry.
57  for (int i = 0; i < deopt_data->DeoptCount(); i++) {
58  if (deopt_data->Pc(i)->value() == -1) continue;
59  Address call_address = code_start_address + deopt_data->Pc(i)->value();
60  Address deopt_entry = GetDeoptimizationEntry(isolate, i, LAZY);
61  // We need calls to have a predictable size in the unoptimized code, but
62  // this is optimized code, so we don't have to have a predictable size.
63  int call_size_in_bytes =
65  deopt_entry,
67  int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
68  DCHECK(call_size_in_bytes % Assembler::kInstrSize == 0);
69  DCHECK(call_size_in_bytes <= patch_size());
70  CodePatcher patcher(call_address, call_size_in_words);
71  patcher.masm()->Call(deopt_entry, RelocInfo::NONE32);
72  DCHECK(prev_call_address == NULL ||
73  call_address >= prev_call_address + patch_size());
74  DCHECK(call_address + patch_size() <= code->instruction_end());
75 #ifdef DEBUG
76  prev_call_address = call_address;
77 #endif
78  }
79 }
80 
81 
83  // Set the register values. The values are not important as there are no
84  // callee saved registers in JavaScript frames, so all registers are
85  // spilled. Registers fp and sp are set to the correct values though.
86 
87  for (int i = 0; i < Register::kNumRegisters; i++) {
88  input_->SetRegister(i, i * 4);
89  }
90  input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
91  input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
92  for (int i = 0; i < DoubleRegister::NumAllocatableRegisters(); i++) {
93  input_->SetDoubleRegister(i, 0.0);
94  }
95 
96  // Fill the frame content from the actual data on the frame.
97  for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
99  }
100 }
101 
102 
104  FrameDescription* output_frame, CodeStubDescriptor* descriptor) {
105  ApiFunction function(descriptor->deoptimization_handler());
106  ExternalReference xref(&function, ExternalReference::BUILTIN_CALL, isolate_);
107  intptr_t handler = reinterpret_cast<intptr_t>(xref.address());
108  int params = descriptor->GetHandlerParameterCount();
109  output_frame->SetRegister(r0.code(), params);
110  output_frame->SetRegister(r1.code(), handler);
111 }
112 
113 
115  for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) {
116  double double_value = input_->GetDoubleRegister(i);
117  output_frame->SetDoubleRegister(i, double_value);
118  }
119 }
120 
121 
123  // There is no dynamic alignment padding on ARM in the input frame.
124  return false;
125 }
126 
127 
128 #define __ masm()->
129 
130 // This code tries to be close to ia32 code so that any changes can be
131 // easily ported.
132 void Deoptimizer::EntryGenerator::Generate() {
133  GeneratePrologue();
134 
135  // Save all general purpose registers before messing with them.
137 
138  // Everything but pc, lr and ip which will be saved but not restored.
139  RegList restored_regs = kJSCallerSaved | kCalleeSaved | ip.bit();
140 
141  const int kDoubleRegsSize =
143 
144  // Save all allocatable VFP registers before messing with them.
145  DCHECK(kDoubleRegZero.code() == 14);
146  DCHECK(kScratchDoubleReg.code() == 15);
147 
148  // Check CPU flags for number of registers, setting the Z condition flag.
149  __ CheckFor32DRegs(ip);
150 
151  // Push registers d0-d13, and possibly d16-d31, on the stack.
152  // If d16-d31 are not pushed, decrease the stack pointer instead.
153  __ vstm(db_w, sp, d16, d31, ne);
154  __ sub(sp, sp, Operand(16 * kDoubleSize), LeaveCC, eq);
155  __ vstm(db_w, sp, d0, d13);
156 
157  // Push all 16 registers (needed to populate FrameDescription::registers_).
158  // TODO(1588) Note that using pc with stm is deprecated, so we should perhaps
159  // handle this a bit differently.
160  __ stm(db_w, sp, restored_regs | sp.bit() | lr.bit() | pc.bit());
161 
162  const int kSavedRegistersAreaSize =
163  (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
164 
165  // Get the bailout id from the stack.
166  __ ldr(r2, MemOperand(sp, kSavedRegistersAreaSize));
167 
168  // Get the address of the location in the code object (r3) (return
169  // address for lazy deoptimization) and compute the fp-to-sp delta in
170  // register r4.
171  __ mov(r3, lr);
172  // Correct one word for bailout id.
173  __ add(r4, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
174  __ sub(r4, fp, r4);
175 
176  // Allocate a new deoptimizer object.
177  // Pass four arguments in r0 to r3 and fifth argument on stack.
178  __ PrepareCallCFunction(6, r5);
180  __ mov(r1, Operand(type())); // bailout type,
181  // r2: bailout id already loaded.
182  // r3: code address or 0 already loaded.
183  __ str(r4, MemOperand(sp, 0 * kPointerSize)); // Fp-to-sp delta.
184  __ mov(r5, Operand(ExternalReference::isolate_address(isolate())));
185  __ str(r5, MemOperand(sp, 1 * kPointerSize)); // Isolate.
186  // Call Deoptimizer::New().
187  {
188  AllowExternalCallThatCantCauseGC scope(masm());
189  __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate()), 6);
190  }
191 
192  // Preserve "deoptimizer" object in register r0 and get the input
193  // frame descriptor pointer to r1 (deoptimizer->input_);
195 
196  // Copy core registers into FrameDescription::registers_[kNumRegisters].
198  for (int i = 0; i < kNumberOfRegisters; i++) {
200  __ ldr(r2, MemOperand(sp, i * kPointerSize));
201  __ str(r2, MemOperand(r1, offset));
202  }
203 
204  // Copy VFP registers to
205  // double_registers_[DoubleRegister::kMaxNumAllocatableRegisters]
206  int double_regs_offset = FrameDescription::double_registers_offset();
207  for (int i = 0; i < DwVfpRegister::kMaxNumAllocatableRegisters; ++i) {
208  int dst_offset = i * kDoubleSize + double_regs_offset;
209  int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
210  __ vldr(d0, sp, src_offset);
211  __ vstr(d0, r1, dst_offset);
212  }
213 
214  // Remove the bailout id and the saved registers from the stack.
215  __ add(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
216 
217  // Compute a pointer to the unwinding limit in register r2; that is
218  // the first stack slot not part of the input frame.
220  __ add(r2, r2, sp);
221 
222  // Unwind the stack down to - but not including - the unwinding
223  // limit and copy the contents of the activation frame to the input
224  // frame description.
226  Label pop_loop;
227  Label pop_loop_header;
228  __ b(&pop_loop_header);
229  __ bind(&pop_loop);
230  __ pop(r4);
231  __ str(r4, MemOperand(r3, 0));
232  __ add(r3, r3, Operand(sizeof(uint32_t)));
233  __ bind(&pop_loop_header);
234  __ cmp(r2, sp);
235  __ b(ne, &pop_loop);
236 
237  // Compute the output frame in the deoptimizer.
238  __ push(r0); // Preserve deoptimizer object across call.
239  // r0: deoptimizer object; r1: scratch.
240  __ PrepareCallCFunction(1, r1);
241  // Call Deoptimizer::ComputeOutputFrames().
242  {
243  AllowExternalCallThatCantCauseGC scope(masm());
244  __ CallCFunction(
245  ExternalReference::compute_output_frames_function(isolate()), 1);
246  }
247  __ pop(r0); // Restore deoptimizer object (class Deoptimizer).
248 
249  // Replace the current (input) frame with the output frames.
250  Label outer_push_loop, inner_push_loop,
251  outer_loop_header, inner_loop_header;
252  // Outer loop state: r4 = current "FrameDescription** output_",
253  // r1 = one past the last FrameDescription**.
255  __ ldr(r4, MemOperand(r0, Deoptimizer::output_offset())); // r4 is output_.
256  __ add(r1, r4, Operand(r1, LSL, 2));
257  __ jmp(&outer_loop_header);
258  __ bind(&outer_push_loop);
259  // Inner loop state: r2 = current FrameDescription*, r3 = loop index.
260  __ ldr(r2, MemOperand(r4, 0)); // output_[ix]
262  __ jmp(&inner_loop_header);
263  __ bind(&inner_push_loop);
264  __ sub(r3, r3, Operand(sizeof(uint32_t)));
265  __ add(r6, r2, Operand(r3));
267  __ push(r6);
268  __ bind(&inner_loop_header);
269  __ cmp(r3, Operand::Zero());
270  __ b(ne, &inner_push_loop); // test for gt?
271  __ add(r4, r4, Operand(kPointerSize));
272  __ bind(&outer_loop_header);
273  __ cmp(r4, r1);
274  __ b(lt, &outer_push_loop);
275 
276  // Check CPU flags for number of registers, setting the Z condition flag.
277  __ CheckFor32DRegs(ip);
278 
281  for (int i = 0; i < DwVfpRegister::kMaxNumRegisters; ++i) {
282  if (i == kDoubleRegZero.code()) continue;
283  if (i == kScratchDoubleReg.code()) continue;
284 
285  const DwVfpRegister reg = DwVfpRegister::from_code(i);
286  __ vldr(reg, r1, src_offset, i < 16 ? al : ne);
287  src_offset += kDoubleSize;
288  }
289 
290  // Push state, pc, and continuation from the last output frame.
292  __ push(r6);
294  __ push(r6);
296  __ push(r6);
297 
298  // Push the registers from the last output frame.
299  for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
301  __ ldr(r6, MemOperand(r2, offset));
302  __ push(r6);
303  }
304 
305  // Restore the registers from the stack.
306  __ ldm(ia_w, sp, restored_regs); // all but pc registers.
307  __ pop(ip); // remove sp
308  __ pop(ip); // remove lr
309 
310  __ InitializeRootRegister();
311 
312  __ pop(ip); // remove pc
313  __ pop(ip); // get continuation, leave pc on stack
314  __ pop(lr);
315  __ Jump(ip);
316  __ stop("Unreachable.");
317 }
318 
319 
321  // Create a sequence of deoptimization entries.
322  // Note that registers are still live when jumping to an entry.
323  Label done;
324  for (int i = 0; i < count(); i++) {
325  int start = masm()->pc_offset();
326  USE(start);
327  __ mov(ip, Operand(i));
328  __ b(&done);
329  DCHECK(masm()->pc_offset() - start == table_entry_size_);
330  }
331  __ bind(&done);
332  __ push(ip);
333 }
334 
335 
336 void FrameDescription::SetCallerPc(unsigned offset, intptr_t value) {
337  SetFrameSlot(offset, value);
338 }
339 
340 
341 void FrameDescription::SetCallerFp(unsigned offset, intptr_t value) {
342  SetFrameSlot(offset, value);
343 }
344 
345 
346 void FrameDescription::SetCallerConstantPool(unsigned offset, intptr_t value) {
347  DCHECK(FLAG_enable_ool_constant_pool);
348  SetFrameSlot(offset, value);
349 }
350 
351 
352 #undef __
353 
354 } } // namespace v8::internal
#define kDoubleRegZero
#define kScratchDoubleReg
static const int kInstrSize
void bkpt(uint32_t imm16)
Address deoptimization_handler() const
Definition: code-stubs.h:413
void InvalidateRelocation()
Definition: objects.cc:10104
byte * instruction_end()
Definition: objects-inl.h:6181
byte * instruction_start()
Definition: objects-inl.h:6176
byte * FindCodeAgeSequence()
Definition: objects.cc:10498
static int output_offset()
Definition: deoptimizer.h:239
static const int table_entry_size_
Definition: deoptimizer.h:451
void CopyDoubleRegisters(FrameDescription *output_frame)
static Address GetDeoptimizationEntry(Isolate *isolate, int id, BailoutType type, GetEntryMode mode=ENSURE_ENTRY_CODE)
Definition: deoptimizer.cc:672
static void PatchCodeForDeoptimization(Isolate *isolate, Code *code)
static int output_count_offset()
Definition: deoptimizer.h:236
void SetPlatformCompiledStubRegisters(FrameDescription *output_frame, CodeStubDescriptor *desc)
bool HasAlignmentPadding(JSFunction *function)
FrameDescription * input_
Definition: deoptimizer.h:415
Isolate * isolate() const
Definition: deoptimizer.h:292
void FillInputFrame(Address tos, JavaScriptFrame *frame)
void SetCallerFp(unsigned offset, intptr_t value)
uint32_t GetFrameSize() const
Definition: deoptimizer.h:477
void SetCallerConstantPool(unsigned offset, intptr_t value)
double GetDoubleRegister(unsigned n) const
Definition: deoptimizer.h:518
static int double_registers_offset()
Definition: deoptimizer.h:574
void SetRegister(unsigned n, intptr_t value)
Definition: deoptimizer.h:523
void SetCallerPc(unsigned offset, intptr_t value)
void SetFrameSlot(unsigned offset, intptr_t value)
Definition: deoptimizer.h:495
void SetDoubleRegister(unsigned n, double value)
Definition: deoptimizer.h:528
void Call(Register target, Condition cond=al)
static int CallSizeNotPredictableCodeSize(Isolate *isolate, Address target, RelocInfo::Mode rmode, Condition cond=al)
static uint32_t & uint32_at(Address addr)
Definition: v8memory.h:24
#define __
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes enable context specialization in TurboFan execution budget before interrupt is triggered max percentage of megamorphic generic ICs to allow optimization enable use of SAHF instruction if enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable use of MLS instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long enable alignment of csp to bytes on platforms which prefer the register to always be NULL
#define DCHECK(condition)
Definition: logging.h:205
void USE(T)
Definition: macros.h:322
const int kPointerSize
Definition: globals.h:129
const Register r2
const RegList kJSCallerSaved
Definition: frames-arm.h:24
const Register r6
const Register r0
const RegList kCalleeSaved
Definition: frames-arm.h:38
const LowDwVfpRegister d0
const Register ip
const int kDoubleSize
Definition: globals.h:127
const Register r3
const DwVfpRegister d31
const Register fp
const Register sp
const Register r4
const Register pc
const Register r5
uint32_t RegList
Definition: frames.h:18
const Register lr
byte * Address
Definition: globals.h:101
const Register r1
const DwVfpRegister d16
const LowDwVfpRegister d13
const unsigned kNumberOfRegisters
static const int kNoCodeAgeSequenceLength
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20
static DwVfpRegister from_code(int code)
static const int kMaxNumAllocatableRegisters
static const int kMaxNumRegisters
static const int kNumRegisters
Definition: assembler-arm.h:95