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. |