Skip to content

Commit

Permalink
This adds a mechanism to invalidate code that was generated in the ba…
Browse files Browse the repository at this point in the history
…ckground. The invalidation can happen if a relevant event was invoked between background compilation start and installation of code (e.g, field type invalidatet, subclasses added, deferred loading).

We introduce 3 invalidation generation counters: CHA, field and library prefixes. If one of the counters was incremented during compilation, and is relevant for that compilation, the code installation is skipped since that code may have become invalid in the meantime.

Add BackgroundCompilationResult that remembers the 3 invalidation generations.

The generational counter can rollover, which is OK.

BUG=
R=asiva@google.com

Review URL: https://codereview.chromium.org/1418813008 .
  • Loading branch information
Srdjan Mitrovic committed Oct 30, 2015
1 parent cc0525c commit fe4a5d7
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 40 deletions.
155 changes: 130 additions & 25 deletions runtime/vm/compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ static bool CompileParsedFunctionHelper(CompilationPipeline* pipeline,
ParsedFunction* parsed_function,
bool optimized,
intptr_t osr_id,
Code* optimized_result_code) {
BackgroundCompilationResult* result) {
const Function& function = parsed_function->function();
if (optimized && !function.IsOptimizable()) {
return false;
Expand Down Expand Up @@ -772,12 +772,23 @@ static bool CompileParsedFunctionHelper(CompilationPipeline* pipeline,
graph_compiler.FinalizeStaticCallTargetsTable(code);

if (optimized) {
if (optimized_result_code != NULL) {
if (result != NULL) {
ASSERT(!Thread::Current()->IsMutatorThread());
// Do not install code, but return it instead.
// Since code dependencies (CHA, fields) are defined eagerly,
// the code may be disabled before installing it.
code.set_owner(function);
*optimized_result_code = code.raw();
result->set_result_code(code);
// Disable invalidation counters that are not relevant.
if (thread->cha()->leaf_classes().is_empty()) {
result->ClearCHAInvalidationGen();
}
if (flow_graph->guarded_fields()->is_empty()) {
result->ClearFieldInnvalidationGen();
}
if (!parsed_function->HasDeferredPrefixes()) {
result->ClearPrefixInnvalidationGen();
}
} else {
const bool is_osr = osr_id != Compiler::kNoOSRDeoptId;
function.InstallOptimizedCode(code, is_osr);
Expand Down Expand Up @@ -1023,7 +1034,7 @@ static RawError* CompileFunctionHelper(CompilationPipeline* pipeline,
const Function& function,
bool optimized,
intptr_t osr_id,
Code* result_code) {
BackgroundCompilationResult* result) {
// Check that we optimize if 'Compiler::always_optimize()' is set to true,
// except if the function is marked as not optimizable.
ASSERT(!function.IsOptimizable() ||
Expand Down Expand Up @@ -1066,7 +1077,7 @@ static RawError* CompileFunctionHelper(CompilationPipeline* pipeline,
parsed_function,
optimized,
osr_id,
result_code);
result);
if (!success) {
if (optimized) {
ASSERT(!Compiler::always_optimize()); // Optimized is the only code.
Expand Down Expand Up @@ -1189,7 +1200,7 @@ RawError* Compiler::EnsureUnoptimizedCode(Thread* thread,
RawError* Compiler::CompileOptimizedFunction(Thread* thread,
const Function& function,
intptr_t osr_id,
Code* result_code) {
BackgroundCompilationResult* res) {
VMTagScope tagScope(thread, VMTag::kCompileOptimizedTagId);
TIMELINE_FUNCTION_COMPILATION_DURATION(thread,
"OptimizedFunction", function);
Expand All @@ -1204,7 +1215,7 @@ RawError* Compiler::CompileOptimizedFunction(Thread* thread,
function,
true, /* optimized */
osr_id,
result_code);
res);
}


Expand Down Expand Up @@ -1465,8 +1476,16 @@ class CompilationWorkQueue : public ValueObject {
RawFunction* LastFunction() { return Function::RawCast(Last()); }

void PushBackCode(const Code& code) { PushBack(code); }
void PushBackInteger(const Integer& value) { PushBack(value); }
RawCode* PopBackCode() { return Code::RawCast(PopBack()); }
RawCode* LastCode() { return Code::RawCast(Last()); }
RawInteger* PopBackInteger() {
Object& o = Object::Handle(PopBack());
if (o.IsNull()) {
return Integer::null();
} else {
return Integer::Cast(o).raw();
}
}

private:
// Adds to the queue only if 'function' is not already in there.
Expand Down Expand Up @@ -1511,6 +1530,87 @@ class CompilationWorkQueue : public ValueObject {
};



BackgroundCompilationResult::BackgroundCompilationResult()
: result_code_(Code::Handle()),
cha_invalidation_gen_(Integer::Handle()),
field_invalidation_gen_(Integer::Handle()),
prefix_invalidation_gen_(Integer::Handle()) {
}


void BackgroundCompilationResult::Init() {
Isolate* i = Isolate::Current();
result_code_ = Code::null();
cha_invalidation_gen_ = Integer::New(i->cha_invalidation_gen(), Heap::kOld);
field_invalidation_gen_ =
Integer::New(i->field_invalidation_gen(), Heap::kOld);
prefix_invalidation_gen_ =
Integer::New(i->prefix_invalidation_gen(), Heap::kOld);
}


bool BackgroundCompilationResult::IsValid() const {
if (result_code().IsNull() || result_code().IsDisabled()) {
return false;
}
Isolate* i = Isolate::Current();
if (!cha_invalidation_gen_.IsNull() &&
(cha_invalidation_gen_.AsInt64Value() != i->cha_invalidation_gen())) {
return false;
}
if (!field_invalidation_gen_.IsNull() &&
(field_invalidation_gen_.AsInt64Value() != i->field_invalidation_gen())) {
return false;
}
if (!prefix_invalidation_gen_.IsNull() &&
(prefix_invalidation_gen_.AsInt64Value() !=
i->prefix_invalidation_gen())) {
return false;
}
return true;
}


void BackgroundCompilationResult::PrintValidity() const {
Object& o = Object::Handle(result_code().owner());
THR_Print("BackgroundCompilationResult: %s\n",
Function::Cast(o).ToQualifiedCString());
if (result_code().IsNull()) {
THR_Print(" result_code is NULL\n");
return;
}
if (result_code().IsDisabled()) {
THR_Print(" result_code is disabled\n");
return;
}
Isolate* i = Isolate::Current();
THR_Print(" cha_invalidation_gen: %s (current: %u)\n",
cha_invalidation_gen_.ToCString(), i->cha_invalidation_gen());
THR_Print(" field_invalidation_gen: %s (current: %u)\n",
field_invalidation_gen_.ToCString(), i->field_invalidation_gen());
THR_Print(" prefix_invalidation_gen: %s (current: %u)\n",
prefix_invalidation_gen_.ToCString(), i->prefix_invalidation_gen());
}


void BackgroundCompilationResult::PushOnQueue(
CompilationWorkQueue* queue) const {
queue->PushBackCode(result_code());
queue->PushBackInteger(cha_invalidation_gen_);
queue->PushBackInteger(field_invalidation_gen_);
queue->PushBackInteger(prefix_invalidation_gen_);
}


void BackgroundCompilationResult::PopFromQueue(CompilationWorkQueue* queue) {
prefix_invalidation_gen_ = queue->PopBackInteger();
field_invalidation_gen_ = queue->PopBackInteger();
cha_invalidation_gen_ = queue->PopBackInteger();
result_code_ = queue->PopBackCode();
}


BackgroundCompiler::BackgroundCompiler(Isolate* isolate)
: isolate_(isolate), running_(true), done_(new bool()),
queue_monitor_(new Monitor()), done_monitor_(new Monitor()),
Expand All @@ -1531,16 +1631,17 @@ void BackgroundCompiler::Run() {
Zone* zone = stack_zone.GetZone();
Function& function = Function::Handle(zone);
Function& temp_function = Function::Handle(zone);
Code& code = Code::Handle(zone);
function = LastFunctionOrNull();
BackgroundCompilationResult result;
// Finish all compilation before exiting (even if running_ is changed to
// false).
while (!function.IsNull()) {
result.Init();
const Error& error = Error::Handle(zone,
Compiler::CompileOptimizedFunction(thread,
function,
Compiler::kNoOSRDeoptId,
&code));
&result));
// TODO(srdjan): We do not expect errors while compiling optimized
// code, any errors should have been caught when compiling
// unoptimized code.
Expand All @@ -1549,8 +1650,8 @@ void BackgroundCompiler::Run() {
temp_function = RemoveFunctionOrNull();
ASSERT(temp_function.raw() == function.raw());
function = LastFunctionOrNull();
ASSERT(!code.IsNull());
AddCode(code);
ASSERT(!result.result_code().IsNull());
AddResult(result);
}
}
Thread::ExitIsolateAsHelper();
Expand All @@ -1577,16 +1678,21 @@ void BackgroundCompiler::CompileOptimized(const Function& function) {


void BackgroundCompiler::InstallGeneratedCode() {
ASSERT(Thread::Current()->IsMutatorThread());
MonitorLocker ml(queue_monitor_);
CompilationWorkQueue queue(CodesQueue());
Code& code = Code::Handle();
CompilationWorkQueue queue(ResultQueue());
Object& owner = Object::Handle();
for (intptr_t i = 0; i < queue.Length(); i++) {
code = queue.PopBackCode();
if (code.IsDisabled()) continue;
owner = code.owner();
BackgroundCompilationResult result;
result.PopFromQueue(&queue);
owner = result.result_code().owner();
const Function& function = Function::Cast(owner);
function.InstallOptimizedCode(code, false /* not OSR */);
if (result.IsValid()) {
function.InstallOptimizedCode(result.result_code(), false /* not OSR */);
} else if (FLAG_trace_compiler) {
THR_Print("Drop code generated in the background compiler:\n");
result.PrintValidity();
}
if (function.usage_counter() < 0) {
// Reset to 0 so that it can be recompiled if needed.
function.set_usage_counter(0);
Expand All @@ -1601,9 +1707,8 @@ GrowableObjectArray* BackgroundCompiler::FunctionsQueue() const {
}


GrowableObjectArray* BackgroundCompiler::CodesQueue() const {
// Use code queue
return &GrowableObjectArray::ZoneHandle(isolate_->compilation_code_queue());
GrowableObjectArray* BackgroundCompiler::ResultQueue() const {
return &GrowableObjectArray::ZoneHandle(isolate_->compilation_result_queue());
}


Expand Down Expand Up @@ -1633,10 +1738,10 @@ RawFunction* BackgroundCompiler::LastFunctionOrNull() const {
}


void BackgroundCompiler::AddCode(const Code& c) {
void BackgroundCompiler::AddResult(const BackgroundCompilationResult& value) {
MonitorLocker ml(queue_monitor_);
CompilationWorkQueue queue(CodesQueue());
queue.PushBackCode(c);
CompilationWorkQueue queue(ResultQueue());
value.PushOnQueue(&queue);
}


Expand Down Expand Up @@ -1680,7 +1785,7 @@ void BackgroundCompiler::EnsureInit(Thread* thread) {
isolate->set_background_compiler(task);
isolate->set_compilation_function_queue(GrowableObjectArray::Handle(
thread->zone(), GrowableObjectArray::New()));
isolate->set_compilation_code_queue(GrowableObjectArray::Handle(
isolate->set_compilation_result_queue(GrowableObjectArray::Handle(
thread->zone(), GrowableObjectArray::New()));
start_task = true;
}
Expand Down
47 changes: 44 additions & 3 deletions runtime/vm/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,54 @@ namespace dart {
// Forward declarations.
class Class;
class Code;
class CompilationWorkQueue;
class Function;
class Library;
class ParsedFunction;
class RawInstance;
class Script;
class SequenceNode;

// Carries result from background compilation: code and generation counters
// that help check if the code may have become invalid during background
// compilation.
class BackgroundCompilationResult : public ValueObject {
public:
BackgroundCompilationResult();

// Initializes with current isolate-stored generations
void Init();

void set_result_code(const Code& value) { result_code_ = value.raw(); }
const Code& result_code() const { return result_code_; }

// Returns true if all relevant gen-counts are current and code is valid.
bool IsValid() const;

// Remove gen-counts from validation check.
void ClearCHAInvalidationGen() {
cha_invalidation_gen_ = Integer::null();
}
void ClearFieldInnvalidationGen() {
field_invalidation_gen_ = Integer::null();
}
void ClearPrefixInnvalidationGen() {
prefix_invalidation_gen_ = Integer::null();
}

void PushOnQueue(CompilationWorkQueue* queue) const;
void PopFromQueue(CompilationWorkQueue* queue);

void PrintValidity() const;

private:
Code& result_code_;
Integer& cha_invalidation_gen_;
Integer& field_invalidation_gen_;
Integer& prefix_invalidation_gen_;
};


class Compiler : public AllStatic {
public:
static const intptr_t kNoOSRDeoptId = Thread::kNoDeoptId;
Expand Down Expand Up @@ -58,7 +99,7 @@ class Compiler : public AllStatic {
Thread* thread,
const Function& function,
intptr_t osr_id = kNoOSRDeoptId,
Code* result_code = NULL);
BackgroundCompilationResult* res = NULL);

// Generates code for given parsed function (without parsing it again) and
// sets its code field.
Expand Down Expand Up @@ -136,15 +177,15 @@ class BackgroundCompiler : public ThreadPool::Task {
explicit BackgroundCompiler(Isolate* isolate);

GrowableObjectArray* FunctionsQueue() const;
GrowableObjectArray* CodesQueue() const;
GrowableObjectArray* ResultQueue() const;

virtual void Run();

void AddFunction(const Function& f);
RawFunction* RemoveFunctionOrNull();
RawFunction* LastFunctionOrNull() const;

void AddCode(const Code& c);
void AddResult(const BackgroundCompilationResult& value);

Isolate* isolate_;
bool running_; // While true, will try to read queue and compile.
Expand Down
13 changes: 8 additions & 5 deletions runtime/vm/isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -789,14 +789,17 @@ Isolate::Isolate(const Dart_IsolateFlags& api_flags)
deoptimized_code_array_(GrowableObjectArray::null()),
background_compiler_(NULL),
compilation_function_queue_(GrowableObjectArray::null()),
compilation_code_queue_(GrowableObjectArray::null()),
compilation_result_queue_(GrowableObjectArray::null()),
pending_service_extension_calls_(GrowableObjectArray::null()),
registered_service_extension_handlers_(GrowableObjectArray::null()),
metrics_list_head_(NULL),
compilation_allowed_(true),
all_classes_finalized_(false),
next_(NULL),
pause_loop_monitor_(NULL) {
pause_loop_monitor_(NULL),
cha_invalidation_gen_(0),
field_invalidation_gen_(0),
prefix_invalidation_gen_(0) {
flags_.CopyFrom(api_flags);
Thread::Current()->set_vm_tag(VMTag::kEmbedderTagId);
set_user_tag(UserTags::kDefaultUserTag);
Expand Down Expand Up @@ -1774,7 +1777,7 @@ void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor,
&compilation_function_queue_));

visitor->VisitPointer(reinterpret_cast<RawObject**>(
&compilation_code_queue_));
&compilation_result_queue_));

// Visit the deoptimized code array which is stored in the isolate.
visitor->VisitPointer(
Expand Down Expand Up @@ -2007,9 +2010,9 @@ void Isolate::set_compilation_function_queue(
}


void Isolate::set_compilation_code_queue(
void Isolate::set_compilation_result_queue(
const GrowableObjectArray& value) {
compilation_code_queue_ = value.raw();
compilation_result_queue_ = value.raw();
}


Expand Down
Loading

0 comments on commit fe4a5d7

Please sign in to comment.