| Index: runtime/vm/compiler.cc
|
| diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
|
| index 39196c19d2fe142667f2e3931092f0ac8b91f5e3..cf452bd71ea6bd45078844ff0092e3d388ad007f 100644
|
| --- a/runtime/vm/compiler.cc
|
| +++ b/runtime/vm/compiler.cc
|
| @@ -378,10 +378,13 @@ RawError* Compiler::CompileClass(const Class& cls) {
|
|
|
|
|
| // Return false if bailed out.
|
| +// If optimized_result_code is not NULL then it is caller's responsibility
|
| +// to install code.
|
| static bool CompileParsedFunctionHelper(CompilationPipeline* pipeline,
|
| ParsedFunction* parsed_function,
|
| bool optimized,
|
| - intptr_t osr_id) {
|
| + intptr_t osr_id,
|
| + Code* optimized_result_code) {
|
| const Function& function = parsed_function->function();
|
| if (optimized && !function.IsOptimizable()) {
|
| return false;
|
| @@ -447,7 +450,7 @@ static bool CompileParsedFunctionHelper(CompilationPipeline* pipeline,
|
| FlowGraphPrinter::ShouldPrint(function);
|
|
|
| if (print_flow_graph) {
|
| - if (osr_id == Thread::kNoDeoptId) {
|
| + if (osr_id == Compiler::kNoOSRDeoptId) {
|
| FlowGraphPrinter::PrintGraph("Before Optimizations", flow_graph);
|
| } else {
|
| FlowGraphPrinter::PrintGraph("For OSR", flow_graph);
|
| @@ -730,6 +733,7 @@ static bool CompileParsedFunctionHelper(CompilationPipeline* pipeline,
|
| const Code& code = Code::Handle(
|
| Code::FinalizeCode(function, &assembler, optimized));
|
| code.set_is_optimized(optimized);
|
| + code.set_owner(function);
|
|
|
| const Array& intervals = graph_compiler.inlined_code_intervals();
|
| INC_STAT(thread, total_code_size,
|
| @@ -757,12 +761,19 @@ static bool CompileParsedFunctionHelper(CompilationPipeline* pipeline,
|
| graph_compiler.FinalizeStaticCallTargetsTable(code);
|
|
|
| if (optimized) {
|
| - // We may not have previous code if 'always_optimize' is set.
|
| - if ((osr_id == Thread::kNoDeoptId) && function.HasCode()) {
|
| - Code::Handle(function.CurrentCode()).DisableDartCode();
|
| + if (optimized_result_code != NULL) {
|
| + // 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();
|
| + } else {
|
| + const bool is_osr = osr_id != Compiler::kNoOSRDeoptId;
|
| + function.InstallOptimizedCode(code, is_osr);
|
| }
|
| - function.AttachCode(code);
|
|
|
| + // TODO(srdjan): In background compilation, verify that CHA has not
|
| + // been invalidated in the meantime.
|
| // Register code with the classes it depends on because of CHA.
|
| for (intptr_t i = 0;
|
| i < thread->cha()->leaf_classes().length();
|
| @@ -1000,7 +1011,8 @@ static void CheckInliningIntervals(const Function& function) {
|
| static RawError* CompileFunctionHelper(CompilationPipeline* pipeline,
|
| const Function& function,
|
| bool optimized,
|
| - intptr_t osr_id) {
|
| + intptr_t osr_id,
|
| + Code* result_code) {
|
| // Check that we optimize if 'Compiler::always_optimize()' is set to true,
|
| // except if the function is marked as not optimizable.
|
| ASSERT(!function.IsOptimizable() ||
|
| @@ -1019,7 +1031,7 @@ static RawError* CompileFunctionHelper(CompilationPipeline* pipeline,
|
| thread, Function::ZoneHandle(zone, function.raw()));
|
| if (FLAG_trace_compiler) {
|
| THR_Print("Compiling %s%sfunction: '%s' @ token %" Pd ", size %" Pd "\n",
|
| - (osr_id == Thread::kNoDeoptId ? "" : "osr "),
|
| + (osr_id == Compiler::kNoOSRDeoptId ? "" : "osr "),
|
| (optimized ? "optimized " : ""),
|
| function.ToFullyQualifiedCString(),
|
| function.token_pos(),
|
| @@ -1042,7 +1054,8 @@ static RawError* CompileFunctionHelper(CompilationPipeline* pipeline,
|
| const bool success = CompileParsedFunctionHelper(pipeline,
|
| parsed_function,
|
| optimized,
|
| - osr_id);
|
| + osr_id,
|
| + result_code);
|
| if (!success) {
|
| if (optimized) {
|
| ASSERT(!Compiler::always_optimize()); // Optimized is the only code.
|
| @@ -1120,8 +1133,11 @@ RawError* Compiler::CompileFunction(Thread* thread,
|
| const bool optimized =
|
| Compiler::always_optimize() && function.IsOptimizable();
|
|
|
| - return CompileFunctionHelper(pipeline, function, optimized,
|
| - Thread::kNoDeoptId);
|
| + return CompileFunctionHelper(pipeline,
|
| + function,
|
| + optimized,
|
| + kNoOSRDeoptId, /* not OSR */
|
| + NULL /* no result code */);
|
| }
|
|
|
|
|
| @@ -1137,7 +1153,11 @@ RawError* Compiler::EnsureUnoptimizedCode(Thread* thread,
|
| CompilationPipeline* pipeline =
|
| CompilationPipeline::New(thread->zone(), function);
|
| const Error& error = Error::Handle(
|
| - CompileFunctionHelper(pipeline, function, false, Thread::kNoDeoptId));
|
| + CompileFunctionHelper(pipeline,
|
| + function,
|
| + false, /* not optimized */
|
| + kNoOSRDeoptId, /* not OSR */
|
| + NULL /* no result code */));
|
| if (!error.IsNull()) {
|
| return error.raw();
|
| }
|
| @@ -1157,18 +1177,23 @@ RawError* Compiler::EnsureUnoptimizedCode(Thread* thread,
|
|
|
| RawError* Compiler::CompileOptimizedFunction(Thread* thread,
|
| const Function& function,
|
| - intptr_t osr_id) {
|
| + intptr_t osr_id,
|
| + Code* result_code) {
|
| VMTagScope tagScope(thread, VMTag::kCompileOptimizedTagId);
|
| TIMELINE_FUNCTION_COMPILATION_DURATION(thread,
|
| "OptimizedFunction", function);
|
|
|
| // Optimization must happen in non-mutator/Dart thread if background
|
| // compilation is on. OSR compilation still occurs in the main thread.
|
| - ASSERT((osr_id != Thread::kNoDeoptId) || !FLAG_background_compilation ||
|
| + ASSERT((osr_id != kNoOSRDeoptId) || !FLAG_background_compilation ||
|
| !thread->IsMutatorThread());
|
| CompilationPipeline* pipeline =
|
| CompilationPipeline::New(thread->zone(), function);
|
| - return CompileFunctionHelper(pipeline, function, true, osr_id);
|
| + return CompileFunctionHelper(pipeline,
|
| + function,
|
| + true, /* optimized */
|
| + osr_id,
|
| + result_code);
|
| }
|
|
|
|
|
| @@ -1182,7 +1207,8 @@ RawError* Compiler::CompileParsedFunction(
|
| CompileParsedFunctionHelper(&pipeline,
|
| parsed_function,
|
| false,
|
| - Thread::kNoDeoptId);
|
| + kNoOSRDeoptId,
|
| + NULL /* no result code */);
|
| if (FLAG_disassemble) {
|
| DisassembleCode(parsed_function->function(), false);
|
| }
|
| @@ -1288,7 +1314,8 @@ void Compiler::CompileStaticInitializer(const Field& field) {
|
| CompileParsedFunctionHelper(&pipeline,
|
| parsed_function,
|
| false, // optimized
|
| - Thread::kNoDeoptId);
|
| + kNoOSRDeoptId,
|
| + NULL /* no result code */);
|
|
|
| const Function& initializer = parsed_function->function();
|
| field.SetPrecompiledInitializer(initializer);
|
| @@ -1319,7 +1346,8 @@ RawObject* Compiler::EvaluateStaticInitializer(const Field& field) {
|
| CompileParsedFunctionHelper(&pipeline,
|
| parsed_function,
|
| false, // optimized
|
| - Thread::kNoDeoptId);
|
| + kNoOSRDeoptId,
|
| + NULL /* no result code */);
|
| initializer = parsed_function->function().raw();
|
| Code::Handle(initializer.unoptimized_code()).set_var_descriptors(
|
| Object::empty_var_descriptors());
|
| @@ -1389,7 +1417,8 @@ RawObject* Compiler::ExecuteOnce(SequenceNode* fragment) {
|
| CompileParsedFunctionHelper(&pipeline,
|
| parsed_function,
|
| false,
|
| - Thread::kNoDeoptId);
|
| + kNoOSRDeoptId,
|
| + NULL /* no result code */);
|
| Code::Handle(func.unoptimized_code()).set_var_descriptors(
|
| Object::empty_var_descriptors());
|
|
|
| @@ -1409,51 +1438,63 @@ RawObject* Compiler::ExecuteOnce(SequenceNode* fragment) {
|
| }
|
|
|
|
|
| -// A simple work queue containing functions to be optimized. Use
|
| -// PushFront and PopBack to add and read from queue.
|
| +// 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 {
|
| public:
|
| - explicit CompilationWorkQueue(Isolate* isolate) :
|
| - data_(GrowableObjectArray::Handle()) {
|
| - data_ = isolate->background_compilation_queue();
|
| - }
|
| + 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()); }
|
|
|
| - intptr_t IsEmpty() const { return data_.Length() == 0; }
|
| - intptr_t Length() const { return data_.Length(); }
|
| + void PushBackCode(const Code& code) { PushBack(code); }
|
| + RawCode* PopBackCode() { return Code::RawCast(PopBack()); }
|
| + RawCode* LastCode() { return Code::RawCast(Last()); }
|
|
|
| + private:
|
| // Adds to the queue only if 'function' is not already in there.
|
| - void PushFront(const Function& function) {
|
| - for (intptr_t i = 0; i < data_.Length(); i++) {
|
| - if (data_.At(i) == function.raw()) {
|
| + void PushFront(const Object& value) {
|
| + for (intptr_t i = 0; i < data_->Length(); i++) {
|
| + if (data_->At(i) == value.raw()) {
|
| return;
|
| }
|
| }
|
| // Insert new element in front.
|
| Object& f = Object::Handle();
|
| - data_.Add(f);
|
| - for (intptr_t i = data_.Length() - 1; i > 0; i--) {
|
| - f = data_.At(i - 1);
|
| - data_.SetAt(i, f);
|
| + data_->Add(f);
|
| + for (intptr_t i = data_->Length() - 1; i > 0; i--) {
|
| + f = data_->At(i - 1);
|
| + data_->SetAt(i, f);
|
| }
|
| - data_.SetAt(0, function);
|
| + data_->SetAt(0, value);
|
| + }
|
| +
|
| +
|
| + void PushBack(const Object& value) {
|
| + data_->Add(value, Heap::kOld);
|
| }
|
|
|
| - RawFunction* PopBack() {
|
| +
|
| + RawObject* PopBack() {
|
| ASSERT(!IsEmpty());
|
| Object& result = Object::Handle();
|
| - result = data_.At(data_.Length() - 1);
|
| - data_.SetLength(data_.Length() - 1);
|
| - return Function::Cast(result).raw();
|
| + result = data_->At(data_->Length() - 1);
|
| + data_->SetLength(data_->Length() - 1);
|
| + return result.raw();
|
| }
|
|
|
| - RawFunction* Last() {
|
| + RawObject* Last() {
|
| ASSERT(!IsEmpty());
|
| - return Function::RawCast(data_.At(data_.Length() - 1));
|
| + return data_->At(data_->Length() - 1);
|
| }
|
|
|
| - private:
|
| - GrowableObjectArray& data_;
|
| + GrowableObjectArray* data_;
|
|
|
| DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationWorkQueue);
|
| };
|
| @@ -1462,7 +1503,7 @@ class CompilationWorkQueue : public ValueObject {
|
| BackgroundCompiler::BackgroundCompiler(Isolate* isolate)
|
| : isolate_(isolate), running_(true), done_(new bool()),
|
| queue_monitor_(new Monitor()), done_monitor_(new Monitor()),
|
| - queue_length_(0) {
|
| + function_queue_length_(0) {
|
| *done_ = false;
|
| }
|
|
|
| @@ -1479,27 +1520,34 @@ void BackgroundCompiler::Run() {
|
| Zone* zone = stack_zone.GetZone();
|
| Function& function = Function::Handle(zone);
|
| Function& temp_function = Function::Handle(zone);
|
| - function = LastOrNull();
|
| - while (!function.IsNull() && running_) {
|
| + Code& code = Code::Handle(zone);
|
| + function = LastFunctionOrNull();
|
| + // Finish all compilation before exiting (even if running_ is changed to
|
| + // false).
|
| + while (!function.IsNull()) {
|
| const Error& error = Error::Handle(zone,
|
| - Compiler::CompileOptimizedFunction(thread, function));
|
| + Compiler::CompileOptimizedFunction(thread,
|
| + function,
|
| + Compiler::kNoOSRDeoptId,
|
| + &code));
|
| // TODO(srdjan): We do not expect errors while compiling optimized
|
| // code, any errors should have been caught when compiling
|
| // unoptimized code.
|
| // If it still happens mark function as not optimizable.
|
| ASSERT(error.IsNull());
|
| - temp_function = RemoveOrNull();
|
| + temp_function = RemoveFunctionOrNull();
|
| ASSERT(temp_function.raw() == function.raw());
|
| // Reset to 0 so that it can be recompiled if needed.
|
| function.set_usage_counter(0);
|
| - function = LastOrNull();
|
| + function = LastFunctionOrNull();
|
| + AddCode(code);
|
| }
|
| }
|
| Thread::ExitIsolateAsHelper();
|
| {
|
| // Wait to be notified when the work queue is not empty.
|
| MonitorLocker ml(queue_monitor_);
|
| - while ((queue_length() == 0) && running_) {
|
| + while ((function_queue_length() == 0) && running_) {
|
| ml.Wait();
|
| }
|
| }
|
| @@ -1514,36 +1562,72 @@ void BackgroundCompiler::Run() {
|
|
|
|
|
| void BackgroundCompiler::CompileOptimized(const Function& function) {
|
| - Add(function);
|
| + AddFunction(function);
|
| }
|
|
|
|
|
| -void BackgroundCompiler::Add(const Function& f) {
|
| +void BackgroundCompiler::InstallGeneratedCode() {
|
| MonitorLocker ml(queue_monitor_);
|
| - CompilationWorkQueue queue(isolate_);
|
| - queue.PushFront(f);
|
| - set_queue_length(queue.Length());
|
| + CompilationWorkQueue queue(CodesQueue());
|
| + Code& code = Code::Handle();
|
| + Object& owner = Object::Handle();
|
| + for (intptr_t i = 0; i < queue.Length(); i++) {
|
| + code = queue.PopBackCode();
|
| + if (code.IsDisabled()) continue;
|
| + owner = code.owner();
|
| + ASSERT(owner.IsFunction());
|
| + Function::Cast(owner).InstallOptimizedCode(code, false /* not OSR */);
|
| + }
|
| +}
|
| +
|
| +
|
| +GrowableObjectArray* BackgroundCompiler::FunctionsQueue() const {
|
| + return
|
| + &GrowableObjectArray::ZoneHandle(isolate_->compilation_function_queue());
|
| +}
|
| +
|
| +
|
| +GrowableObjectArray* BackgroundCompiler::CodesQueue() const {
|
| + // Use code queue
|
| + return &GrowableObjectArray::ZoneHandle(isolate_->compilation_code_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::RemoveOrNull() {
|
| +RawFunction* BackgroundCompiler::RemoveFunctionOrNull() {
|
| MonitorLocker ml(queue_monitor_);
|
| - CompilationWorkQueue queue(isolate_);
|
| + CompilationWorkQueue queue(FunctionsQueue());
|
| if (queue.IsEmpty()) return Function::null();
|
| - set_queue_length(queue.Length() - 1);
|
| - return queue.PopBack();
|
| + 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();
|
| }
|
|
|
|
|
| -RawFunction* BackgroundCompiler::LastOrNull() const {
|
| +void BackgroundCompiler::AddCode(const Code& c) {
|
| MonitorLocker ml(queue_monitor_);
|
| - CompilationWorkQueue queue(isolate_);
|
| - return queue.IsEmpty() ? Function::null() : queue.Last();
|
| + CompilationWorkQueue queue(CodesQueue());
|
| + queue.PushBackCode(c);
|
| }
|
|
|
|
|
| void BackgroundCompiler::Stop(BackgroundCompiler* task) {
|
| + ASSERT(Isolate::Current()->background_compiler() == task);
|
| if (task == NULL) {
|
| return;
|
| }
|
| @@ -1568,6 +1652,7 @@ void BackgroundCompiler::Stop(BackgroundCompiler* task) {
|
| delete task_done;
|
| delete done_monitor;
|
| delete monitor;
|
| + Isolate::Current()->set_background_compiler(NULL);
|
| }
|
|
|
|
|
| @@ -1579,7 +1664,9 @@ void BackgroundCompiler::EnsureInit(Thread* thread) {
|
| if (isolate->background_compiler() == NULL) {
|
| BackgroundCompiler* task = new BackgroundCompiler(isolate);
|
| isolate->set_background_compiler(task);
|
| - isolate->set_background_compilation_queue(GrowableObjectArray::Handle(
|
| + isolate->set_compilation_function_queue(GrowableObjectArray::Handle(
|
| + thread->zone(), GrowableObjectArray::New()));
|
| + isolate->set_compilation_code_queue(GrowableObjectArray::Handle(
|
| thread->zone(), GrowableObjectArray::New()));
|
| start_task = true;
|
| }
|
|
|