| // Copyright (c) 2015, 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/precompiler.h" |
| |
| #include "vm/compiler.h" |
| #include "vm/isolate.h" |
| #include "vm/longjump.h" |
| #include "vm/object.h" |
| #include "vm/object_store.h" |
| #include "vm/resolver.h" |
| #include "vm/symbols.h" |
| |
| namespace dart { |
| |
| |
| #define I (isolate()) |
| #define Z (zone()) |
| |
| |
| DEFINE_FLAG(bool, trace_precompiler, false, "Trace precompiler."); |
| |
| |
| static void Jump(const Error& error) { |
| Isolate::Current()->long_jump_base()->Jump(1, error); |
| } |
| |
| |
| RawError* Precompiler::CompileAll() { |
| LongJumpScope jump; |
| if (setjmp(*jump.Set()) == 0) { |
| Precompiler precompiler(Thread::Current()); |
| precompiler.DoCompileAll(); |
| return Error::null(); |
| } else { |
| Isolate* isolate = Isolate::Current(); |
| const Error& error = Error::Handle(isolate->object_store()->sticky_error()); |
| isolate->object_store()->clear_sticky_error(); |
| return error.raw(); |
| } |
| } |
| |
| |
| Precompiler::Precompiler(Thread* thread) : |
| thread_(thread), |
| zone_(thread->zone()), |
| isolate_(thread->isolate()), |
| changed_(false), |
| function_count_(0), |
| class_count_(0), |
| libraries_(GrowableObjectArray::Handle(Z, I->object_store()->libraries())), |
| pending_functions_(GrowableObjectArray::Handle(Z, |
| GrowableObjectArray::New())), |
| collected_closures_(GrowableObjectArray::Handle(Z, I->collected_closures())), |
| sent_selectors_(GrowableObjectArray::Handle(Z, GrowableObjectArray::New())), |
| error_(Error::Handle(Z)) { |
| } |
| |
| |
| void Precompiler::DoCompileAll() { |
| // Drop all existing code so we can use the presence of code as an indicator |
| // that we have already looked for the function's callees. |
| ClearAllCode(); |
| |
| // Start with the allocations and invocations that happen from C++. |
| AddRoots(); |
| |
| // TODO(rmacnak): Eagerly add field-invocation functions to all signature |
| // classes so closure calls don't go through the runtime. |
| |
| // Compile newly found targets and add their callees until we reach a fixed |
| // point. |
| Iterate(); |
| |
| CleanUp(); |
| |
| if (FLAG_trace_precompiler) { |
| OS::Print("Precompiled %" Pd " functions, %" Pd " dynamic types," |
| " %" Pd " dynamic selectors\n", |
| function_count_, |
| class_count_, |
| sent_selectors_.Length()); |
| } |
| |
| I->set_compilation_allowed(false); |
| } |
| |
| |
| void Precompiler::ClearAllCode() { |
| Library& lib = Library::Handle(Z); |
| Class& cls = Class::Handle(Z); |
| Array& functions = Array::Handle(Z); |
| Function& function = Function::Handle(Z); |
| |
| for (intptr_t i = 0; i < libraries_.Length(); i++) { |
| lib ^= libraries_.At(i); |
| ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| while (it.HasNext()) { |
| cls = it.GetNextClass(); |
| error_ = cls.EnsureIsFinalized(I); |
| if (!error_.IsNull()) { |
| Jump(error_); |
| } |
| } |
| } |
| |
| for (intptr_t i = 0; i < libraries_.Length(); i++) { |
| lib ^= libraries_.At(i); |
| ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| while (it.HasNext()) { |
| cls = it.GetNextClass(); |
| functions = cls.functions(); |
| for (intptr_t i = 0; i < functions.Length(); i++) { |
| function ^= functions.At(i); |
| function.ClearCode(); |
| } |
| } |
| } |
| } |
| |
| |
| void Precompiler::AddRoots() { |
| // Note that <rootlibrary>.main is not a root. The appropriate main will be |
| // discovered through _getMainClosure. |
| |
| AddSelector(Symbols::NoSuchMethod()); |
| |
| AddSelector(Symbols::Call()); // For speed, not correctness. |
| |
| // Allocated from C++. |
| static const intptr_t kExternallyAllocatedCids[] = { |
| kBoolCid, |
| kNullCid, |
| |
| kSmiCid, |
| kMintCid, |
| kBigintCid, |
| kDoubleCid, |
| |
| kOneByteStringCid, |
| kTwoByteStringCid, |
| kExternalOneByteStringCid, |
| kExternalTwoByteStringCid, |
| |
| kArrayCid, |
| kImmutableArrayCid, |
| kGrowableObjectArrayCid, |
| kLinkedHashMapCid, |
| |
| kTypedDataUint8ClampedArrayCid, |
| kTypedDataUint8ArrayCid, |
| kTypedDataUint16ArrayCid, |
| kTypedDataUint32ArrayCid, |
| kTypedDataUint64ArrayCid, |
| |
| kTypedDataInt8ArrayCid, |
| kTypedDataInt16ArrayCid, |
| kTypedDataInt32ArrayCid, |
| kTypedDataInt64ArrayCid, |
| |
| kExternalTypedDataUint8ArrayCid, |
| |
| kTypedDataFloat32ArrayCid, |
| kTypedDataFloat64ArrayCid, |
| |
| kTypedDataFloat32x4ArrayCid, |
| kTypedDataInt32x4ArrayCid, |
| kTypedDataFloat64x2ArrayCid, |
| |
| kInt32x4Cid, |
| kFloat32x4Cid, |
| kFloat64x2Cid, |
| |
| kTypeCid, |
| kTypeRefCid, |
| kTypeParameterCid, |
| kBoundedTypeCid, |
| kLibraryPrefixCid, |
| |
| kJSRegExpCid, |
| kUserTagCid, |
| kStacktraceCid, |
| kWeakPropertyCid, |
| kCapabilityCid, |
| ReceivePort::kClassId, |
| SendPort::kClassId, |
| |
| kIllegalCid |
| }; |
| |
| Class& cls = Class::Handle(Z); |
| for (intptr_t i = 0; kExternallyAllocatedCids[i] != kIllegalCid; i++) { |
| cls = isolate()->class_table()->At(kExternallyAllocatedCids[i]); |
| AddClass(cls); |
| } |
| |
| static const struct { |
| const char* library_; |
| const char* class_; |
| const char* function_; |
| } kExternallyCalled[] = { |
| { "dart:_builtin", "::", "_getMainClosure" }, |
| { "dart:_builtin", "::", "_getPrintClosure" }, |
| { "dart:_builtin", "::", "_getUriBaseClosure" }, |
| { "dart:_builtin", "::", "_resolveUri" }, |
| { "dart:_builtin", "::", "_setWorkingDirectory" }, |
| { "dart:async", "::", "_setScheduleImmediateClosure" }, |
| { "dart:core", "_InternalError", "_InternalError." }, |
| { "dart:core", "_InvocationMirror", "_allocateInvocationMirror" }, |
| { "dart:io", "::", "_makeUint8ListView" }, |
| { "dart:io", "::", "_makeDatagram" }, |
| { "dart:io", "CertificateException", "CertificateException." }, |
| { "dart:io", "HandshakeException", "HandshakeException." }, |
| { "dart:io", "TlsException", "TlsException." }, |
| { "dart:io", "X509Certificate", "X509Certificate." }, |
| { "dart:io", "_ExternalBuffer", "set:data" }, |
| { "dart:io", "_Platform", "set:_nativeScript" }, |
| { "dart:io", "_ProcessStartStatus", "set:_errorCode" }, |
| { "dart:io", "_ProcessStartStatus", "set:_errorMessage" }, |
| { "dart:io", "_SecureFilterImpl", "get:ENCRYPTED_SIZE" }, |
| { "dart:io", "_SecureFilterImpl", "get:SIZE" }, |
| { "dart:isolate", "::", "_getIsolateScheduleImmediateClosure" }, |
| { "dart:isolate", "::", "_startMainIsolate" }, |
| { "dart:isolate", "_RawReceivePortImpl", "_handleMessage" }, |
| { "dart:isolate", "_RawReceivePortImpl", "_lookupHandler" }, |
| { "dart:vmservice", "::", "_registerIsolate" }, |
| { "dart:vmservice", "::", "boot" }, |
| { "dart:vmservice_io", "::", "_addResource" }, |
| { "dart:vmservice_io", "::", "main" }, |
| |
| // Cf. Exceptions::Create |
| { "dart:core", "RangeError", "RangeError." }, |
| { "dart:core", "RangeError", "RangeError.range" }, |
| { "dart:core", "ArgumentError", "ArgumentError." }, |
| { "dart:core", "NoSuchMethodError", "NoSuchMethodError._withType" }, |
| { "dart:core", "FormatException", "FormatException." }, |
| { "dart:core", "UnsupportedError", "UnsupportedError." }, |
| { "dart:core", "NullThrownError", "NullThrownError." }, |
| { "dart:isolate", "IsolateSpawnException", "IsolateSpawnException." }, |
| { "dart:isolate", "_IsolateUnhandledException", |
| "_IsolateUnhandledException." }, |
| { "dart:core", "_JavascriptIntegerOverflowError", |
| "_JavascriptIntegerOverflowError." }, |
| { "dart:core", "_JavascriptCompatibilityError", |
| "_JavascriptCompatibilityError." }, |
| { "dart:core", "AssertionError", "AssertionError." }, |
| { "dart:core", "_CastError", "_CastError._create" }, |
| { "dart:core", "_TypeError", "_TypeError._create" }, |
| { "dart:core", "FallThroughError", "FallThroughError._create" }, |
| { "dart:core", "AbstractClassInstantiationError", |
| "AbstractClassInstantiationError._create" }, |
| { "dart:core", "CyclicInitializationError", |
| "CyclicInitializationError." }, |
| { "dart:core", "StackOverflowError", "StackOverflowError." }, |
| { "dart:core", "OutOfMemoryError", "OutOfMemoryError." }, |
| { NULL, NULL, NULL } |
| }; |
| |
| Library& lib = Library::Handle(Z); |
| Function& func = Function::Handle(Z); |
| String& library_name = String::Handle(Z); |
| String& class_name = String::Handle(Z); |
| String& function_name = String::Handle(Z); |
| for (intptr_t i = 0; kExternallyCalled[i].library_ != NULL; i++) { |
| library_name = Symbols::New(kExternallyCalled[i].library_); |
| class_name = Symbols::New(kExternallyCalled[i].class_); |
| function_name = Symbols::New(kExternallyCalled[i].function_); |
| |
| lib = Library::LookupLibrary(library_name); |
| if (lib.IsNull()) { |
| if (FLAG_trace_precompiler) { |
| OS::Print("WARNING: Missing %s\n", kExternallyCalled[i].library_); |
| } |
| continue; |
| } |
| |
| if (class_name.raw() == Symbols::TopLevel().raw()) { |
| func = lib.LookupFunctionAllowPrivate(function_name); |
| } else { |
| cls = lib.LookupClassAllowPrivate(class_name); |
| if (cls.IsNull()) { |
| if (FLAG_trace_precompiler) { |
| OS::Print("WARNING: Missing %s %s\n", |
| kExternallyCalled[i].library_, |
| kExternallyCalled[i].class_); |
| } |
| continue; |
| } |
| |
| ASSERT(!cls.IsNull()); |
| func = cls.LookupFunctionAllowPrivate(function_name); |
| } |
| |
| if (func.IsNull()) { |
| if (FLAG_trace_precompiler) { |
| OS::Print("WARNING: Missing %s %s %s\n", |
| kExternallyCalled[i].library_, |
| kExternallyCalled[i].class_, |
| kExternallyCalled[i].function_); |
| } |
| continue; |
| } |
| |
| AddFunction(func); |
| } |
| } |
| |
| |
| void Precompiler::Iterate() { |
| Function& function = Function::Handle(Z); |
| |
| while (changed_) { |
| changed_ = false; |
| |
| while (pending_functions_.Length() > 0) { |
| function ^= pending_functions_.RemoveLast(); |
| ProcessFunction(function); |
| } |
| |
| CheckForNewDynamicFunctions(); |
| |
| // Drain collected_closures last because additions to this list come from |
| // outside the Precompiler and so do not flip our changed_ flag. |
| while (collected_closures_.Length() > 0) { |
| function ^= collected_closures_.RemoveLast(); |
| ProcessFunction(function); |
| } |
| } |
| } |
| |
| |
| void Precompiler::CleanUp() { |
| I->set_collected_closures(GrowableObjectArray::Handle(Z)); |
| |
| // TODO(rmacnak): Drop functions without code, classes without functions, etc. |
| } |
| |
| |
| void Precompiler::ProcessFunction(const Function& function) { |
| if (!function.HasCode()) { |
| function_count_++; |
| |
| if (FLAG_trace_precompiler) { |
| OS::Print("Precompiling %" Pd " %s (%" Pd ", %s)\n", |
| function_count_, |
| function.ToLibNamePrefixedQualifiedCString(), |
| function.token_pos(), |
| Function::KindToCString(function.kind())); |
| } |
| |
| ASSERT(!function.is_abstract()); |
| ASSERT(!function.IsRedirectingFactory()); |
| |
| error_ = Compiler::CompileFunction(thread_, function); |
| if (!error_.IsNull()) { |
| Jump(error_); |
| } |
| } |
| |
| ASSERT(function.HasCode()); |
| AddCalleesOf(function); |
| } |
| |
| |
| void Precompiler::AddCalleesOf(const Function& function) { |
| ASSERT(function.HasCode()); |
| |
| const Code& code = Code::Handle(Z, function.CurrentCode()); |
| |
| const Array& table = Array::Handle(Z, code.static_calls_target_table()); |
| Object& entry = Object::Handle(Z); |
| Function& target = Function::Handle(Z); |
| for (intptr_t i = 0; i < table.Length(); i++) { |
| entry = table.At(i); |
| if (entry.IsFunction()) { |
| target ^= table.At(i); |
| AddFunction(target); |
| } |
| } |
| |
| #if defined(TARGET_ARCH_IA32) |
| FATAL("Callee scanning unimplemented for IA32"); |
| #endif |
| |
| const ObjectPool& pool = ObjectPool::Handle(Z, code.GetObjectPool()); |
| ICData& call_site = ICData::Handle(Z); |
| String& selector = String::Handle(Z); |
| Field& field = Field::Handle(Z); |
| Class& cls = Class::Handle(Z); |
| for (intptr_t i = 0; i < pool.Length(); i++) { |
| if (pool.InfoAt(i) == ObjectPool::kTaggedObject) { |
| entry = pool.ObjectAt(i); |
| if (entry.IsICData()) { |
| call_site ^= entry.raw(); |
| if (call_site.NumberOfChecks() == 1) { |
| // Probably a static call. |
| target = call_site.GetTargetAt(0); |
| AddFunction(target); |
| if (!target.is_static()) { |
| // Super call (should not enqueue selector) or dynamic call with a |
| // CHA prediction (should enqueue selector). |
| selector = call_site.target_name(); |
| AddSelector(selector); |
| } |
| } else { |
| // A dynamic call. |
| selector = call_site.target_name(); |
| AddSelector(selector); |
| } |
| } else if (entry.IsField()) { |
| // Potential need for field initializer. |
| field ^= entry.raw(); |
| AddField(field); |
| } else if (entry.IsInstance()) { |
| // Potential const object. |
| cls = entry.clazz(); |
| AddClass(cls); |
| } |
| } |
| } |
| } |
| |
| |
| void Precompiler::AddField(const Field& field) { |
| if (field.is_static()) { |
| // Potential const object. Uninitialized field will harmlessly do a |
| // redundant add of the Null class. |
| const Object& value = Object::Handle(Z, field.value()); |
| const Class& cls = Class::Handle(Z, value.clazz()); |
| AddClass(cls); |
| |
| if (field.has_initializer()) { |
| if (field.initializer() != Function::null()) return; |
| |
| if (FLAG_trace_precompiler) { |
| OS::Print("Precompiling initializer for %s\n", field.ToCString()); |
| } |
| Compiler::CompileStaticInitializer(field); |
| |
| const Function& function = Function::Handle(Z, field.initializer()); |
| AddCalleesOf(function); |
| } |
| } |
| } |
| |
| |
| void Precompiler::AddFunction(const Function& function) { |
| if (function.HasCode()) return; |
| |
| pending_functions_.Add(function); |
| changed_ = true; |
| } |
| |
| |
| bool Precompiler::IsSent(const String& selector) { |
| ASSERT(selector.IsSymbol()); |
| |
| // TODO(rmacnak): Use a proper set. |
| for (intptr_t i = 0; i < sent_selectors_.Length(); i++) { |
| if (sent_selectors_.At(i) == selector.raw()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| void Precompiler::AddSelector(const String& selector) { |
| if (!IsSent(selector)) { |
| if (FLAG_trace_precompiler) { |
| OS::Print("Enqueueing selector %" Pd " %s\n", |
| sent_selectors_.Length(), |
| selector.ToCString()); |
| } |
| |
| sent_selectors_.Add(selector); |
| changed_ = true; |
| |
| if (!Field::IsGetterName(selector) && |
| !Field::IsSetterName(selector)) { |
| // Regular method may be call-through-getter. |
| // TODO(rmacnak): Do not create the symbol if it does not already exist. |
| String& getter = String::Handle(Field::GetterName(selector)); |
| getter = Symbols::New(getter); |
| AddSelector(getter); |
| } |
| } |
| } |
| |
| |
| void Precompiler::AddClass(const Class& cls) { |
| if (cls.is_allocated()) return; |
| |
| class_count_++; |
| cls.set_is_allocated(); |
| changed_ = true; |
| |
| if (FLAG_trace_precompiler) { |
| OS::Print("Allocation %" Pd " %s\n", class_count_, cls.ToCString()); |
| } |
| |
| const Class& superclass = Class::Handle(cls.SuperClass()); |
| if (!superclass.IsNull()) { |
| AddClass(superclass); |
| } |
| } |
| |
| |
| void Precompiler::CheckForNewDynamicFunctions() { |
| Library& lib = Library::Handle(Z); |
| Class& cls = Class::Handle(Z); |
| Array& functions = Array::Handle(Z); |
| Function& function = Function::Handle(Z); |
| String& selector = String::Handle(Z); |
| |
| for (intptr_t i = 0; i < libraries_.Length(); i++) { |
| lib ^= libraries_.At(i); |
| ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); |
| while (it.HasNext()) { |
| cls = it.GetNextClass(); |
| |
| if (!cls.is_allocated()) { |
| bool has_compiled_constructor = false; |
| if (cls.allocation_stub() != Code::null()) { |
| // Regular objects. |
| has_compiled_constructor = true; |
| } else if (cls.is_synthesized_class()) { |
| // Enums. |
| has_compiled_constructor = true; |
| } else { |
| // Objects only allocated via const constructors, and not stored in a |
| // static field or code. |
| // E.g. A in |
| // class A { |
| // const A(); |
| // toString() => "Don't drop me!"; |
| // } |
| // class B { |
| // const a = const A(); |
| // const B(); |
| // static const theB = const B(); |
| // } |
| // main() => print(B.theB.a); |
| functions = cls.functions(); |
| for (intptr_t k = 0; k < functions.Length(); k++) { |
| function ^= functions.At(k); |
| if (function.IsGenerativeConstructor() && |
| function.HasCode()) { |
| has_compiled_constructor = true; |
| break; |
| } |
| } |
| } |
| if (!has_compiled_constructor) { |
| continue; |
| } |
| AddClass(cls); |
| } |
| |
| functions = cls.functions(); |
| for (intptr_t k = 0; k < functions.Length(); k++) { |
| function ^= functions.At(k); |
| |
| if (function.is_static() || function.is_abstract()) continue; |
| |
| // Don't bail out early if there is already code because we may discover |
| // the corresponding getter selector is sent in some later iteration. |
| // if (function.HasCode()) continue; |
| |
| selector = function.name(); |
| if (IsSent(selector)) { |
| AddFunction(function); |
| } |
| |
| if (function.kind() == RawFunction::kRegularFunction && |
| !Field::IsGetterName(selector) && |
| !Field::IsSetterName(selector)) { |
| // TODO(rmacnak): Do not create the symbol if it does not already |
| // exist. |
| selector = Field::GetterName(selector); |
| selector = Symbols::New(selector); |
| if (IsSent(selector)) { |
| function = function.ImplicitClosureFunction(); |
| AddFunction(function); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| } // namespace dart |