Commit a7717bdf authored by Yang Guo's avatar Yang Guo Committed by Commit Bot

[debug] no longer iterate the heap to find optimized code.

And some refactoring to the existing code for LiveEdit.

R=jarin@chromium.org

Change-Id: Ic1d626db9722b39cbcd83bf6878fc24d6094e612
Reviewed-on: https://chromium-review.googlesource.com/687014
Commit-Queue: Yang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48233}
parent 3c4bc27f
......@@ -555,7 +555,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function,
// Make sure the function is compiled and has set up the debug info.
Handle<SharedFunctionInfo> shared(function->shared());
if (!EnsureBreakInfo(shared)) return true;
CHECK(PrepareFunctionForBreakPoints(shared));
PrepareFunctionForBreakPoints(shared);
Handle<DebugInfo> debug_info(shared->GetDebugInfo());
// Source positions starts with zero.
DCHECK_LE(0, *source_position);
......@@ -593,7 +593,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
// Make sure the function has set up the debug info.
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result);
if (!EnsureBreakInfo(shared)) return false;
CHECK(PrepareFunctionForBreakPoints(shared));
PrepareFunctionForBreakPoints(shared);
// Find position within function. The script position might be before the
// source position of the first function.
......@@ -698,7 +698,7 @@ void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
if (IsBlackboxed(shared)) return;
// Make sure the function is compiled and has set up the debug info.
if (!EnsureBreakInfo(shared)) return;
CHECK(PrepareFunctionForBreakPoints(shared));
PrepareFunctionForBreakPoints(shared);
Handle<DebugInfo> debug_info(shared->GetDebugInfo());
// Flood the function with break points.
DCHECK(debug_info->HasDebugBytecodeArray());
......@@ -1039,10 +1039,8 @@ class RedirectActiveFunctions : public ThreadVisitor {
for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) {
JavaScriptFrame* frame = it.frame();
JSFunction* function = frame->function();
if (frame->is_optimized()) continue;
if (!function->Inlines(shared_)) continue;
DCHECK(frame->is_interpreted());
if (!frame->is_interpreted()) continue;
if (function->shared() != shared_) continue;
InterpretedFrame* interpreted_frame =
reinterpret_cast<InterpretedFrame*>(frame);
BytecodeArray* debug_copy = shared_->GetDebugInfo()->DebugBytecodeArray();
......@@ -1055,17 +1053,9 @@ class RedirectActiveFunctions : public ThreadVisitor {
DisallowHeapAllocation no_gc_;
};
bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
// To prepare bytecode for debugging, we already need to have the debug
// info (containing the debug copy) upfront, but since we do not recompile,
// preparing for break points cannot fail.
DCHECK(shared->is_compiled());
DCHECK(shared->HasDebugInfo());
DCHECK(shared->HasBreakInfo());
Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
if (debug_info->IsPreparedForBreakpoints()) return true;
void Debug::DeoptimizeFunction(Handle<SharedFunctionInfo> shared) {
// Deoptimize all code compiled from this shared function info including
// inlining.
if (isolate_->concurrent_recompilation_enabled()) {
isolate_->optimizing_compile_dispatcher()->Flush(
OptimizingCompileDispatcher::BlockingBehavior::kBlock);
......@@ -1075,28 +1065,32 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask,
GarbageCollectionReason::kDebugger);
DCHECK(shared->is_compiled());
{
// TODO(yangguo): with bytecode, we still walk the heap to find all
// optimized code for the function to deoptimize. We can probably be
// smarter here and avoid the heap walk.
HeapIterator iterator(isolate_->heap());
HeapObject* obj;
while ((obj = iterator.next()) != nullptr) {
if (obj->IsJSFunction()) {
JSFunction* function = JSFunction::cast(obj);
if (!function->Inlines(*shared)) continue;
if (function->has_feedback_vector()) {
function->ClearOptimizedCodeSlot("Prepare for breakpoints");
}
if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) {
Deoptimizer::DeoptimizeFunction(function);
}
}
bool found_something = false;
Code::OptimizedCodeIterator iterator(isolate_);
while (Code* code = iterator.Next()) {
if (code->Inlines(*shared)) {
code->set_marked_for_deoptimization(true);
found_something = true;
}
}
if (found_something) {
// Only go through with the deoptimization if something was found.
Deoptimizer::DeoptimizeMarkedCode(isolate_);
}
}
void Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
// To prepare bytecode for debugging, we already need to have the debug
// info (containing the debug copy) upfront, but since we do not recompile,
// preparing for break points cannot fail.
DCHECK(shared->is_compiled());
DCHECK(shared->HasDebugInfo());
DCHECK(shared->HasBreakInfo());
Handle<DebugInfo> debug_info = GetOrCreateDebugInfo(shared);
if (debug_info->IsPreparedForBreakpoints()) return;
DeoptimizeFunction(shared);
// Update PCs on the stack to point to recompiled code.
RedirectActiveFunctions redirect_visitor(*shared);
redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top());
......@@ -1104,7 +1098,6 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) {
debug_info->set_flags(debug_info->flags() |
DebugInfo::kPreparedForBreakpoints);
return true;
}
namespace {
......
......@@ -252,7 +252,8 @@ class Debug {
void ClearStepping();
void ClearStepOut();
bool PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared);
void DeoptimizeFunction(Handle<SharedFunctionInfo> shared);
void PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared);
bool GetPossibleBreakpoints(Handle<Script> script, int start_position,
int end_position, bool restrict_to_function,
std::vector<BreakLocation>* locations);
......
......@@ -805,52 +805,6 @@ class FeedbackVectorFixer {
};
};
namespace {
bool NeedsDeoptimization(SharedFunctionInfo* function_info, Code* code) {
DeoptimizationInputData* table =
DeoptimizationInputData::cast(code->deoptimization_data());
SharedFunctionInfo* sfi =
SharedFunctionInfo::cast(table->SharedFunctionInfo());
if (sfi == function_info) return true;
DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
DCHECK_NE(0, table->length());
FixedArray* const literals = table->LiteralArray();
int const inlined_count = table->InlinedFunctionCount()->value();
for (int i = 0; i < inlined_count; i++) {
if (SharedFunctionInfo::cast(literals->get(i)) == function_info) {
return true;
}
}
return false;
}
} // namespace
static void DeoptimizeDependentFunctions(SharedFunctionInfo* function_info) {
DisallowHeapAllocation no_allocation;
bool found_something = false;
Isolate* isolate = function_info->GetIsolate();
Object* context_link = isolate->heap()->native_contexts_list();
while (!context_link->IsUndefined(isolate)) {
Context* context = Context::cast(context_link);
Object* element = context->OptimizedCodeListHead();
while (!element->IsUndefined(isolate)) {
Code* code = Code::cast(element);
DCHECK(code->kind() == Code::OPTIMIZED_FUNCTION);
if (NeedsDeoptimization(function_info, code)) {
code->set_marked_for_deoptimization(true);
found_something = true;
}
element = code->next_code_link();
}
context_link = context->next_context_link();
}
if (found_something) {
// Only go through with the deoptimization if something was found.
Deoptimizer::DeoptimizeMarkedCode(isolate);
}
}
void LiveEdit::ReplaceFunctionCode(
Handle<JSArray> new_compile_info_array,
......@@ -900,8 +854,7 @@ void LiveEdit::ReplaceFunctionCode(
FeedbackVectorFixer::PatchFeedbackVector(&compile_info_wrapper, shared_info,
isolate);
DeoptimizeDependentFunctions(*shared_info);
isolate->compilation_cache()->Remove(shared_info);
isolate->debug()->DeoptimizeFunction(shared_info);
}
void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array,
......@@ -910,8 +863,7 @@ void LiveEdit::FunctionSourceUpdated(Handle<JSArray> shared_info_array,
Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
shared_info->set_function_literal_id(new_function_literal_id);
DeoptimizeDependentFunctions(*shared_info);
shared_info_array->GetIsolate()->compilation_cache()->Remove(shared_info);
shared_info_array->GetIsolate()->debug()->DeoptimizeFunction(shared_info);
}
void LiveEdit::FixupScript(Handle<Script> script, int max_function_literal_id) {
......@@ -1132,7 +1084,9 @@ static bool CheckActivation(Handle<JSArray> shared_info_array,
Handle<SharedFunctionInfo> shared =
UnwrapSharedFunctionInfoFromJSValue(jsvalue);
if (function->Inlines(*shared)) {
if (function->shared() == *shared ||
(function->code()->is_optimized_code() &&
function->code()->Inlines(*shared))) {
SetElementSloppy(result, i, Handle<Smi>(Smi::FromInt(status), isolate));
return true;
}
......
......@@ -12104,23 +12104,6 @@ bool Map::EquivalentToForNormalization(const Map* other,
}
bool JSFunction::Inlines(SharedFunctionInfo* candidate) {
DisallowHeapAllocation no_gc;
if (shared() == candidate) return true;
if (code()->kind() != Code::OPTIMIZED_FUNCTION) return false;
DeoptimizationInputData* const data =
DeoptimizationInputData::cast(code()->deoptimization_data());
if (data->length() == 0) return false;
FixedArray* const literals = data->LiteralArray();
int const inlined_count = data->InlinedFunctionCount()->value();
for (int i = 0; i < inlined_count; ++i) {
if (SharedFunctionInfo::cast(literals->get(i)) == candidate) {
return true;
}
}
return false;
}
void JSFunction::MarkForOptimization(ConcurrencyMode mode) {
Isolate* isolate = GetIsolate();
if (!isolate->concurrent_recompilation_enabled() ||
......@@ -13981,7 +13964,6 @@ Handle<WeakCell> Code::WeakCellFor(Handle<Code> code) {
return cell;
}
WeakCell* Code::CachedWeakCell() {
DCHECK(kind() == OPTIMIZED_FUNCTION);
Object* weak_cell_cache =
......@@ -13993,6 +13975,53 @@ WeakCell* Code::CachedWeakCell() {
return NULL;
}
bool Code::Inlines(SharedFunctionInfo* sfi) {
// We can only check for inlining for optimized code.
DCHECK(is_optimized_code());
DisallowHeapAllocation no_gc;
DeoptimizationInputData* const data =
DeoptimizationInputData::cast(deoptimization_data());
if (data->length() == 0) return false;
if (data->SharedFunctionInfo() == sfi) return true;
FixedArray* const literals = data->LiteralArray();
int const inlined_count = data->InlinedFunctionCount()->value();
for (int i = 0; i < inlined_count; ++i) {
if (SharedFunctionInfo::cast(literals->get(i)) == sfi) return true;
}
return false;
}
Code::OptimizedCodeIterator::OptimizedCodeIterator(Isolate* isolate) {
isolate_ = isolate;
Object* list = isolate->heap()->native_contexts_list();
next_context_ = list->IsUndefined(isolate_) ? nullptr : Context::cast(list);
current_code_ = nullptr;
}
Code* Code::OptimizedCodeIterator::Next() {
do {
Object* next;
if (current_code_ != nullptr) {
// Get next code in the linked list.
next = Code::cast(current_code_)->next_code_link();
} else if (next_context_ != nullptr) {
// Linked list of code exhausted. Get list of next context.
next = next_context_->OptimizedCodeListHead();
Object* next_context = next_context_->next_context_link();
next_context_ = next_context->IsUndefined(isolate_)
? nullptr
: Context::cast(next_context);
} else {
// Exhausted contexts.
return nullptr;
}
current_code_ = next->IsUndefined(isolate_) ? nullptr : Code::cast(next);
} while (current_code_ == nullptr);
Code* code = Code::cast(current_code_);
DCHECK_EQ(Code::OPTIMIZED_FUNCTION, code->kind());
return code;
}
#if defined(OBJECT_PRINT) || defined(ENABLE_DISASSEMBLER)
const char* Code::ICState2String(InlineCacheState state) {
......
......@@ -3947,6 +3947,23 @@ class Code: public HeapObject {
static Handle<WeakCell> WeakCellFor(Handle<Code> code);
WeakCell* CachedWeakCell();
// Return true if the function is inlined in the code.
bool Inlines(SharedFunctionInfo* sfi);
class OptimizedCodeIterator {
public:
explicit OptimizedCodeIterator(Isolate* isolate);
Code* Next();
private:
Context* next_context_;
Code* current_code_;
Isolate* isolate_;
DisallowHeapAllocation no_gc;
DISALLOW_COPY_AND_ASSIGN(OptimizedCodeIterator)
};
static const int kConstantPoolSize =
FLAG_enable_embedded_constant_pool ? kIntSize : 0;
......@@ -4823,9 +4840,6 @@ class JSFunction: public JSObject {
// a Code object or a BytecodeArray.
inline AbstractCode* abstract_code();
// Tells whether this function inlines the given shared function info.
bool Inlines(SharedFunctionInfo* candidate);
// Tells whether or not this function is interpreted.
//
// Note: function->IsInterpreted() does not necessarily return the same value
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment