Introduce concurrent on-stack replacement.

Currently disabled behind --concurrent-osr.

R=titzer@chromium.org
BUG=

Review URL: https://codereview.chromium.org/23710014

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16527 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2013-09-04 12:55:59 +00:00
parent 45d6ef065e
commit 070e3b0af4
15 changed files with 380 additions and 150 deletions

View File

@ -58,7 +58,8 @@ CompilationInfo::CompilationInfo(Handle<Script> script,
Zone* zone)
: flags_(LanguageModeField::encode(CLASSIC_MODE)),
script_(script),
osr_ast_id_(BailoutId::None()) {
osr_ast_id_(BailoutId::None()),
osr_pc_offset_(0) {
Initialize(script->GetIsolate(), BASE, zone);
}
@ -68,7 +69,8 @@ CompilationInfo::CompilationInfo(Handle<SharedFunctionInfo> shared_info,
: flags_(LanguageModeField::encode(CLASSIC_MODE) | IsLazy::encode(true)),
shared_info_(shared_info),
script_(Handle<Script>(Script::cast(shared_info->script()))),
osr_ast_id_(BailoutId::None()) {
osr_ast_id_(BailoutId::None()),
osr_pc_offset_(0) {
Initialize(script_->GetIsolate(), BASE, zone);
}
@ -80,7 +82,8 @@ CompilationInfo::CompilationInfo(Handle<JSFunction> closure,
shared_info_(Handle<SharedFunctionInfo>(closure->shared())),
script_(Handle<Script>(Script::cast(shared_info_->script()))),
context_(closure->context()),
osr_ast_id_(BailoutId::None()) {
osr_ast_id_(BailoutId::None()),
osr_pc_offset_(0) {
Initialize(script_->GetIsolate(), BASE, zone);
}
@ -90,7 +93,8 @@ CompilationInfo::CompilationInfo(HydrogenCodeStub* stub,
Zone* zone)
: flags_(LanguageModeField::encode(CLASSIC_MODE) |
IsLazy::encode(true)),
osr_ast_id_(BailoutId::None()) {
osr_ast_id_(BailoutId::None()),
osr_pc_offset_(0) {
Initialize(isolate, STUB, zone);
code_stub_ = stub;
}
@ -963,8 +967,9 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
}
void Compiler::RecompileConcurrent(Handle<JSFunction> closure) {
ASSERT(closure->IsMarkedForConcurrentRecompilation());
bool Compiler::RecompileConcurrent(Handle<JSFunction> closure,
uint32_t osr_pc_offset) {
bool compiling_for_osr = (osr_pc_offset != 0);
Isolate* isolate = closure->GetIsolate();
// Here we prepare compile data for the concurrent recompilation thread, but
@ -978,23 +983,39 @@ void Compiler::RecompileConcurrent(Handle<JSFunction> closure) {
closure->PrintName();
PrintF(" on next run.\n");
}
return;
return false;
}
SmartPointer<CompilationInfo> info(new CompilationInfoWithZone(closure));
Handle<SharedFunctionInfo> shared = info->shared_info();
if (compiling_for_osr) {
BailoutId osr_ast_id =
shared->code()->TranslatePcOffsetToAstId(osr_pc_offset);
ASSERT(!osr_ast_id.IsNone());
info->SetOptimizing(osr_ast_id);
info->set_osr_pc_offset(osr_pc_offset);
if (FLAG_trace_osr) {
PrintF("[COSR - attempt to queue ");
closure->PrintName();
PrintF(" at AST id %d]\n", osr_ast_id.ToInt());
}
} else {
info->SetOptimizing(BailoutId::None());
}
VMState<COMPILER> state(isolate);
PostponeInterruptsScope postpone(isolate);
Handle<SharedFunctionInfo> shared = info->shared_info();
int compiled_size = shared->end_position() - shared->start_position();
isolate->counters()->total_compile_size()->Increment(compiled_size);
info->SetOptimizing(BailoutId::None());
{
CompilationHandleScope handle_scope(*info);
if (InstallCodeFromOptimizedCodeMap(*info)) {
return;
if (!compiling_for_osr && InstallCodeFromOptimizedCodeMap(*info)) {
return true;
}
if (Parser::Parse(*info)) {
@ -1011,6 +1032,8 @@ void Compiler::RecompileConcurrent(Handle<JSFunction> closure) {
info.Detach();
shared->code()->set_profiler_ticks(0);
isolate->optimizing_compiler_thread()->QueueForOptimization(compiler);
ASSERT(!isolate->has_pending_exception());
return true;
} else if (status == OptimizingCompiler::BAILED_OUT) {
isolate->clear_pending_exception();
InstallFullCode(*info);
@ -1019,19 +1042,12 @@ void Compiler::RecompileConcurrent(Handle<JSFunction> closure) {
}
}
if (shared->code()->back_edges_patched_for_osr()) {
// At this point we either put the function on recompilation queue or
// aborted optimization. In either case we want to continue executing
// the unoptimized code without running into OSR. If the unoptimized
// code has been patched for OSR, unpatch it.
Deoptimizer::RevertInterruptCode(isolate, shared->code());
}
if (isolate->has_pending_exception()) isolate->clear_pending_exception();
return false;
}
void Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) {
bool Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) {
SmartPointer<CompilationInfo> info(optimizing_compiler->info());
// The function may have already been optimized by OSR. Simply continue.
// Except when OSR already disabled optimization for some reason.
@ -1044,7 +1060,7 @@ void Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) {
PrintF(" as it has been disabled.\n");
}
ASSERT(!info->closure()->IsMarkedForInstallingRecompiledCode());
return;
return false;
}
Isolate* isolate = info->isolate();
@ -1091,6 +1107,168 @@ void Compiler::InstallOptimizedCode(OptimizingCompiler* optimizing_compiler) {
// profiler ticks to prevent too soon re-opt after a deopt.
info->shared_info()->code()->set_profiler_ticks(0);
ASSERT(!info->closure()->IsMarkedForInstallingRecompiledCode());
return status == OptimizingCompiler::SUCCEEDED;
}
static uint32_t CurrentPcOffset(Isolate* isolate,
Handle<JSFunction> function,
Handle<Code> unoptimized) {
JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
ASSERT(frame->function() == *function);
ASSERT(frame->LookupCode() == *unoptimized);
ASSERT(unoptimized->contains(frame->pc()));
// Use linear search of the unoptimized code's back edge table to find
// the AST id matching the PC.
return static_cast<uint32_t>(frame->pc() - unoptimized->instruction_start());
}
static bool IsSuitableForOnStackReplacement(Isolate* isolate,
Handle<JSFunction> function,
Handle<Code> unoptimized) {
// Keep track of whether we've succeeded in optimizing.
if (!unoptimized->optimizable()) return false;
// If we are trying to do OSR when there are already optimized
// activations of the function, it means (a) the function is directly or
// indirectly recursive and (b) an optimized invocation has been
// deoptimized so that we are currently in an unoptimized activation.
// Check for optimized activations of this function.
for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) {
JavaScriptFrame* frame = it.frame();
if (frame->is_optimized() && frame->function() == *function) return false;
}
return true;
}
BailoutId Compiler::CompileForOnStackReplacement(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
// We have hit a back edge in an unoptimized frame for a function that was
// selected for on-stack replacement. Find the unoptimized code object.
Handle<Code> unoptimized(function->shared()->code(), isolate);
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
if (FLAG_trace_osr) {
PrintF("[OSR - restored original interrupt calls in ");
function->PrintName();
PrintF("]\n");
}
if (!IsSuitableForOnStackReplacement(isolate, function, unoptimized)) {
return BailoutId::None();
}
uint32_t pc_offset = CurrentPcOffset(isolate, function, unoptimized);
BailoutId ast_id = unoptimized->TranslatePcOffsetToAstId(pc_offset);
ASSERT(!ast_id.IsNone());
if (FLAG_trace_osr) {
PrintF("[OSR - replacing at AST id %d in ", ast_id.ToInt());
function->PrintName();
PrintF("]\n");
}
// Try to compile the optimized code. A true return value from
// CompileOptimized means that compilation succeeded, not necessarily
// that optimization succeeded.
if (JSFunction::CompileOptimized(function, ast_id, CLEAR_EXCEPTION) &&
function->IsOptimized()) {
DeoptimizationInputData* data = DeoptimizationInputData::cast(
function->code()->deoptimization_data());
if (data->OsrPcOffset()->value() >= 0) {
if (FLAG_trace_osr) {
PrintF("[OSR - entry, offset %d in optimized code]\n",
data->OsrPcOffset()->value());
}
ASSERT(BailoutId(data->OsrAstId()->value()) == ast_id);
return ast_id;
}
} else {
if (FLAG_trace_osr) {
PrintF("[OSR - optimization failed for ");
function->PrintName();
PrintF("]\n");
}
}
return BailoutId::None();
}
BailoutId Compiler::CompileForConcurrentOSR(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Code> unoptimized(function->shared()->code(), isolate);
uint32_t pc_offset = CurrentPcOffset(isolate, function, unoptimized);
if (isolate->optimizing_compiler_thread()->
IsQueuedForOSR(function, pc_offset)) {
// Still waiting for the optimizing compiler thread to finish. Carry on.
if (FLAG_trace_osr) {
PrintF("[COSR - polling recompile tasks for ");
function->PrintName();
PrintF("]\n");
}
return BailoutId::None();
}
OptimizingCompiler* compiler = isolate->optimizing_compiler_thread()->
FindReadyOSRCandidate(function, pc_offset);
if (compiler != NULL) {
if (FLAG_trace_osr) {
PrintF("[COSR - optimization complete for ");
function->PrintName();
PrintF(", restoring interrupt calls]\n");
}
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
BailoutId ast_id = compiler->info()->osr_ast_id();
bool succeeded = InstallOptimizedCode(compiler);
isolate->optimizing_compiler_thread()->RemoveStaleOSRCandidates();
if (!succeeded) {
if (FLAG_trace_osr) {
PrintF("[COSR - optimization failed for ");
function->PrintName();
PrintF("]\n");
}
return BailoutId::None();
}
DeoptimizationInputData* data = DeoptimizationInputData::cast(
function->code()->deoptimization_data());
if (data->OsrPcOffset()->value() >= 0) {
ASSERT(BailoutId(data->OsrAstId()->value()) == ast_id);
if (FLAG_trace_osr) {
PrintF("[COSR - entry at AST id %d, offset %d in optimized code]\n",
ast_id.ToInt(), data->OsrPcOffset()->value());
}
return ast_id;
}
return BailoutId::None();
}
if (!IsSuitableForOnStackReplacement(isolate, function, unoptimized)) {
if (FLAG_trace_osr) {
PrintF("[COSR - ");
function->PrintName();
PrintF(" is unsuitable, restoring interrupt calls]\n");
}
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
return BailoutId::None();
}
if (!RecompileConcurrent(function, pc_offset)) {
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
}
return BailoutId::None();
}

View File

@ -308,6 +308,14 @@ class CompilationInfo {
return abort_due_to_dependency_;
}
void set_osr_pc_offset(uint32_t pc_offset) {
osr_pc_offset_ = pc_offset;
}
bool HasSameOsrEntry(Handle<JSFunction> function, uint32_t pc_offset) {
return osr_pc_offset_ == pc_offset && function.is_identical_to(closure_);
}
protected:
CompilationInfo(Handle<Script> script,
Zone* zone);
@ -402,6 +410,9 @@ class CompilationInfo {
// Compilation mode flag and whether deoptimization is allowed.
Mode mode_;
BailoutId osr_ast_id_;
// The pc_offset corresponding to osr_ast_id_ in unoptimized code.
// We can look this up in the back edge table, but cache it for quick access.
uint32_t osr_pc_offset_;
// Flag whether compilation needs to be aborted due to dependency change.
bool abort_due_to_dependency_;
@ -600,7 +611,8 @@ class Compiler : public AllStatic {
// success and false if the compilation resulted in a stack overflow.
static bool CompileLazy(CompilationInfo* info);
static void RecompileConcurrent(Handle<JSFunction> function);
static bool RecompileConcurrent(Handle<JSFunction> function,
uint32_t osr_pc_offset = 0);
// Compile a shared function info object (the function is possibly lazily
// compiled).
@ -613,7 +625,11 @@ class Compiler : public AllStatic {
bool is_toplevel,
Handle<Script> script);
static void InstallOptimizedCode(OptimizingCompiler* info);
static bool InstallOptimizedCode(OptimizingCompiler* info);
static BailoutId CompileForOnStackReplacement(Handle<JSFunction> function);
static BailoutId CompileForConcurrentOSR(Handle<JSFunction> function);
#ifdef ENABLE_DEBUGGER_SUPPORT
static bool MakeCodeForLiveEdit(CompilationInfo* info);

View File

@ -2591,71 +2591,68 @@ bool Deoptimizer::DoOsrTranslateCommand(TranslationIterator* iterator,
void Deoptimizer::PatchInterruptCode(Isolate* isolate,
Code* unoptimized_code) {
Code* unoptimized) {
DisallowHeapAllocation no_gc;
Code* replacement_code =
isolate->builtins()->builtin(Builtins::kOnStackReplacement);
// Iterate over the back edge table and patch every interrupt
// call to an unconditional call to the replacement code.
int loop_nesting_level = unoptimized_code->allow_osr_at_loop_nesting_level();
int loop_nesting_level = unoptimized->allow_osr_at_loop_nesting_level();
for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized_code);
for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized, &no_gc);
!back_edges.Done();
back_edges.Next()) {
if (static_cast<int>(back_edges.loop_depth()) == loop_nesting_level) {
ASSERT_EQ(NOT_PATCHED, GetInterruptPatchState(isolate,
unoptimized_code,
unoptimized,
back_edges.pc()));
PatchInterruptCodeAt(unoptimized_code,
PatchInterruptCodeAt(unoptimized,
back_edges.pc(),
replacement_code);
}
}
unoptimized_code->set_back_edges_patched_for_osr(true);
#ifdef DEBUG
Deoptimizer::VerifyInterruptCode(
isolate, unoptimized_code, loop_nesting_level);
#endif // DEBUG
unoptimized->set_back_edges_patched_for_osr(true);
ASSERT(Deoptimizer::VerifyInterruptCode(
isolate, unoptimized, loop_nesting_level));
}
void Deoptimizer::RevertInterruptCode(Isolate* isolate,
Code* unoptimized_code) {
Code* unoptimized) {
DisallowHeapAllocation no_gc;
Code* interrupt_code =
isolate->builtins()->builtin(Builtins::kInterruptCheck);
// Iterate over the back edge table and revert the patched interrupt calls.
ASSERT(unoptimized_code->back_edges_patched_for_osr());
int loop_nesting_level = unoptimized_code->allow_osr_at_loop_nesting_level();
ASSERT(unoptimized->back_edges_patched_for_osr());
int loop_nesting_level = unoptimized->allow_osr_at_loop_nesting_level();
for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized_code);
for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized, &no_gc);
!back_edges.Done();
back_edges.Next()) {
if (static_cast<int>(back_edges.loop_depth()) <= loop_nesting_level) {
ASSERT_EQ(PATCHED_FOR_OSR, GetInterruptPatchState(isolate,
unoptimized_code,
unoptimized,
back_edges.pc()));
RevertInterruptCodeAt(unoptimized_code, back_edges.pc(), interrupt_code);
RevertInterruptCodeAt(unoptimized, back_edges.pc(), interrupt_code);
}
}
unoptimized_code->set_back_edges_patched_for_osr(false);
unoptimized_code->set_allow_osr_at_loop_nesting_level(0);
#ifdef DEBUG
unoptimized->set_back_edges_patched_for_osr(false);
unoptimized->set_allow_osr_at_loop_nesting_level(0);
// Assert that none of the back edges are patched anymore.
Deoptimizer::VerifyInterruptCode(isolate, unoptimized_code, -1);
#endif // DEBUG
ASSERT(Deoptimizer::VerifyInterruptCode(isolate, unoptimized, -1));
}
#ifdef DEBUG
void Deoptimizer::VerifyInterruptCode(Isolate* isolate,
Code* unoptimized_code,
bool Deoptimizer::VerifyInterruptCode(Isolate* isolate,
Code* unoptimized,
int loop_nesting_level) {
for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized_code);
DisallowHeapAllocation no_gc;
for (FullCodeGenerator::BackEdgeTableIterator back_edges(unoptimized, &no_gc);
!back_edges.Done();
back_edges.Next()) {
uint32_t loop_depth = back_edges.loop_depth();
@ -2664,9 +2661,10 @@ void Deoptimizer::VerifyInterruptCode(Isolate* isolate,
// have already been patched.
CHECK_EQ((static_cast<int>(loop_depth) <= loop_nesting_level),
GetInterruptPatchState(isolate,
unoptimized_code,
unoptimized,
back_edges.pc()) != NOT_PATCHED);
}
return true;
}
#endif // DEBUG

View File

@ -262,7 +262,7 @@ class Deoptimizer : public Malloced {
Address pc_after);
// Verify that all back edges of a certain loop depth are patched.
static void VerifyInterruptCode(Isolate* isolate,
static bool VerifyInterruptCode(Isolate* isolate,
Code* unoptimized_code,
int loop_nesting_level);
#endif // DEBUG

View File

@ -331,6 +331,8 @@ DEFINE_int(concurrent_recompilation_queue_length, 8,
"the length of the concurrent compilation queue")
DEFINE_int(concurrent_recompilation_delay, 0,
"artificial compilation delay in ms")
DEFINE_bool(concurrent_osr, false,
"concurrent on-stack replacement")
DEFINE_bool(omit_map_checks_for_leaf_maps, true,
"do not emit check maps for constant values that have a leaf map, "

View File

@ -141,7 +141,8 @@ class FullCodeGenerator: public AstVisitor {
class BackEdgeTableIterator {
public:
explicit BackEdgeTableIterator(Code* unoptimized) {
explicit BackEdgeTableIterator(Code* unoptimized,
DisallowHeapAllocation* required) {
ASSERT(unoptimized->kind() == Code::FUNCTION);
instruction_start_ = unoptimized->instruction_start();
cursor_ = instruction_start_ + unoptimized->back_edge_table_offset();
@ -192,7 +193,6 @@ class FullCodeGenerator: public AstVisitor {
Address end_;
Address instruction_start_;
uint32_t table_length_;
DisallowHeapAllocation no_gc_while_iterating_over_raw_addresses_;
DISALLOW_COPY_AND_ASSIGN(BackEdgeTableIterator);
};

View File

@ -10376,6 +10376,18 @@ void Code::ClearTypeFeedbackCells(Heap* heap) {
}
BailoutId Code::TranslatePcOffsetToAstId(uint32_t pc_offset) {
DisallowHeapAllocation no_gc;
ASSERT(kind() == FUNCTION);
for (FullCodeGenerator::BackEdgeTableIterator it(this, &no_gc);
!it.Done();
it.Next()) {
if (it.pc_offset() == pc_offset) return it.ast_id();
}
return BailoutId::None();
}
bool Code::allowed_in_shared_map_code_cache() {
return is_keyed_load_stub() || is_keyed_store_stub() ||
(is_compare_ic_stub() &&
@ -10836,7 +10848,8 @@ void Code::Disassemble(const char* name, FILE* out) {
// If there is no back edge table, the "table start" will be at or after
// (due to alignment) the end of the instruction stream.
if (static_cast<int>(offset) < instruction_size()) {
FullCodeGenerator::BackEdgeTableIterator back_edges(this);
DisallowHeapAllocation no_gc;
FullCodeGenerator::BackEdgeTableIterator back_edges(this, &no_gc);
PrintF(out, "Back edges (size = %u)\n", back_edges.table_length());
PrintF(out, "ast_id pc_offset loop_depth\n");

View File

@ -5107,6 +5107,8 @@ class Code: public HeapObject {
void ClearInlineCaches();
void ClearTypeFeedbackCells(Heap* heap);
BailoutId TranslatePcOffsetToAstId(uint32_t pc_offset);
#define DECLARE_CODE_AGE_ENUM(X) k##X##CodeAge,
enum Age {
kNoAge = 0,

View File

@ -108,12 +108,18 @@ void OptimizingCompilerThread::CompileNext() {
// The function may have already been optimized by OSR. Simply continue.
// Use a mutex to make sure that functions marked for install
// are always also queued.
if (!optimizing_compiler->info()->osr_ast_id().IsNone()) {
ASSERT(FLAG_concurrent_osr);
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_);
osr_candidates_.RemoveElement(optimizing_compiler);
ready_for_osr_.Add(optimizing_compiler);
} else {
LockGuard<Mutex> mark_and_queue(&install_mutex_);
{ Heap::RelocationLock relocation_lock(isolate_->heap());
Heap::RelocationLock relocation_lock(isolate_->heap());
AllowHandleDereference ahd;
optimizing_compiler->info()->closure()->MarkForInstallingRecompiledCode();
}
output_queue_.Enqueue(optimizing_compiler);
}
}
@ -145,6 +151,9 @@ void OptimizingCompilerThread::FlushOutputQueue(bool restore_function_code) {
}
delete info;
}
osr_candidates_.Clear();
RemoveStaleOSRCandidates(0);
}
@ -179,6 +188,10 @@ void OptimizingCompilerThread::Stop() {
PrintF(" ** Compiler thread did %.2f%% useful work\n", percentage);
}
if (FLAG_trace_osr && FLAG_concurrent_osr) {
PrintF("[COSR hit rate %d / %d]\n", osr_hits_, osr_attempts_);
}
Join();
}
@ -194,6 +207,10 @@ void OptimizingCompilerThread::InstallOptimizedFunctions() {
}
Compiler::InstallOptimizedCode(compiler);
}
// Remove the oldest OSR candidates that are ready so that we
// only have limited number of them waiting.
if (FLAG_concurrent_osr) RemoveStaleOSRCandidates();
}
@ -202,12 +219,62 @@ void OptimizingCompilerThread::QueueForOptimization(
ASSERT(IsQueueAvailable());
ASSERT(!IsOptimizerThread());
Barrier_AtomicIncrement(&queue_length_, static_cast<Atomic32>(1));
if (optimizing_compiler->info()->osr_ast_id().IsNone()) {
optimizing_compiler->info()->closure()->MarkInRecompileQueue();
} else {
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_);
osr_candidates_.Add(optimizing_compiler);
osr_attempts_++;
}
input_queue_.Enqueue(optimizing_compiler);
input_queue_semaphore_.Signal();
}
OptimizingCompiler* OptimizingCompilerThread::FindReadyOSRCandidate(
Handle<JSFunction> function, uint32_t osr_pc_offset) {
ASSERT(!IsOptimizerThread());
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_);
for (int i = 0; i < ready_for_osr_.length(); i++) {
if (ready_for_osr_[i]->info()->HasSameOsrEntry(function, osr_pc_offset)) {
osr_hits_++;
return ready_for_osr_.Remove(i);
}
}
return NULL;
}
bool OptimizingCompilerThread::IsQueuedForOSR(Handle<JSFunction> function,
uint32_t osr_pc_offset) {
ASSERT(!IsOptimizerThread());
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_);
for (int i = 0; i < osr_candidates_.length(); i++) {
if (osr_candidates_[i]->info()->HasSameOsrEntry(function, osr_pc_offset)) {
return true;
}
}
return false;
}
void OptimizingCompilerThread::RemoveStaleOSRCandidates(int limit) {
ASSERT(!IsOptimizerThread());
LockGuard<Mutex> access_osr_lists(&osr_list_mutex_);
while (ready_for_osr_.length() > limit) {
OptimizingCompiler* compiler = ready_for_osr_.Remove(0);
CompilationInfo* throw_away = compiler->info();
if (FLAG_trace_osr) {
PrintF("[COSR - Discarded ");
throw_away->closure()->PrintName();
PrintF(", AST id %d]\n",
throw_away->osr_ast_id().ToInt());
}
delete throw_away;
}
}
#ifdef DEBUG
bool OptimizingCompilerThread::IsOptimizerThread() {
if (!FLAG_concurrent_recompilation) return false;

View File

@ -30,6 +30,7 @@
#include "atomicops.h"
#include "flags.h"
#include "list.h"
#include "platform.h"
#include "platform/mutex.h"
#include "platform/time.h"
@ -51,7 +52,11 @@ class OptimizingCompilerThread : public Thread {
#endif
isolate_(isolate),
stop_semaphore_(0),
input_queue_semaphore_(0) {
input_queue_semaphore_(0),
osr_candidates_(2),
ready_for_osr_(2),
osr_hits_(0),
osr_attempts_(0) {
NoBarrier_Store(&stop_thread_, static_cast<AtomicWord>(CONTINUE));
NoBarrier_Store(&queue_length_, static_cast<AtomicWord>(0));
}
@ -62,6 +67,13 @@ class OptimizingCompilerThread : public Thread {
void Flush();
void QueueForOptimization(OptimizingCompiler* optimizing_compiler);
void InstallOptimizedFunctions();
OptimizingCompiler* FindReadyOSRCandidate(Handle<JSFunction> function,
uint32_t osr_pc_offset);
bool IsQueuedForOSR(Handle<JSFunction> function, uint32_t osr_pc_offset);
// Remove the oldest OSR candidates that are ready so that we
// only have |limit| left waiting.
void RemoveStaleOSRCandidates(int limit = kReadyForOSRLimit);
inline bool IsQueueAvailable() {
// We don't need a barrier since we have a data dependency right
@ -86,7 +98,6 @@ class OptimizingCompilerThread : public Thread {
void FlushInputQueue(bool restore_function_code);
void FlushOutputQueue(bool restore_function_code);
void CompileNext();
#ifdef DEBUG
@ -97,13 +108,27 @@ class OptimizingCompilerThread : public Thread {
Isolate* isolate_;
Semaphore stop_semaphore_;
Semaphore input_queue_semaphore_;
// Queue of incoming recompilation tasks (including OSR).
UnboundQueue<OptimizingCompiler*> input_queue_;
// Queue of recompilation tasks ready to be installed (excluding OSR).
UnboundQueue<OptimizingCompiler*> output_queue_;
// List of all OSR related recompilation tasks (both incoming and ready ones).
List<OptimizingCompiler*> osr_candidates_;
// List of recompilation tasks ready for OSR.
List<OptimizingCompiler*> ready_for_osr_;
Mutex install_mutex_;
volatile AtomicWord stop_thread_;
volatile Atomic32 queue_length_;
TimeDelta time_spent_compiling_;
TimeDelta time_spent_total_;
Mutex osr_list_mutex_;
int osr_hits_;
int osr_attempts_;
static const int kReadyForOSRLimit = 4;
};
} } // namespace v8::internal

View File

@ -230,8 +230,7 @@ class LockGuard V8_FINAL {
private:
Mutex* mutex_;
LockGuard(const LockGuard<Mutex>& other) V8_DELETE;
LockGuard<Mutex>& operator=(const LockGuard<Mutex>& other) V8_DELETE;
DISALLOW_COPY_AND_ASSIGN(LockGuard);
};
} } // namespace v8::internal

View File

@ -172,9 +172,9 @@ void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) {
// any back edge in any unoptimized frame will trigger on-stack
// replacement for that frame.
if (FLAG_trace_osr) {
PrintF("[patching back edges in ");
PrintF("[OSR - patching back edges in ");
function->PrintName();
PrintF(" for on-stack replacement]\n");
PrintF("]\n");
}
Deoptimizer::PatchInterruptCode(isolate_, shared->code());

View File

@ -8343,7 +8343,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ConcurrentRecompile) {
}
function->shared()->code()->set_profiler_ticks(0);
ASSERT(FLAG_concurrent_recompilation);
Compiler::RecompileConcurrent(function);
if (!Compiler::RecompileConcurrent(function)) {
function->ReplaceCode(function->shared()->code());
}
return isolate->heap()->undefined_value();
}
@ -8512,7 +8514,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_OptimizeFunctionOnNextCall) {
unoptimized->kind() == Code::FUNCTION) {
CONVERT_ARG_HANDLE_CHECKED(String, type, 1);
if (type->IsOneByteEqualTo(STATIC_ASCII_VECTOR("osr"))) {
for (int i = 0; i <= Code::kMaxLoopNestingMarker; i++) {
// Start patching from the currently patched loop nesting level.
int current_level = unoptimized->allow_osr_at_loop_nesting_level();
ASSERT(Deoptimizer::VerifyInterruptCode(
isolate, unoptimized, current_level));
for (int i = current_level + 1; i <= Code::kMaxLoopNestingMarker; i++) {
unoptimized->set_allow_osr_at_loop_nesting_level(i);
isolate->runtime_profiler()->AttemptOnStackReplacement(*function);
}
@ -8586,98 +8592,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileForOnStackReplacement) {
// We're not prepared to handle a function with arguments object.
ASSERT(!function->shared()->uses_arguments());
// We have hit a back edge in an unoptimized frame for a function that was
// selected for on-stack replacement. Find the unoptimized code object.
Handle<Code> unoptimized(function->shared()->code(), isolate);
// Keep track of whether we've succeeded in optimizing.
bool succeeded = unoptimized->optimizable();
if (succeeded) {
// If we are trying to do OSR when there are already optimized
// activations of the function, it means (a) the function is directly or
// indirectly recursive and (b) an optimized invocation has been
// deoptimized so that we are currently in an unoptimized activation.
// Check for optimized activations of this function.
JavaScriptFrameIterator it(isolate);
while (succeeded && !it.done()) {
JavaScriptFrame* frame = it.frame();
succeeded = !frame->is_optimized() || frame->function() != *function;
it.Advance();
}
}
BailoutId ast_id = BailoutId::None();
if (succeeded) {
// The top JS function is this one, the PC is somewhere in the
// unoptimized code.
JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
ASSERT(frame->function() == *function);
ASSERT(frame->LookupCode() == *unoptimized);
ASSERT(unoptimized->contains(frame->pc()));
// Use linear search of the unoptimized code's back edge table to find
// the AST id matching the PC.
uint32_t target_pc_offset =
static_cast<uint32_t>(frame->pc() - unoptimized->instruction_start());
uint32_t loop_depth = 0;
for (FullCodeGenerator::BackEdgeTableIterator back_edges(*unoptimized);
!back_edges.Done();
back_edges.Next()) {
if (back_edges.pc_offset() == target_pc_offset) {
ast_id = back_edges.ast_id();
loop_depth = back_edges.loop_depth();
break;
}
}
ASSERT(!ast_id.IsNone());
if (FLAG_trace_osr) {
PrintF("[replacing on-stack at AST id %d, loop depth %d in ",
ast_id.ToInt(), loop_depth);
function->PrintName();
PrintF("]\n");
}
// Try to compile the optimized code. A true return value from
// CompileOptimized means that compilation succeeded, not necessarily
// that optimization succeeded.
if (JSFunction::CompileOptimized(function, ast_id, CLEAR_EXCEPTION) &&
function->IsOptimized()) {
DeoptimizationInputData* data = DeoptimizationInputData::cast(
function->code()->deoptimization_data());
if (data->OsrPcOffset()->value() >= 0) {
if (FLAG_trace_osr) {
PrintF("[on-stack replacement offset %d in optimized code]\n",
data->OsrPcOffset()->value());
}
ASSERT(BailoutId(data->OsrAstId()->value()) == ast_id);
} else {
// We may never generate the desired OSR entry if we emit an
// early deoptimize.
succeeded = false;
}
} else {
succeeded = false;
}
}
// Revert to the original interrupt calls in the original unoptimized code.
if (FLAG_trace_osr) {
PrintF("[restoring original interrupt calls in ");
function->PrintName();
PrintF("]\n");
}
Deoptimizer::RevertInterruptCode(isolate, *unoptimized);
// If the optimization attempt succeeded, return the AST id tagged as a
// smi. This tells the builtin that we need to translate the unoptimized
// frame to an optimized one.
if (succeeded) {
BailoutId ast_id =
(FLAG_concurrent_recompilation && FLAG_concurrent_osr)
? Compiler::CompileForConcurrentOSR(function)
: Compiler::CompileForOnStackReplacement(function);
if (!ast_id.IsNone()) {
ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
return Smi::FromInt(ast_id.ToInt());
} else {
if (function->IsMarkedForLazyRecompilation()) {
if (function->IsMarkedForLazyRecompilation() ||
function->IsMarkedForConcurrentRecompilation()) {
function->ReplaceCode(function->shared()->code());
}
return Smi::FromInt(-1);

View File

@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --optimize-for-in --allow-natives-syntax
// Flags: --no-concurrent-osr
// Test for-in support in Crankshaft. For simplicity this tests assumes certain
// fixed iteration order for properties and will have to be adjusted if V8

View File

@ -39,6 +39,8 @@ function g() {
var o2 = [{ x: 1.5, y: 1 }];
return o2;
}
// Clear type feedback from previous stress runs.
%ClearFunctionTypeFeedback(f);
return f;
}