Deoptimization support in optimizing compiler for setting local values
Due to compiler optimizations, we may not always be able to update
the value of a local variable in a compiled frame (like a variable
seen as constant by the compiler). To avoid that situation, we simply
deoptimize compiled frames updated by the debugger so they are
executed by the interpreter with the updated value.
When the debugger attempts to set a local variable (actually a DEX
register or a pair of registers) in a compiled frame, we allocate a
ShadowFrame associated to that frame (using its frame id) and set the
new value in that ShadowFrame. When we know we are about to continue
the execution of the compiled frame, we deoptimize the stack using
the preallocated ShadowFrame (instead of creating a new one). We
initialize it with the current value of all DEX registers except
the ones that have been set by the debugger. Therefore, the
ShadowFrame represent the runtime context modified by the debugger.
Bumps oat version to force recompilation.
Bug: 19944235
Change-Id: I0ebe6241264f7a3be0f14ee4516c1f7436e04da6
diff --git a/runtime/stack.cc b/runtime/stack.cc
index d956f0e..d739743 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -220,10 +220,37 @@
return vreg < num_regs && TestBitmap(vreg, reg_bitmap);
}
+bool StackVisitor::GetVRegFromDebuggerShadowFrame(uint16_t vreg,
+ VRegKind kind,
+ uint32_t* val) const {
+ size_t frame_id = const_cast<StackVisitor*>(this)->GetFrameId();
+ ShadowFrame* shadow_frame = thread_->FindDebuggerShadowFrame(frame_id);
+ if (shadow_frame != nullptr) {
+ bool* updated_vreg_flags = thread_->GetUpdatedVRegFlags(frame_id);
+ DCHECK(updated_vreg_flags != nullptr);
+ if (updated_vreg_flags[vreg]) {
+ // Value is set by the debugger.
+ if (kind == kReferenceVReg) {
+ *val = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(
+ shadow_frame->GetVRegReference(vreg)));
+ } else {
+ *val = shadow_frame->GetVReg(vreg);
+ }
+ return true;
+ }
+ }
+ // No value is set by the debugger.
+ return false;
+}
+
bool StackVisitor::GetVReg(ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* val) const {
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
+ // Check if there is value set by the debugger.
+ if (GetVRegFromDebuggerShadowFrame(vreg, kind, val)) {
+ return true;
+ }
if (m->IsOptimized(sizeof(void*))) {
return GetVRegFromOptimizedCode(m, vreg, kind, val);
} else {
@@ -267,7 +294,6 @@
// its instructions?
uint16_t number_of_dex_registers = code_item->registers_size_;
DCHECK_LT(vreg, code_item->registers_size_);
-
ArtMethod* outer_method = *GetCurrentQuickFrame();
const void* code_pointer = outer_method->GetQuickOatCodePointer(sizeof(void*));
DCHECK(code_pointer != nullptr);
@@ -348,6 +374,20 @@
return true;
}
+bool StackVisitor::GetVRegPairFromDebuggerShadowFrame(uint16_t vreg,
+ VRegKind kind_lo,
+ VRegKind kind_hi,
+ uint64_t* val) const {
+ uint32_t low_32bits;
+ uint32_t high_32bits;
+ bool success = GetVRegFromDebuggerShadowFrame(vreg, kind_lo, &low_32bits);
+ success &= GetVRegFromDebuggerShadowFrame(vreg + 1, kind_hi, &high_32bits);
+ if (success) {
+ *val = (static_cast<uint64_t>(high_32bits) << 32) | static_cast<uint64_t>(low_32bits);
+ }
+ return success;
+}
+
bool StackVisitor::GetVRegPair(ArtMethod* m, uint16_t vreg, VRegKind kind_lo,
VRegKind kind_hi, uint64_t* val) const {
if (kind_lo == kLongLoVReg) {
@@ -358,6 +398,10 @@
LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi;
UNREACHABLE();
}
+ // Check if there is value set by the debugger.
+ if (GetVRegPairFromDebuggerShadowFrame(vreg, kind_lo, kind_hi, val)) {
+ return true;
+ }
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
@@ -435,17 +479,17 @@
bool StackVisitor::SetVReg(ArtMethod* m, uint16_t vreg, uint32_t new_value,
VRegKind kind) {
if (cur_quick_frame_ != nullptr) {
- DCHECK(context_ != nullptr); // You can't reliably write registers without a context.
- DCHECK(m == GetMethod());
- if (m->IsOptimized(sizeof(void*))) {
- return false;
- } else {
- return SetVRegFromQuickCode(m, vreg, new_value, kind);
- }
+ DCHECK(context_ != nullptr); // You can't reliably write registers without a context.
+ DCHECK(m == GetMethod());
+ if (m->IsOptimized(sizeof(void*))) {
+ return false;
} else {
- cur_shadow_frame_->SetVReg(vreg, new_value);
- return true;
+ return SetVRegFromQuickCode(m, vreg, new_value, kind);
}
+ } else {
+ cur_shadow_frame_->SetVReg(vreg, new_value);
+ return true;
+ }
}
bool StackVisitor::SetVRegFromQuickCode(ArtMethod* m, uint16_t vreg, uint32_t new_value,
@@ -475,6 +519,34 @@
}
}
+bool StackVisitor::SetVRegFromDebugger(ArtMethod* m,
+ uint16_t vreg,
+ uint32_t new_value,
+ VRegKind kind) {
+ const DexFile::CodeItem* code_item = m->GetCodeItem();
+ if (code_item == nullptr) {
+ return false;
+ }
+ ShadowFrame* shadow_frame = GetCurrentShadowFrame();
+ if (shadow_frame == nullptr) {
+ // This is a compiled frame: we must prepare and update a shadow frame that will
+ // be executed by the interpreter after deoptimization of the stack.
+ const size_t frame_id = GetFrameId();
+ const uint16_t num_regs = code_item->registers_size_;
+ shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc());
+ CHECK(shadow_frame != nullptr);
+ // Remember the vreg has been set for debugging and must not be overwritten by the
+ // original value during deoptimization of the stack.
+ thread_->GetUpdatedVRegFlags(frame_id)[vreg] = true;
+ }
+ if (kind == kReferenceVReg) {
+ shadow_frame->SetVRegReference(vreg, reinterpret_cast<mirror::Object*>(new_value));
+ } else {
+ shadow_frame->SetVReg(vreg, new_value);
+ }
+ return true;
+}
+
bool StackVisitor::SetRegisterIfAccessible(uint32_t reg, uint32_t new_value, VRegKind kind) {
const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
if (!IsAccessibleRegister(reg, is_float)) {
@@ -557,6 +629,39 @@
}
}
+bool StackVisitor::SetVRegPairFromDebugger(ArtMethod* m,
+ uint16_t vreg,
+ uint64_t new_value,
+ VRegKind kind_lo,
+ VRegKind kind_hi) {
+ if (kind_lo == kLongLoVReg) {
+ DCHECK_EQ(kind_hi, kLongHiVReg);
+ } else if (kind_lo == kDoubleLoVReg) {
+ DCHECK_EQ(kind_hi, kDoubleHiVReg);
+ } else {
+ LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi;
+ UNREACHABLE();
+ }
+ const DexFile::CodeItem* code_item = m->GetCodeItem();
+ if (code_item == nullptr) {
+ return false;
+ }
+ ShadowFrame* shadow_frame = GetCurrentShadowFrame();
+ if (shadow_frame == nullptr) {
+ // This is a compiled frame: we must prepare for deoptimization (see SetVRegFromDebugger).
+ const size_t frame_id = GetFrameId();
+ const uint16_t num_regs = code_item->registers_size_;
+ shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc());
+ CHECK(shadow_frame != nullptr);
+ // Remember the vreg pair has been set for debugging and must not be overwritten by the
+ // original value during deoptimization of the stack.
+ thread_->GetUpdatedVRegFlags(frame_id)[vreg] = true;
+ thread_->GetUpdatedVRegFlags(frame_id)[vreg + 1] = true;
+ }
+ shadow_frame->SetVRegLong(vreg, new_value);
+ return true;
+}
+
bool StackVisitor::SetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi,
uint64_t new_value, bool is_float) {
if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {