Index: src/debug.cc |
diff --git a/src/debug.cc b/src/debug.cc |
index 41aa5c564c0a006686553e8e80a344182d63cb6d..530cf30dd195b99d9cae6c8877d9d2145aba6473 100644 |
--- a/src/debug.cc |
+++ b/src/debug.cc |
@@ -1881,59 +1881,6 @@ static void CollectActiveFunctionsFromThread( |
} |
-// Figure out how many bytes of "pc_offset" correspond to actual code by |
-// subtracting off the bytes that correspond to constant/veneer pools. See |
-// Assembler::CheckConstPool() and Assembler::CheckVeneerPool(). Note that this |
-// is only useful for architectures using constant pools or veneer pools. |
-static int ComputeCodeOffsetFromPcOffset(Code *code, int pc_offset) { |
- ASSERT_EQ(code->kind(), Code::FUNCTION); |
- ASSERT(!code->has_debug_break_slots()); |
- ASSERT_LE(0, pc_offset); |
- ASSERT_LT(pc_offset, code->instruction_end() - code->instruction_start()); |
- |
- int mask = RelocInfo::ModeMask(RelocInfo::CONST_POOL) | |
- RelocInfo::ModeMask(RelocInfo::VENEER_POOL); |
- byte *pc = code->instruction_start() + pc_offset; |
- int code_offset = pc_offset; |
- for (RelocIterator it(code, mask); !it.done(); it.next()) { |
- RelocInfo* info = it.rinfo(); |
- if (info->pc() >= pc) break; |
- ASSERT(RelocInfo::IsConstPool(info->rmode())); |
- code_offset -= static_cast<int>(info->data()); |
- ASSERT_LE(0, code_offset); |
- } |
- |
- return code_offset; |
-} |
- |
- |
-// The inverse of ComputeCodeOffsetFromPcOffset. |
-static int ComputePcOffsetFromCodeOffset(Code *code, int code_offset) { |
- ASSERT_EQ(code->kind(), Code::FUNCTION); |
- |
- int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | |
- RelocInfo::ModeMask(RelocInfo::CONST_POOL) | |
- RelocInfo::ModeMask(RelocInfo::VENEER_POOL); |
- int reloc = 0; |
- for (RelocIterator it(code, mask); !it.done(); it.next()) { |
- RelocInfo* info = it.rinfo(); |
- if (info->pc() - code->instruction_start() - reloc >= code_offset) break; |
- if (RelocInfo::IsDebugBreakSlot(info->rmode())) { |
- reloc += Assembler::kDebugBreakSlotLength; |
- } else { |
- ASSERT(RelocInfo::IsConstPool(info->rmode())); |
- reloc += static_cast<int>(info->data()); |
- } |
- } |
- |
- int pc_offset = code_offset + reloc; |
- |
- ASSERT_LT(code->instruction_start() + pc_offset, code->instruction_end()); |
- |
- return pc_offset; |
-} |
- |
- |
static void RedirectActivationsToRecompiledCodeOnThread( |
Isolate* isolate, |
ThreadLocalTop* top) { |
@@ -1955,12 +1902,51 @@ static void RedirectActivationsToRecompiledCodeOnThread( |
continue; |
} |
- int old_pc_offset = frame->pc() - frame_code->instruction_start(); |
- int code_offset = ComputeCodeOffsetFromPcOffset(*frame_code, old_pc_offset); |
- int new_pc_offset = ComputePcOffsetFromCodeOffset(*new_code, code_offset); |
+ // Iterate over the RelocInfo in the original code to compute the sum of the |
+ // constant pools and veneer pools sizes. (See Assembler::CheckConstPool() |
+ // and Assembler::CheckVeneerPool()) |
+ // Note that this is only useful for architectures using constant pools or |
+ // veneer pools. |
+ int pool_mask = RelocInfo::ModeMask(RelocInfo::CONST_POOL) | |
+ RelocInfo::ModeMask(RelocInfo::VENEER_POOL); |
+ int frame_pool_size = 0; |
+ for (RelocIterator it(*frame_code, pool_mask); !it.done(); it.next()) { |
+ RelocInfo* info = it.rinfo(); |
+ if (info->pc() >= frame->pc()) break; |
+ frame_pool_size += static_cast<int>(info->data()); |
+ } |
+ intptr_t frame_offset = |
+ frame->pc() - frame_code->instruction_start() - frame_pool_size; |
+ |
+ // Iterate over the RelocInfo for new code to find the number of bytes |
+ // generated for debug slots and constant pools. |
+ int debug_break_slot_bytes = 0; |
+ int new_code_pool_size = 0; |
+ int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | |
+ RelocInfo::ModeMask(RelocInfo::CONST_POOL) | |
+ RelocInfo::ModeMask(RelocInfo::VENEER_POOL); |
+ for (RelocIterator it(*new_code, mask); !it.done(); it.next()) { |
+ // Check if the pc in the new code with debug break |
+ // slots is before this slot. |
+ RelocInfo* info = it.rinfo(); |
+ intptr_t new_offset = info->pc() - new_code->instruction_start() - |
+ new_code_pool_size - debug_break_slot_bytes; |
+ if (new_offset >= frame_offset) { |
+ break; |
+ } |
+ |
+ if (RelocInfo::IsDebugBreakSlot(info->rmode())) { |
+ debug_break_slot_bytes += Assembler::kDebugBreakSlotLength; |
+ } else { |
+ ASSERT(RelocInfo::IsConstPool(info->rmode())); |
+ // The size of the pools is encoded in the data. |
+ new_code_pool_size += static_cast<int>(info->data()); |
+ } |
+ } |
// Compute the equivalent pc in the new code. |
- byte* new_pc = new_code->instruction_start() + new_pc_offset; |
+ byte* new_pc = new_code->instruction_start() + frame_offset + |
+ debug_break_slot_bytes + new_code_pool_size; |
if (FLAG_trace_deopt) { |
PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) " |
@@ -2016,55 +2002,6 @@ class ActiveFunctionsRedirector : public ThreadVisitor { |
}; |
-class ForceDebuggerActive { |
- public: |
- explicit ForceDebuggerActive(Isolate *isolate) { |
- isolate_ = isolate; |
- old_state_ = isolate->debugger()->force_debugger_active(); |
- isolate_->debugger()->set_force_debugger_active(true); |
- } |
- |
- ~ForceDebuggerActive() { |
- isolate_->debugger()->set_force_debugger_active(old_state_); |
- } |
- |
- private: |
- Isolate *isolate_; |
- bool old_state_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ForceDebuggerActive); |
-}; |
- |
- |
-void Debug::MaybeRecompileFunctionForDebugging(Handle<JSFunction> function) { |
- ASSERT_EQ(Code::FUNCTION, function->code()->kind()); |
- ASSERT_EQ(function->code(), function->shared()->code()); |
- |
- if (function->code()->has_debug_break_slots()) return; |
- |
- ForceDebuggerActive force_debugger_active(isolate_); |
- MaybeHandle<Code> code = Compiler::GetCodeForDebugging(function); |
- // Recompilation can fail. In that case leave the code as it was. |
- if (!code.is_null()) |
- function->ReplaceCode(*code.ToHandleChecked()); |
- ASSERT_EQ(function->code(), function->shared()->code()); |
-} |
- |
- |
-void Debug::RecompileAndRelocateSuspendedGenerators( |
- const List<Handle<JSGeneratorObject> > &generators) { |
- for (int i = 0; i < generators.length(); i++) { |
- Handle<JSFunction> fun(generators[i]->function()); |
- |
- MaybeRecompileFunctionForDebugging(fun); |
- |
- int code_offset = generators[i]->continuation(); |
- int pc_offset = ComputePcOffsetFromCodeOffset(fun->code(), code_offset); |
- generators[i]->set_continuation(pc_offset); |
- } |
-} |
- |
- |
void Debug::PrepareForBreakPoints() { |
// If preparing for the first break point make sure to deoptimize all |
// functions as debugging does not work with optimized code. |
@@ -2084,21 +2021,6 @@ void Debug::PrepareForBreakPoints() { |
// is used both in GC and non-GC code. |
List<Handle<JSFunction> > active_functions(100); |
- // A list of all suspended generators. |
- List<Handle<JSGeneratorObject> > suspended_generators; |
- |
- // A list of all generator functions. We need to recompile all functions, |
- // but we don't know until after visiting the whole heap which generator |
- // functions have suspended activations and which do not. As in the case of |
- // functions with activations on the stack, we need to be careful with |
- // generator functions with suspended activations because although they |
- // should be recompiled, recompilation can fail, and we need to avoid |
- // leaving the heap in an inconsistent state. |
- // |
- // We could perhaps avoid this list and instead re-use the GC metadata |
- // links. |
- List<Handle<JSFunction> > generator_functions; |
- |
{ |
// We are going to iterate heap to find all functions without |
// debug break slots. |
@@ -2106,9 +2028,6 @@ void Debug::PrepareForBreakPoints() { |
heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
"preparing for breakpoints"); |
- // Collecting the generators should not alter iterability of the heap. |
- ASSERT(heap->IsHeapIterable()); |
- |
// Ensure no GC in this scope as we are going to use gc_metadata |
// field in the Code object to mark active functions. |
DisallowHeapAllocation no_allocation; |
@@ -2139,11 +2058,6 @@ void Debug::PrepareForBreakPoints() { |
if (function->IsBuiltin()) continue; |
if (shared->code()->gc_metadata() == active_code_marker) continue; |
- if (shared->is_generator()) { |
- generator_functions.Add(Handle<JSFunction>(function, isolate_)); |
- continue; |
- } |
- |
Code::Kind kind = function->code()->kind(); |
if (kind == Code::FUNCTION && |
!function->code()->has_debug_break_slots()) { |
@@ -2163,24 +2077,6 @@ void Debug::PrepareForBreakPoints() { |
function->shared()->set_code(*lazy_compile); |
} |
} |
- } else if (obj->IsJSGeneratorObject()) { |
- JSGeneratorObject* gen = JSGeneratorObject::cast(obj); |
- if (!gen->is_suspended()) continue; |
- |
- JSFunction* fun = gen->function(); |
- ASSERT_EQ(fun->code()->kind(), Code::FUNCTION); |
- if (fun->code()->has_debug_break_slots()) continue; |
- |
- int pc_offset = gen->continuation(); |
- ASSERT_LT(0, pc_offset); |
- |
- int code_offset = |
- ComputeCodeOffsetFromPcOffset(fun->code(), pc_offset); |
- |
- // This will be fixed after we recompile the functions. |
- gen->set_continuation(code_offset); |
- |
- suspended_generators.Add(Handle<JSGeneratorObject>(gen, isolate_)); |
} |
} |
@@ -2191,35 +2087,42 @@ void Debug::PrepareForBreakPoints() { |
} |
} |
- // Recompile generator functions that have suspended activations, and |
- // relocate those activations. |
- RecompileAndRelocateSuspendedGenerators(suspended_generators); |
- |
- // Mark generator functions that didn't have suspended activations for lazy |
- // recompilation. Note that this set does not include any active functions. |
- for (int i = 0; i < generator_functions.length(); i++) { |
- Handle<JSFunction> &function = generator_functions[i]; |
- if (function->code()->kind() != Code::FUNCTION) continue; |
- if (function->code()->has_debug_break_slots()) continue; |
- function->set_code(*lazy_compile); |
- function->shared()->set_code(*lazy_compile); |
- } |
- |
// Now recompile all functions with activation frames and and |
- // patch the return address to run in the new compiled code. It could be |
- // that some active functions were recompiled already by the suspended |
- // generator recompilation pass above; a generator with suspended |
- // activations could also have active activations. That's fine. |
+ // patch the return address to run in the new compiled code. |
for (int i = 0; i < active_functions.length(); i++) { |
Handle<JSFunction> function = active_functions[i]; |
Handle<SharedFunctionInfo> shared(function->shared()); |
+ if (function->code()->kind() == Code::FUNCTION && |
+ function->code()->has_debug_break_slots()) { |
+ // Nothing to do. Function code already had debug break slots. |
+ continue; |
+ } |
+ |
// If recompilation is not possible just skip it. |
- if (shared->is_toplevel()) continue; |
- if (!shared->allows_lazy_compilation()) continue; |
- if (shared->code()->kind() == Code::BUILTIN) continue; |
+ if (shared->is_toplevel() || |
+ !shared->allows_lazy_compilation() || |
+ shared->code()->kind() == Code::BUILTIN) { |
+ continue; |
+ } |
+ |
+ // Make sure that the shared full code is compiled with debug |
+ // break slots. |
+ if (!shared->code()->has_debug_break_slots()) { |
+ // Try to compile the full code with debug break slots. If it |
+ // fails just keep the current code. |
+ bool prev_force_debugger_active = |
+ isolate_->debugger()->force_debugger_active(); |
+ isolate_->debugger()->set_force_debugger_active(true); |
+ Handle<Code> code = Compiler::GetCodeForDebugging( |
+ function).ToHandleChecked(); |
+ function->ReplaceCode(*code); |
+ isolate_->debugger()->set_force_debugger_active( |
+ prev_force_debugger_active); |
+ } |
- MaybeRecompileFunctionForDebugging(function); |
+ // Keep function code in sync with shared function info. |
+ function->set_code(shared->code()); |
} |
RedirectActivationsToRecompiledCodeOnThread(isolate_, |