| Index: runtime/vm/stub_code_arm.cc
 | 
| ===================================================================
 | 
| --- runtime/vm/stub_code_arm.cc	(revision 21968)
 | 
| +++ runtime/vm/stub_code_arm.cc	(working copy)
 | 
| @@ -310,13 +310,100 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +DECLARE_LEAF_RUNTIME_ENTRY(intptr_t, DeoptimizeCopyFrame,
 | 
| +                           intptr_t deopt_reason,
 | 
| +                           uword saved_registers_address);
 | 
| +
 | 
| +DECLARE_LEAF_RUNTIME_ENTRY(void, DeoptimizeFillFrame, uword last_fp);
 | 
| +
 | 
| +
 | 
| +// Used by eager and lazy deoptimization. Preserve result in R0 if necessary.
 | 
| +// This stub translates optimized frame into unoptimized frame. The optimized
 | 
| +// frame can contain values in registers and on stack, the unoptimized
 | 
| +// frame contains all values on stack.
 | 
| +// Deoptimization occurs in following steps:
 | 
| +// - Push all registers that can contain values.
 | 
| +// - Call C routine to copy the stack and saved registers into temporary buffer.
 | 
| +// - Adjust caller's frame to correct unoptimized frame size.
 | 
| +// - Fill the unoptimized frame.
 | 
| +// - Materialize objects that require allocation (e.g. Double instances).
 | 
| +// GC can occur only after frame is fully rewritten.
 | 
| +// Stack after EnterFrame(...) below:
 | 
| +//   +------------------+
 | 
| +//   | Saved FP         | <- TOS
 | 
| +//   +------------------+
 | 
| +//   | return-address   |  (deoptimization point)
 | 
| +//   +------------------+
 | 
| +//   | optimized frame  |
 | 
| +//   |  ...             |
 | 
| +//
 | 
| +// Parts of the code cannot GC, part of the code can GC.
 | 
