blob: 52287f3abc2b0e86dcfca35cdedd9115052c1164 [file] [log] [blame]
// 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