| // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/compilation_trace.h" |
| |
| #include "vm/globals.h" |
| #include "vm/log.h" |
| #include "vm/longjump.h" |
| #include "vm/object_store.h" |
| #include "vm/resolver.h" |
| #include "vm/symbols.h" |
| |
| namespace dart { |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| |
| DEFINE_FLAG(bool, trace_compilation_trace, false, "Trace compilation trace."); |
| |
| CompilationTraceSaver::CompilationTraceSaver(Zone* zone) |
| : buf_(zone, 4 * KB), |
| func_name_(String::Handle(zone)), |
| cls_(Class::Handle(zone)), |
| cls_name_(String::Handle(zone)), |
| lib_(Library::Handle(zone)), |
| uri_(String::Handle(zone)) {} |
| |
| void CompilationTraceSaver::Visit(const Function& function) { |
| if (!function.HasCode()) { |
| return; // Not compiled. |
| } |
| if (function.parent_function() != Function::null()) { |
| // Lookup works poorly for local functions. We compile all local functions |
| // in a compiled function instead. |
| return; |
| } |
| |
| func_name_ = function.name(); |
| func_name_ = String::RemovePrivateKey(func_name_); |
| cls_ = function.Owner(); |
| cls_name_ = cls_.Name(); |
| cls_name_ = String::RemovePrivateKey(cls_name_); |
| lib_ = cls_.library(); |
| uri_ = lib_.url(); |
| buf_.Printf("%s,%s,%s\n", uri_.ToCString(), cls_name_.ToCString(), |
| func_name_.ToCString()); |
| } |
| |
| CompilationTraceLoader::CompilationTraceLoader(Thread* thread) |
| : thread_(thread), |
| zone_(thread->zone()), |
| uri_(String::Handle(zone_)), |
| class_name_(String::Handle(zone_)), |
| function_name_(String::Handle(zone_)), |
| function_name2_(String::Handle(zone_)), |
| lib_(Library::Handle(zone_)), |
| cls_(Class::Handle(zone_)), |
| function_(Function::Handle(zone_)), |
| function2_(Function::Handle(zone_)), |
| field_(Field::Handle(zone_)), |
| error_(Object::Handle(zone_)) {} |
| |
| static char* FindCharacter(char* str, char goal, char* limit) { |
| while (str < limit) { |
| if (*str == goal) { |
| return str; |
| } |
| str++; |
| } |
| return NULL; |
| } |
| |
| RawObject* CompilationTraceLoader::CompileTrace(uint8_t* buffer, |
| intptr_t size) { |
| // First compile functions named in the trace. |
| char* cursor = reinterpret_cast<char*>(buffer); |
| char* limit = cursor + size; |
| while (cursor < limit) { |
| char* uri = cursor; |
| char* comma1 = FindCharacter(uri, ',', limit); |
| if (comma1 == NULL) { |
| break; |
| } |
| *comma1 = 0; |
| char* cls_name = comma1 + 1; |
| char* comma2 = FindCharacter(cls_name, ',', limit); |
| if (comma2 == NULL) { |
| break; |
| } |
| *comma2 = 0; |
| char* func_name = comma2 + 1; |
| char* newline = FindCharacter(func_name, '\n', limit); |
| if (newline == NULL) { |
| break; |
| } |
| *newline = 0; |
| error_ = CompileTriple(uri, cls_name, func_name); |
| if (error_.IsError()) { |
| return error_.raw(); |
| } |
| cursor = newline + 1; |
| } |
| |
| // Next, compile common dispatchers. These aren't found with the normal |
| // lookup above because they have irregular lookup that depends on the |
| // arguments descriptor (e.g. call() versus call(x)). |
| const Class& closure_class = |
| Class::Handle(zone_, thread_->isolate()->object_store()->closure_class()); |
| Array& arguments_descriptor = Array::Handle(zone_); |
| Function& dispatcher = Function::Handle(zone_); |
| for (intptr_t argc = 1; argc <= 4; argc++) { |
| const intptr_t kTypeArgsLen = 0; |
| arguments_descriptor = ArgumentsDescriptor::New(kTypeArgsLen, argc); |
| dispatcher = closure_class.GetInvocationDispatcher( |
| Symbols::Call(), arguments_descriptor, |
| RawFunction::kInvokeFieldDispatcher, true /* create_if_absent */); |
| error_ = CompileFunction(dispatcher); |
| if (error_.IsError()) { |
| return error_.raw(); |
| } |
| } |
| |
| // Finally, compile closures in all compiled functions. Don't cache the |
| // length since compiling may append to this list. |
| const GrowableObjectArray& closure_functions = GrowableObjectArray::Handle( |
| zone_, thread_->isolate()->object_store()->closure_functions()); |
| for (intptr_t i = 0; i < closure_functions.Length(); i++) { |
| function_ ^= closure_functions.At(i); |
| function2_ = function_.parent_function(); |
| if (function2_.HasCode()) { |
| error_ = CompileFunction(function_); |
| if (error_.IsError()) { |
| return error_.raw(); |
| } |
| } |
| } |
| |
| return Object::null(); |
| } |
| |
| // Use a fuzzy match to find the right function to compile. This allows a |
| // compilation trace to remain mostly valid in the face of program changes, and |
| // deals with implicit/dispatcher functions that don't have proper names. |
| // - Ignore private name mangling |
| // - If looking for a getter and we only have the corresponding regular method, |
| // compile the regular method, create its implicit closure and compile that. |
| // - If looking for a regular method and we only have the corresponding getter, |
| // compile the getter, create its method extractor and compile that. |
| // - If looking for a getter and we only have a const field, evaluate the const |
| // field. |
| RawObject* CompilationTraceLoader::CompileTriple(const char* uri_cstr, |
| const char* cls_cstr, |
| const char* func_cstr) { |
| uri_ = Symbols::New(thread_, uri_cstr); |
| class_name_ = Symbols::New(thread_, cls_cstr); |
| function_name_ = Symbols::New(thread_, func_cstr); |
| |
| if (function_name_.Equals("_getMainClosure")) { |
| // The scheme for invoking main relies on compiling _getMainClosure after |
| // synthetically importing the root library. |
| if (FLAG_trace_compilation_trace) { |
| THR_Print("Compilation trace: skip %s,%s,%s\n", uri_.ToCString(), |
| class_name_.ToCString(), function_name_.ToCString()); |
| } |
| return Object::null(); |
| } |
| |
| lib_ = Library::LookupLibrary(thread_, uri_); |
| if (lib_.IsNull()) { |
| // Missing library. |
| if (FLAG_trace_compilation_trace) { |
| THR_Print("Compilation trace: missing library %s,%s,%s\n", |
| uri_.ToCString(), class_name_.ToCString(), |
| function_name_.ToCString()); |
| } |
| return Object::null(); |
| } |
| |
| bool is_getter = Field::IsGetterName(function_name_); |
| bool add_closure = false; |
| bool processed = false; |
| |
| if (class_name_.Equals(Symbols::TopLevel())) { |
| function_ = lib_.LookupFunctionAllowPrivate(function_name_); |
| field_ = lib_.LookupFieldAllowPrivate(function_name_); |
| if (function_.IsNull() && is_getter) { |
| // Maybe this was a tear off. |
| add_closure = true; |
| function_name2_ = Field::NameFromGetter(function_name_); |
| function_ = lib_.LookupFunctionAllowPrivate(function_name2_); |
| field_ = lib_.LookupFieldAllowPrivate(function_name2_); |
| } |
| } else { |
| cls_ = lib_.SlowLookupClassAllowMultiPartPrivate(class_name_); |
| if (cls_.IsNull()) { |
| // Missing class. |
| if (FLAG_trace_compilation_trace) { |
| THR_Print("Compilation trace: missing class %s,%s,%s\n", |
| uri_.ToCString(), class_name_.ToCString(), |
| function_name_.ToCString()); |
| } |
| return Object::null(); |
| } |
| |
| error_ = cls_.EnsureIsFinalized(thread_); |
| if (error_.IsError()) { |
| // Non-finalized class. |
| if (FLAG_trace_compilation_trace) { |
| THR_Print("Compilation trace: non-finalized class %s,%s,%s (%s)\n", |
| uri_.ToCString(), class_name_.ToCString(), |
| function_name_.ToCString(), |
| Error::Cast(error_).ToErrorCString()); |
| } |
| return error_.raw(); |
| } |
| |
| function_ = cls_.LookupFunctionAllowPrivate(function_name_); |
| field_ = cls_.LookupFieldAllowPrivate(function_name_); |
| if (function_.IsNull() && is_getter) { |
| // Maybe this was a tear off. |
| add_closure = true; |
| function_name2_ = Field::NameFromGetter(function_name_); |
| function_ = cls_.LookupFunctionAllowPrivate(function_name2_); |
| field_ = cls_.LookupFieldAllowPrivate(function_name2_); |
| if (!function_.IsNull() && !function_.is_static()) { |
| // Maybe this was a method extractor. |
| function2_ = |
| Resolver::ResolveDynamicAnyArgs(zone_, cls_, function_name_); |
| if (!function2_.IsNull()) { |
| error_ = CompileFunction(function2_); |
| if (error_.IsError()) { |
| if (FLAG_trace_compilation_trace) { |
| THR_Print( |
| "Compilation trace: error compiling extractor %s for " |
| "%s,%s,%s (%s)\n", |
| function2_.ToCString(), uri_.ToCString(), |
| class_name_.ToCString(), function_name_.ToCString(), |
| Error::Cast(error_).ToErrorCString()); |
| } |
| return error_.raw(); |
| } |
| } |
| } |
| } |
| } |
| |
| if (!field_.IsNull() && field_.is_const() && field_.is_static() && |
| (field_.StaticValue() == Object::sentinel().raw())) { |
| processed = true; |
| error_ = EvaluateInitializer(field_); |
| if (error_.IsError()) { |
| if (FLAG_trace_compilation_trace) { |
| THR_Print( |
| "Compilation trace: error initializing field %s for %s,%s,%s " |
| "(%s)\n", |
| field_.ToCString(), uri_.ToCString(), class_name_.ToCString(), |
| function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); |
| } |
| return error_.raw(); |
| } |
| } |
| |
| if (!function_.IsNull()) { |
| processed = true; |
| error_ = CompileFunction(function_); |
| if (error_.IsError()) { |
| if (FLAG_trace_compilation_trace) { |
| THR_Print("Compilation trace: error compiling %s,%s,%s (%s)\n", |
| uri_.ToCString(), class_name_.ToCString(), |
| function_name_.ToCString(), |
| Error::Cast(error_).ToErrorCString()); |
| } |
| return error_.raw(); |
| } |
| if (add_closure) { |
| function_ = function_.ImplicitClosureFunction(); |
| error_ = CompileFunction(function_); |
| if (error_.IsError()) { |
| if (FLAG_trace_compilation_trace) { |
| THR_Print( |
| "Compilation trace: error compiling closure %s,%s,%s (%s)\n", |
| uri_.ToCString(), class_name_.ToCString(), |
| function_name_.ToCString(), Error::Cast(error_).ToErrorCString()); |
| } |
| return error_.raw(); |
| } |
| } |
| } |
| |
| if (FLAG_trace_compilation_trace) { |
| if (!processed) { |
| THR_Print("Compilation trace: ignored %s,%s,%s\n", uri_.ToCString(), |
| class_name_.ToCString(), function_name_.ToCString()); |
| } |
| } |
| return Object::null(); |
| } |
| |
| RawObject* CompilationTraceLoader::CompileFunction(const Function& function) { |
| if (function.is_abstract()) { |
| return Object::null(); |
| } |
| return Compiler::CompileFunction(thread_, function); |
| } |
| |
| RawObject* CompilationTraceLoader::EvaluateInitializer(const Field& field) { |
| LongJumpScope jump; |
| if (setjmp(*jump.Set()) == 0) { |
| field_.EvaluateInitializer(); |
| } else { |
| Thread* thread = Thread::Current(); |
| const Error& error = Error::Handle(thread->sticky_error()); |
| thread->clear_sticky_error(); |
| return error.raw(); |
| } |
| return Object::null(); |
| } |
| |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| } // namespace dart |