| +static void GenerateDeoptimizationSequence(Assembler* assembler,
 | 
| +                                           bool preserve_result) {
 | 
| +  __ EnterFrame((1 << FP) | (1 << LR), 0);
 | 
| +  // The code in this frame may not cause GC. kDeoptimizeCopyFrameRuntimeEntry
 | 
| +  // and kDeoptimizeFillFrameRuntimeEntry are leaf runtime calls.
 | 
| +  const intptr_t saved_r0_offset_from_fp = -(kNumberOfCpuRegisters - R0);
 | 
| +  // Result in R0 is preserved as part of pushing all registers below.
 | 
| +
 | 
| +  // Push registers in their enumeration order: lowest register number at
 | 
| +  // lowest address.
 | 
| +  __ PushList(kAllCpuRegistersList);
 | 
| +  ASSERT(kFpuRegisterSize == 2 * kWordSize);
 | 
| +  __ vstmd(DB_W, SP, D0, static_cast<DRegister>(kNumberOfDRegisters - 1));
 | 
| +
 | 
| +  __ mov(R0, ShifterOperand(SP));  // Pass address of saved registers block.
 | 
| +  __ ReserveAlignedFrameSpace(0);
 | 
| +  __ CallRuntime(kDeoptimizeCopyFrameRuntimeEntry);
 | 
| +  // Result (R0) is stack-size (FP - SP) in bytes, incl. the return address.
 | 
| +
 | 
| +  if (preserve_result) {
 | 
| +    // Restore result into R1 temporarily.
 | 
| +    __ ldr(R1, Address(FP, saved_r0_offset_from_fp * kWordSize));
 | 
| +  }
 | 
| +
 | 
| +  __ LeaveFrame((1 << FP) | (1 << LR));
 | 
| +  __ sub(SP, FP, ShifterOperand(R0));
 | 
| +
 | 
| +  __ EnterFrame((1 << FP) | (1 << LR), 0);
 | 
| +  __ mov(R0, ShifterOperand(SP));  // Get last FP address.
 | 
| +  if (preserve_result) {
 | 
| +    __ Push(R1);  // Preserve result.
 | 
| +  }
 | 
| +  __ ReserveAlignedFrameSpace(0);
 | 
| +  __ CallRuntime(kDeoptimizeFillFrameRuntimeEntry);  // Pass last FP in R0.
 | 
| +  // Result (R0) is our FP.
 | 
| +  if (preserve_result) {
 | 
| +    // Restore result into R1.
 | 
| +    __ ldr(R1, Address(FP, -1 * kWordSize));
 | 
| +  }
 | 
| +  // Code above cannot cause GC.
 | 
| +  __ LeaveFrame((1 << FP) | (1 << LR));
 | 
| +  __ mov(FP, ShifterOperand(R0));
 | 
| +
 | 
| +  // Frame is fully rewritten at this point and it is safe to perform a GC.
 | 
| +  // Materialize any objects that were deferred by FillFrame because they
 | 
| +  // require allocation.
 | 
| +  __ EnterStubFrame();
 | 
| +  if (preserve_result) {
 | 
| +    __ Push(R1);  // Preserve result, it will be GC-d here.
 | 
| +  }
 | 
| +  __ CallRuntime(kDeoptimizeMaterializeDoublesRuntimeEntry);
 | 
| +  if (preserve_result) {
 | 
| +    __ Pop(R0);  // Restore result.
 | 
| +  }
 | 
| +  __ LeaveStubFrame();
 | 
| +  __ Ret();
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void StubCode::GenerateDeoptimizeLazyStub(Assembler* assembler) {
 | 
|    __ Unimplemented("DeoptimizeLazy stub");
 | 
|  }
 | 
|  
 | 
|  
 | 
|  void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
 | 
| -  __ Unimplemented("Deoptimize stub");
 | 
| +  GenerateDeoptimizationSequence(assembler, false);  // Don't preserve R0.
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -1151,8 +1238,35 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +//  R6: function object.
 | 
| +//  R5: inline cache data object.
 | 
| +//  R4: arguments descriptor array.
 | 
|  void StubCode::GenerateOptimizedUsageCounterIncrement(Assembler* assembler) {
 | 
| -  __ Unimplemented("OptimizedUsageCounterIncrement stub");
 | 
| +  Register ic_reg = R5;
 | 
| +  Register func_reg = R6;
 | 
| +  if (FLAG_trace_optimized_ic_calls) {
 | 
| +    __ EnterStubFrame();
 | 
| +    __ PushList((1 << R4) | (1 << R5) | (1 << R6));  // Preserve.
 | 
| +    __ Push(ic_reg);  // Argument.
 | 
| +    __ Push(func_reg);  // Argument.
 | 
| +    __ CallRuntime(kTraceICCallRuntimeEntry);
 | 
| +    __ Drop(2);  // Discard argument;
 | 
| +    __ PushList((1 << R4) | (1 << R5) | (1 << R6));  // Restore.
 | 
| +    __ LeaveStubFrame();
 | 
| +  }
 | 
| +  __ ldr(R7, FieldAddress(func_reg, Function::usage_counter_offset()));
 | 
| +  Label is_hot;
 | 
| +  if (FlowGraphCompiler::CanOptimize()) {
 | 
| +    ASSERT(FLAG_optimization_counter_threshold > 1);
 | 
| +    __ CompareImmediate(R7, FLAG_optimization_counter_threshold);
 | 
| +    __ b(&is_hot, GE);
 | 
| +    // As long as VM has no OSR do not optimize in the middle of the function
 | 
| +    // but only at exit so that we have collected all type feedback before
 | 
| +    // optimizing.
 | 
| +  }
 | 
| +  __ add(R7, R7, ShifterOperand(1));
 | 
| +  __ str(R7, FieldAddress(func_reg, Function::usage_counter_offset()));
 | 
| +  __ Bind(&is_hot);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -1405,7 +1519,7 @@
 | 
|  
 | 
|  
 | 
|  //  LR: return address (Dart code).
 | 
| -//  R4: Arguments descriptor array.
 | 
| +//  R4: arguments descriptor array.
 | 
|  void StubCode::GenerateBreakpointStaticStub(Assembler* assembler) {
 | 
|    // Create a stub frame as we are pushing some objects on the stack before
 | 
|    // calling into the runtime.
 | 
| @@ -1444,8 +1558,8 @@
 | 
|  
 | 
|  
 | 
|  //  LR: return address (Dart code).
 | 
| -//  R5: Inline cache data array.
 | 
| -//  R4: Arguments descriptor array.
 | 
| +//  R5: inline cache data array.
 | 
| +//  R4: arguments descriptor array.
 | 
|  void StubCode::GenerateBreakpointDynamicStub(Assembler* assembler) {
 | 
|    // Create a stub frame as we are pushing some objects on the stack before
 | 
|    // calling into the runtime.
 | 
| 
 |