Index: runtime/vm/compiler.cc |
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc |
index def329154eb469694d1237c4e494931860da7c2f..bbf1430aa12cf3eccf45bf1e1a984ac7ba9e40a2 100644 |
--- a/runtime/vm/compiler.cc |
+++ b/runtime/vm/compiler.cc |
@@ -1466,93 +1466,166 @@ RawObject* Compiler::ExecuteOnce(SequenceNode* fragment) { |
} |
-// A simple work queue containing functions to be optimized or code generated. |
-// Use PushFrontFunction and PopBackFunction to add and remove from the function |
-// queue and PushBackCode and PopBackCode to add and remove from the code queue. |
-// TODO(srdjan): Write a more efficient implementation. |
-class CompilationWorkQueue : public ValueObject { |
+// C-heap allocated background compilation queue element. |
+class QueueElement { |
public: |
- explicit CompilationWorkQueue(GrowableObjectArray* data) : data_(data) {} |
- |
- intptr_t IsEmpty() const { return data_->Length() == 0; } |
- intptr_t Length() const { return data_->Length(); } |
- |
- void PushFrontFunction(const Function& function) { PushFront(function); } |
- RawFunction* PopBackFunction() { return Function::RawCast(PopBack()); } |
- 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()); } |
- RawInteger* PopBackInteger() { |
- Object& o = Object::Handle(PopBack()); |
- if (o.IsNull()) { |
- return Integer::null(); |
- } else { |
- return Integer::Cast(o).raw(); |
- } |
+ explicit QueueElement(const Function& function) |
+ : next_(NULL), |
+ obj_(function.raw()), |
+ cha_invalidation_gen_(Isolate::kInvalidGen), |
+ field_invalidation_gen_(Isolate::kInvalidGen), |
+ prefix_invalidation_gen_(Isolate::kInvalidGen) { |
+ ASSERT(Thread::Current()->IsMutatorThread()); |
+ } |
+ |
+ ~QueueElement() { |
+ ASSERT(Thread::Current()->IsMutatorThread()); |
+ obj_ = Object::null(); |
+ } |
+ |
+ void Clear() { |
+ next_ = NULL; |
+ obj_ = Object::null(); |
+ cha_invalidation_gen_ = Isolate::kInvalidGen; |
+ field_invalidation_gen_ = Isolate::kInvalidGen; |
+ prefix_invalidation_gen_ = Isolate::kInvalidGen; |
+ } |
+ |
+ RawFunction* Function() const { return Function::RawCast(obj_); } |
+ RawCode* Code() const { |
+ return (obj_ == Object::null()) ? Code::null() : Code::RawCast(obj_); |
+ } |
+ |
+ uint32_t cha_invalidation_gen() const { return cha_invalidation_gen_; } |
+ uint32_t field_invalidation_gen() const { return field_invalidation_gen_; } |
+ uint32_t prefix_invalidation_gen() const { return prefix_invalidation_gen_; } |
+ |
+ void set_next(QueueElement* elem) { next_ = elem; } |
+ QueueElement* next() const { return next_; } |
+ |
+ RawObject** obj_ptr() { return &obj_; } |
+ RawObject* obj() const { return obj_; } |
+ |
+ void SetFromResult(const BackgroundCompilationResult& value) { |
+ ASSERT(!value.result_code().IsNull()); |
+ obj_ = value.result_code().raw(); |
+ cha_invalidation_gen_ = value.cha_invalidation_gen(); |
+ field_invalidation_gen_ = value.field_invalidation_gen(); |
+ prefix_invalidation_gen_ = value.prefix_invalidation_gen(); |
} |
private: |
- // Adds to the queue only if 'function' is not already in there. |
- void PushFront(const Object& value) { |
- for (intptr_t i = 0; i < data_->Length(); i++) { |
- if (data_->At(i) == value.raw()) { |
- return; |
- } |
+ QueueElement* next_; |
+ |
+ RawObject* obj_; // Code or Function. |
+ uint32_t cha_invalidation_gen_; |
+ uint32_t field_invalidation_gen_; |
+ uint32_t prefix_invalidation_gen_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(QueueElement); |
+}; |
+ |
+ |
+// Allocated in C-heap. Handles both input and output of background compilation. |
+// It implements a FIFO queue, using Peek, Add, Remove operations. |
+class BackgroundCompilationQueue { |
+ public: |
+ BackgroundCompilationQueue() : first_(NULL), last_(NULL) {} |
+ ~BackgroundCompilationQueue() { |
+ while (!IsEmpty()) { |
+ QueueElement* e = Remove(); |
+ delete e; |
} |
- // Insert new element in front. |
- Object& f = Object::Handle(); |
- data_->Add(f, Heap::kOld); |
- for (intptr_t i = data_->Length() - 1; i > 0; i--) { |
- f = data_->At(i - 1); |
- data_->SetAt(i, f); |
+ ASSERT((first_ == NULL) && (last_ == NULL)); |
+ } |
+ |
+ void VisitObjectPointers(ObjectPointerVisitor* visitor) { |
+ ASSERT(visitor != NULL); |
+ QueueElement* p = first_; |
+ while (p != NULL) { |
+ visitor->VisitPointer(p->obj_ptr()); |
+ p = p->next(); |
} |
- data_->SetAt(0, value); |
} |
+ bool IsEmpty() const { return first_ == NULL; } |
- void PushBack(const Object& value) { |
- data_->Add(value, Heap::kOld); |
+ void Add(QueueElement* value) { |
+ ASSERT(value != NULL); |
+ if (first_ == NULL) { |
+ first_ = value; |
+ } else { |
+ last_->set_next(value); |
+ } |
+ value->set_next(NULL); |
+ last_ = value; |
} |
+ QueueElement* Peek() const { |
+ return first_; |
+ } |
- RawObject* PopBack() { |
- ASSERT(!IsEmpty()); |
- Object& result = Object::Handle(); |
- result = data_->At(data_->Length() - 1); |
- data_->SetLength(data_->Length() - 1); |
- return result.raw(); |
+ RawFunction* PeekFunction() const { |
+ QueueElement* e = Peek(); |
+ if (e == NULL) { |
+ return Function::null(); |
+ } else { |
+ return e->Function(); |
+ } |
+ } |
+ |
+ QueueElement* Remove() { |
+ ASSERT(first_ != NULL); |
+ QueueElement* result = first_; |
+ first_ = first_->next(); |
+ if (first_ == NULL) { |
+ last_ = NULL; |
+ } |
+ return result; |
} |
- RawObject* Last() { |
- ASSERT(!IsEmpty()); |
- return data_->At(data_->Length() - 1); |
+ bool ContainsObj(const Object& obj) const { |
+ QueueElement* p = first_; |
+ while (p != NULL) { |
+ if (p->obj() == obj.raw()) { |
+ return true; |
+ } |
+ p = p->next(); |
+ } |
+ return false; |
} |
- GrowableObjectArray* data_; |
+ private: |
+ QueueElement* first_; |
+ QueueElement* last_; |
- DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationWorkQueue); |
+ DISALLOW_COPY_AND_ASSIGN(BackgroundCompilationQueue); |
}; |
- |
BackgroundCompilationResult::BackgroundCompilationResult() |
: result_code_(Code::Handle()), |
- cha_invalidation_gen_(Integer::Handle()), |
- field_invalidation_gen_(Integer::Handle()), |
- prefix_invalidation_gen_(Integer::Handle()) { |
+ cha_invalidation_gen_(Isolate::kInvalidGen), |
+ field_invalidation_gen_(Isolate::kInvalidGen), |
+ prefix_invalidation_gen_(Isolate::kInvalidGen) { |
} |
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); |
+ cha_invalidation_gen_ = i->cha_invalidation_gen(); |
+ field_invalidation_gen_ = i->field_invalidation_gen(); |
+ prefix_invalidation_gen_ = i->prefix_invalidation_gen(); |
+} |
+ |
+ |
+void BackgroundCompilationResult::SetFromQElement(QueueElement* value) { |
+ ASSERT(value != NULL); |
+ result_code_ = value->Code(); |
+ cha_invalidation_gen_ = value->cha_invalidation_gen(); |
+ field_invalidation_gen_ = value->field_invalidation_gen(); |
+ prefix_invalidation_gen_ = value->prefix_invalidation_gen(); |
} |
@@ -1561,17 +1634,16 @@ bool BackgroundCompilationResult::IsValid() const { |
return false; |
} |
Isolate* i = Isolate::Current(); |
- if (!cha_invalidation_gen_.IsNull() && |
- (cha_invalidation_gen_.AsInt64Value() != i->cha_invalidation_gen())) { |
+ if ((cha_invalidation_gen_ != Isolate::kInvalidGen) && |
+ (cha_invalidation_gen_ != i->cha_invalidation_gen())) { |
return false; |
} |
- if (!field_invalidation_gen_.IsNull() && |
- (field_invalidation_gen_.AsInt64Value() != i->field_invalidation_gen())) { |
+ if ((field_invalidation_gen_ != Isolate::kInvalidGen) && |
+ (field_invalidation_gen_ != i->field_invalidation_gen())) { |
return false; |
} |
- if (!prefix_invalidation_gen_.IsNull() && |
- (prefix_invalidation_gen_.AsInt64Value() != |
- i->prefix_invalidation_gen())) { |
+ if ((prefix_invalidation_gen_ != Isolate::kInvalidGen) && |
+ (prefix_invalidation_gen_ != i->prefix_invalidation_gen())) { |
return false; |
} |
return true; |
@@ -1591,38 +1663,20 @@ void BackgroundCompilationResult::PrintValidity() const { |
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(); |
+ THR_Print(" cha_invalidation_gen: %u (current: %u)\n", |
+ cha_invalidation_gen_, i->cha_invalidation_gen()); |
+ THR_Print(" field_invalidation_gen: %u (current: %u)\n", |
+ field_invalidation_gen_, i->field_invalidation_gen()); |
+ THR_Print(" prefix_invalidation_gen: %u (current: %u)\n", |
+ prefix_invalidation_gen_, i->prefix_invalidation_gen()); |
} |
BackgroundCompiler::BackgroundCompiler(Isolate* isolate) |
: isolate_(isolate), running_(true), done_(new bool()), |
queue_monitor_(new Monitor()), done_monitor_(new Monitor()), |
- function_queue_length_(0), |
- compilation_function_queue_(GrowableObjectArray::null()), |
- compilation_result_queue_(GrowableObjectArray::null()) { |
+ function_queue_(new BackgroundCompilationQueue()), |
+ result_queue_(new BackgroundCompilationQueue()) { |
*done_ = false; |
} |
@@ -1638,12 +1692,9 @@ void BackgroundCompiler::Run() { |
Zone* zone = stack_zone.GetZone(); |
HANDLESCOPE(thread); |
Function& function = Function::Handle(zone); |
- Function& temp_function = Function::Handle(zone); |
- function = LastFunctionOrNull(); |
+ function = function_queue()->PeekFunction(); |
BackgroundCompilationResult result; |
- // Finish all compilation before exiting (even if running_ is changed to |
- // false). |
- while (!function.IsNull()) { |
+ while (running_ && !function.IsNull()) { |
result.Init(); |
const Error& error = Error::Handle(zone, |
Compiler::CompileOptimizedFunction(thread, |
@@ -1655,25 +1706,25 @@ void BackgroundCompiler::Run() { |
// unoptimized code. |
// If it still happens mark function as not optimizable. |
ASSERT(error.IsNull()); |
- temp_function = RemoveFunctionOrNull(); |
- ASSERT(temp_function.raw() == function.raw()); |
- function = LastFunctionOrNull(); |
- ASSERT(!result.result_code().IsNull()); |
- AddResult(result); |
+ // Reuse the input QueueElement to return the result. |
+ QueueElement* qelem = function_queue()->Remove(); |
+ qelem->Clear(); |
+ result_queue()->Add(qelem); |
+ // Add 'qelem' to the queue first so that it gets visited by GC. |
+ qelem->SetFromResult(result); |
+ function = function_queue()->PeekFunction(); |
} |
} |
Thread::ExitIsolateAsHelper(); |
{ |
// Wait to be notified when the work queue is not empty. |
MonitorLocker ml(queue_monitor_); |
- while ((function_queue_length() == 0) && running_) { |
+ while (function_queue()->IsEmpty() && running_) { |
ml.Wait(); |
} |
} |
} // while running |
- compilation_function_queue_ = GrowableObjectArray::null(); |
- compilation_result_queue_ = GrowableObjectArray::null(); |
{ |
// Notify that the thread is done. |
MonitorLocker ml_done(done_monitor_); |
@@ -1684,18 +1735,28 @@ void BackgroundCompiler::Run() { |
void BackgroundCompiler::CompileOptimized(const Function& function) { |
- AddFunction(function); |
+ ASSERT(Thread::Current()->IsMutatorThread()); |
+ MonitorLocker ml(queue_monitor_); |
+ if (function_queue()->ContainsObj(function)) { |
+ return; |
+ } |
+ QueueElement* elem = new QueueElement(function); |
+ function_queue()->Add(elem); |
+ ml.Notify(); |
} |
void BackgroundCompiler::InstallGeneratedCode() { |
ASSERT(Thread::Current()->IsMutatorThread()); |
MonitorLocker ml(queue_monitor_); |
- CompilationWorkQueue queue(ResultQueue()); |
Object& owner = Object::Handle(); |
- for (intptr_t i = 0; i < queue.Length(); i++) { |
+ while (result_queue()->Peek() != NULL) { |
BackgroundCompilationResult result; |
- result.PopFromQueue(&queue); |
+ QueueElement* elem = result_queue()->Remove(); |
+ ASSERT(elem != NULL); |
+ result.SetFromQElement(elem); |
+ delete elem; |
+ |
owner = result.result_code().owner(); |
const Function& function = Function::Cast(owner); |
if (result.IsValid()) { |
@@ -1712,67 +1773,9 @@ void BackgroundCompiler::InstallGeneratedCode() { |
} |
-GrowableObjectArray* BackgroundCompiler::FunctionsQueue() const { |
- return &GrowableObjectArray::ZoneHandle(compilation_function_queue_); |
-} |
- |
- |
-GrowableObjectArray* BackgroundCompiler::ResultQueue() const { |
- return &GrowableObjectArray::ZoneHandle(compilation_result_queue_); |
-} |
- |
- |
-void BackgroundCompiler::AddFunction(const Function& f) { |
- MonitorLocker ml(queue_monitor_); |
- CompilationWorkQueue queue(FunctionsQueue()); |
- queue.PushFrontFunction(f); |
- set_function_queue_length(queue.Length()); |
- // Notify waiting background compiler task. |
- ml.Notify(); |
-} |
- |
- |
-RawFunction* BackgroundCompiler::RemoveFunctionOrNull() { |
- MonitorLocker ml(queue_monitor_); |
- CompilationWorkQueue queue(FunctionsQueue()); |
- if (queue.IsEmpty()) return Function::null(); |
- set_function_queue_length(queue.Length() - 1); |
- return queue.PopBackFunction(); |
-} |
- |
- |
-RawFunction* BackgroundCompiler::LastFunctionOrNull() const { |
- MonitorLocker ml(queue_monitor_); |
- CompilationWorkQueue queue(FunctionsQueue()); |
- return queue.IsEmpty() ? Function::null() : queue.LastFunction(); |
-} |
- |
- |
-void BackgroundCompiler::AddResult(const BackgroundCompilationResult& value) { |
- MonitorLocker ml(queue_monitor_); |
- CompilationWorkQueue queue(ResultQueue()); |
- value.PushOnQueue(&queue); |
-} |
- |
- |
-void BackgroundCompiler::set_compilation_function_queue( |
- const GrowableObjectArray& value) { |
- compilation_function_queue_ = value.raw(); |
-} |
- |
- |
-void BackgroundCompiler::set_compilation_result_queue( |
- const GrowableObjectArray& value) { |
- compilation_result_queue_ = value.raw(); |
-} |
- |
- |
void BackgroundCompiler::VisitPointers(ObjectPointerVisitor* visitor) { |
- visitor->VisitPointer(reinterpret_cast<RawObject**>( |
- &compilation_function_queue_)); |
- |
- visitor->VisitPointer(reinterpret_cast<RawObject**>( |
- &compilation_result_queue_)); |
+ function_queue_->VisitObjectPointers(visitor); |
+ result_queue_->VisitObjectPointers(visitor); |
} |
@@ -1781,6 +1784,9 @@ void BackgroundCompiler::Stop(BackgroundCompiler* task) { |
if (task == NULL) { |
return; |
} |
+ BackgroundCompilationQueue* function_queue = task->function_queue(); |
+ BackgroundCompilationQueue* result_queue = task->result_queue(); |
+ |
Monitor* queue_monitor = task->queue_monitor_; |
Monitor* done_monitor = task->done_monitor_; |
bool* task_done = task->done_; |
@@ -1802,6 +1808,8 @@ void BackgroundCompiler::Stop(BackgroundCompiler* task) { |
delete task_done; |
delete done_monitor; |
delete queue_monitor; |
+ delete function_queue; |
+ delete result_queue; |
Isolate::Current()->set_background_compiler(NULL); |
} |
@@ -1814,17 +1822,6 @@ void BackgroundCompiler::EnsureInit(Thread* thread) { |
if (isolate->background_compiler() == NULL) { |
BackgroundCompiler* task = new BackgroundCompiler(isolate); |
isolate->set_background_compiler(task); |
- // TODO(srdjan): Temporary fix to prevent growing (and thus GC-ing) of |
- // queues while inside a MonitorLocker. Will replace GrowableObjectArray |
- // with C heap allocated linked list. |
- GrowableObjectArray& a = GrowableObjectArray::Handle( |
- thread->zone(), GrowableObjectArray::New(Heap::kOld)); |
- a.Grow(1000, Heap::kOld); |
- task->set_compilation_function_queue(a); |
- |
- a = GrowableObjectArray::New(Heap::kOld); |
- a.Grow(1000, Heap::kOld); |
- task->set_compilation_result_queue(a); |
start_task = true; |
} |
} |