Reduce AOT initialization.
When compiling apps there is no need to resolve all types in the dex file, just
those declared in the dex file. There's also no need to initialize static
fields if we can only leave the class in a verified state.
Increase use of CompilerDriver::IsImage.
Move timing of dex2oat setup to before Runtime::Create.
On run-test 056 the performance improvement is an order of magnitude, for
ThinkFree dex2oat time is dominated by compilation and this change has no
effect.
Bug 10316099.
Change-Id: Ibdd7caa43284e7448e6a56d810967100ae4a7898
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 120f232..38ffa10 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -582,10 +582,11 @@
bool CompilerDriver::IsImageClass(const char* descriptor) const {
DCHECK(descriptor != NULL);
- if (image_classes_.get() == NULL) {
+ if (!IsImage()) {
return true;
+ } else {
+ return image_classes_->find(descriptor) != image_classes_->end();
}
- return image_classes_->find(descriptor) != image_classes_->end();
}
static void ResolveExceptionsForMethod(MethodHelper* mh,
@@ -655,7 +656,7 @@
// Make a list of descriptors for classes to include in the image
void CompilerDriver::LoadImageClasses(base::TimingLogger& timings)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
- if (image_classes_.get() == NULL) {
+ if (!IsImage()) {
return;
}
@@ -669,7 +670,7 @@
SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(descriptor.c_str()));
if (klass.get() == NULL) {
image_classes_->erase(it++);
- LOG(WARNING) << "Failed to find class " << descriptor;
+ VLOG(compiler) << "Failed to find class " << descriptor;
Thread::Current()->ClearException();
} else {
++it;
@@ -742,21 +743,19 @@
}
void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) {
- if (image_classes_.get() == NULL) {
- return;
+ if (IsImage()) {
+ timings.NewSplit("UpdateImageClasses");
+
+ // Update image_classes_ with classes for objects created by <clinit> methods.
+ Thread* self = Thread::Current();
+ const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ // TODO: Image spaces only?
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ heap->FlushAllocStack();
+ heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this);
+ self->EndAssertNoThreadSuspension(old_cause);
}
-
- timings.NewSplit("UpdateImageClasses");
-
- // Update image_classes_ with classes for objects created by <clinit> methods.
- Thread* self = Thread::Current();
- const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
- gc::Heap* heap = Runtime::Current()->GetHeap();
- // TODO: Image spaces only?
- WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
- heap->FlushAllocStack();
- heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this);
- self->EndAssertNoThreadSuspension(old_cause);
}
void CompilerDriver::RecordClassStatus(ClassReference ref, CompiledClass* compiled_class) {
@@ -1436,12 +1435,18 @@
return true;
}
-static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager, size_t class_def_index)
+static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager,
+ size_t class_def_index)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
ScopedObjectAccess soa(Thread::Current());
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
const DexFile& dex_file = *manager->GetDexFile();
+ // If an instance field is final then we need to have a barrier on the return, static final
+ // fields are assigned within the lock held for class initialization. Conservatively assume
+ // constructor barriers are always required.
+ bool requires_constructor_barrier = true;
+
// Method and Field are the worst. We can't resolve without either
// context from the code use (to disambiguate virtual vs direct
// method and instance vs static field) or from class
@@ -1450,72 +1455,89 @@
// definitions, since many of them many never be referenced by
// generated code.
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- if (SkipClass(class_loader, dex_file, class_def)) {
- return;
- }
+ if (!SkipClass(class_loader, dex_file, class_def)) {
+ // Note the class_data pointer advances through the headers,
+ // static fields, instance fields, direct methods, and virtual
+ // methods.
+ const byte* class_data = dex_file.GetClassData(class_def);
+ if (class_data == NULL) {
+ // Empty class such as a marker interface.
+ requires_constructor_barrier = false;
+ } else {
+ ClassLinker* class_linker = manager->GetClassLinker();
+ mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file);
- // Note the class_data pointer advances through the headers,
- // static fields, instance fields, direct methods, and virtual
- // methods.
- const byte* class_data = dex_file.GetClassData(class_def);
- if (class_data == NULL) {
- // empty class such as a marker interface
- return;
- }
- Thread* self = Thread::Current();
- ClassLinker* class_linker = manager->GetClassLinker();
- mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file);
- ClassDataItemIterator it(dex_file, class_data);
- while (it.HasNextStaticField()) {
- mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
- class_loader, true);
- if (field == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
- }
- it.Next();
- }
- // If an instance field is final then we need to have a barrier on the return, static final
- // fields are assigned within the lock held for class initialization.
- bool requires_constructor_barrier = false;
- while (it.HasNextInstanceField()) {
- if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
- requires_constructor_barrier = true;
- }
+ // Resolve the class.
+ mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
+ class_loader);
- mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
- class_loader, false);
- if (field == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
+ bool resolve_fields_and_methods;
+ if (klass == NULL) {
+ // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
+ // attempt to resolve methods and fields when there is no declaring class.
+ CHECK(soa.Self()->IsExceptionPending());
+ Thread::Current()->ClearException();
+ resolve_fields_and_methods = false;
+ } else {
+ resolve_fields_and_methods = manager->GetCompiler()->IsImage();
+ }
+ ClassDataItemIterator it(dex_file, class_data);
+ while (it.HasNextStaticField()) {
+ if (resolve_fields_and_methods) {
+ mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, true);
+ if (field == NULL) {
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ }
+ }
+ it.Next();
+ }
+ // We require a constructor barrier if there are final instance fields.
+ requires_constructor_barrier = false;
+ while (it.HasNextInstanceField()) {
+ if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
+ requires_constructor_barrier = true;
+ }
+ if (resolve_fields_and_methods) {
+ mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, false);
+ if (field == NULL) {
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ }
+ }
+ it.Next();
+ }
+ if (resolve_fields_and_methods) {
+ while (it.HasNextDirectMethod()) {
+ mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, NULL,
+ it.GetMethodInvokeType(class_def));
+ if (method == NULL) {
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ }
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, NULL,
+ it.GetMethodInvokeType(class_def));
+ if (method == NULL) {
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ }
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
}
- it.Next();
}
if (requires_constructor_barrier) {
manager->GetCompiler()->AddRequiresConstructorBarrier(soa.Self(), manager->GetDexFile(),
class_def_index);
}
- while (it.HasNextDirectMethod()) {
- mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, NULL,
- it.GetMethodInvokeType(class_def));
- if (method == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
- }
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, NULL,
- it.GetMethodInvokeType(class_def));
- if (method == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
- }
- it.Next();
- }
- DCHECK(!it.HasNext());
}
static void ResolveType(const ParallelCompilationManager* manager, size_t type_idx)
@@ -1541,10 +1563,16 @@
// TODO: we could resolve strings here, although the string table is largely filled with class
// and method names.
- timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str()));
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
- context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
+ if (IsImage()) {
+ // For images we resolve all types, such as array, whereas for applications just those with
+ // classdefs are resolved by ResolveClassFieldsAndMethods.
+ // TODO: strdup memory leak.
+ timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str()));
+ context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
+ }
+ // TODO: strdup memory leak.
timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " MethodsAndFields").c_str()));
context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_);
}
@@ -1567,7 +1595,8 @@
mirror::Class* klass =
manager->GetClassLinker()->FindClass(descriptor,
soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()));
- if (klass == NULL) { CHECK(soa.Self()->IsExceptionPending());
+ if (klass == NULL) {
+ CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
/*
@@ -1587,25 +1616,25 @@
<< PrettyDescriptor(manager->GetDexFile()->GetClassDescriptor(class_def))
<< " because: " << error_msg;
}
- return;
+ } else {
+ CHECK(klass->IsResolved()) << PrettyClass(klass);
+ manager->GetClassLinker()->VerifyClass(klass);
+
+ if (klass->IsErroneous()) {
+ // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ }
+
+ CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
+ << PrettyDescriptor(klass) << ": state=" << klass->GetStatus();
}
- CHECK(klass->IsResolved()) << PrettyClass(klass);
- manager->GetClassLinker()->VerifyClass(klass);
-
- if (klass->IsErroneous()) {
- // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
- CHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
- }
-
-
- CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
- << PrettyDescriptor(klass) << ": state=" << klass->GetStatus();
soa.Self()->AssertNoPendingException();
}
void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
+ // TODO: strdup memory leak.
timings.NewSplit(strdup(("Verify " + dex_file.GetLocation()).c_str()));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
@@ -2032,23 +2061,20 @@
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def);
mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader);
- bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1;
- bool can_init_static_fields = compiling_boot &&
- manager->GetCompiler()->IsImageClass(descriptor);
if (klass != NULL) {
- // We don't want class initialization occurring on multiple threads due to deadlock problems.
- // For example, a parent class is initialized (holding its lock) that refers to a sub-class
- // in its static/class initializer causing it to try to acquire the sub-class' lock. While
- // on a second thread the sub-class is initialized (holding its lock) after first initializing
- // its parents, whose locks are acquired. This leads to a parent-to-child and a child-to-parent
- // lock ordering and consequent potential deadlock.
- // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather
- // than use a special Object for the purpose we use the Class of java.lang.Class.
- ObjectLock lock1(soa.Self(), klass->GetClass());
- // The lock required to initialize the class.
- ObjectLock lock2(soa.Self(), klass);
// Only try to initialize classes that were successfully verified.
if (klass->IsVerified()) {
+ // We don't want class initialization occurring on multiple threads due to deadlock problems.
+ // For example, a parent class is initialized (holding its lock) that refers to a sub-class
+ // in its static/class initializer causing it to try to acquire the sub-class' lock. While
+ // on a second thread the sub-class is initialized (holding its lock) after first initializing
+ // its parents, whose locks are acquired. This leads to a parent-to-child and a child-to-parent
+ // lock ordering and consequent potential deadlock.
+ // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather
+ // than use a special Object for the purpose we use the Class of java.lang.Class.
+ ObjectLock lock(soa.Self(), klass->GetClass());
+ bool can_init_static_fields = manager->GetCompiler()->IsImage() &&
+ manager->GetCompiler()->IsImageClass(descriptor);
manager->GetClassLinker()->EnsureInitialized(klass, false, can_init_static_fields);
if (soa.Self()->IsExceptionPending()) {
soa.Self()->GetException(NULL)->Dump();
@@ -2069,6 +2095,7 @@
VLOG(compiler) << "Initializing: " << descriptor;
if (StringPiece(descriptor) == "Ljava/lang/Void;") {
// Hand initialize j.l.Void to avoid Dex file operations in un-started runtime.
+ ObjectLock lock(soa.Self(), klass);
mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields();
CHECK_EQ(fields->GetLength(), 1);
fields->Get(0)->SetObj(klass, manager->GetClassLinker()->FindPrimitiveClass('V'));
@@ -2102,11 +2129,15 @@
void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
+ // TODO: strdup memory leak.
timings.NewSplit(strdup(("InitializeNoClinit " + dex_file.GetLocation()).c_str()));
#ifndef NDEBUG
- for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
- const char* descriptor = class_initializer_black_list[i];
- CHECK(IsValidDescriptor(descriptor)) << descriptor;
+ // Sanity check blacklist descriptors.
+ if (IsImage()) {
+ for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
+ const char* descriptor = class_initializer_black_list[i];
+ CHECK(IsValidDescriptor(descriptor)) << descriptor;
+ }
}
#endif
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 21a44ea..bcde178 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -110,6 +110,7 @@
return compiler_backend_;
}
+ // Are we compiling and creating an image file?
bool IsImage() const {
return image_;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index ceb6bf6..89552a3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -825,6 +825,7 @@
return EXIT_FAILURE;
}
+ timings.StartSplit("dex2oat Setup");
LOG(INFO) << "dex2oat: " << oat_location;
Runtime::Options options;
@@ -926,7 +927,6 @@
}
}
- timings.StartSplit("dex2oat Setup");
UniquePtr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
host_prefix.get(),
android_root,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d0b8bbd..b4c767d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2311,8 +2311,9 @@
mirror::Class::Status oat_file_class_status(mirror::Class::kStatusNotReady);
bool preverified = VerifyClassUsingOatFile(dex_file, klass, oat_file_class_status);
if (oat_file_class_status == mirror::Class::kStatusError) {
- LOG(WARNING) << "Skipping runtime verification of erroneous class " << PrettyDescriptor(klass)
- << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+ VLOG(class_linker) << "Skipping runtime verification of erroneous class "
+ << PrettyDescriptor(klass) << " in "
+ << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
ThrowVerifyError(klass, "Rejecting class %s because it failed compile-time verification",
PrettyDescriptor(klass).c_str());
klass->SetStatus(mirror::Class::kStatusError);
@@ -2326,7 +2327,7 @@
}
if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) {
if (!preverified && verifier_failure != verifier::MethodVerifier::kNoFailure) {
- LOG(WARNING) << "Soft verification failure in class " << PrettyDescriptor(klass)
+ VLOG(class_linker) << "Soft verification failure in class " << PrettyDescriptor(klass)
<< " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
<< " because: " << error_msg;
}
@@ -2722,14 +2723,6 @@
}
clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
- if (clinit != NULL && !can_run_clinit) {
- // if the class has a <clinit> but we can't run it during compilation,
- // don't bother going to kStatusInitializing. We return false so that
- // sub-classes don't believe this class is initialized.
- // Opportunistically link non-static methods, TODO: don't initialize and dirty pages
- // in second pass.
- return false;
- }
// If the class is kStatusInitializing, either this thread is
// initializing higher up the stack or another thread has beat us
@@ -2775,9 +2768,9 @@
return false;
}
- bool has_static_field_initializers = InitializeStaticFields(klass);
+ bool has_static_field_initializers = InitializeStaticFields(klass, can_init_statics);
- if (clinit != NULL) {
+ if (clinit != NULL && can_run_clinit) {
if (Runtime::Current()->IsStarted()) {
JValue result;
clinit->Invoke(self, NULL, 0, &result, 'V');
@@ -2786,7 +2779,11 @@
}
}
- FixupStaticTrampolines(klass);
+ // Opportunistically set static method trampolines to their destiniation. Unless initialization
+ // is being hindered at compile time.
+ if (can_init_statics || can_run_clinit || (!has_static_field_initializers && clinit == NULL)) {
+ FixupStaticTrampolines(klass);
+ }
uint64_t t1 = NanoTime();
@@ -2805,15 +2802,15 @@
++thread_stats->class_init_count;
global_stats->class_init_time_ns += (t1 - t0);
thread_stats->class_init_time_ns += (t1 - t0);
- // Set the class as initialized except if we can't initialize static fields and static field
- // initialization is necessary.
- if (!can_init_statics && has_static_field_initializers) {
- klass->SetStatus(mirror::Class::kStatusVerified); // Don't leave class in initializing state.
+ // Set the class as initialized except if failed to initialize static fields.
+ if ((!can_init_statics && has_static_field_initializers) ||
+ (!can_run_clinit && clinit != NULL)) {
+ klass->SetStatus(mirror::Class::kStatusVerified);
success = false;
} else {
klass->SetStatus(mirror::Class::kStatusInitialized);
}
- if (VLOG_IS_ON(class_linker)) {
+ if (success && VLOG_IS_ON(class_linker)) {
ClassHelper kh(klass);
LOG(INFO) << "Initialized class " << kh.GetDescriptor() << " from " << kh.GetLocation();
}
@@ -2986,10 +2983,9 @@
return true;
}
- Thread* self = Thread::Current();
- ScopedThreadStateChange tsc(self, kRunnable);
bool success = InitializeClass(c, can_run_clinit, can_init_fields);
if (!success) {
+ Thread* self = Thread::Current();
CHECK(self->IsExceptionPending() || !can_run_clinit) << PrettyClass(c);
}
return success;
@@ -3005,7 +3001,7 @@
}
}
-bool ClassLinker::InitializeStaticFields(mirror::Class* klass) {
+bool ClassLinker::InitializeStaticFields(mirror::Class* klass, bool can_init_statics) {
size_t num_static_fields = klass->NumStaticFields();
if (num_static_fields == 0) {
return false;
@@ -3022,16 +3018,19 @@
EncodedStaticFieldValueIterator it(dex_file, dex_cache, klass->GetClassLoader(),
this, *dex_class_def);
- if (it.HasNext()) {
- // We reordered the fields, so we need to be able to map the field indexes to the right fields.
- SafeMap<uint32_t, mirror::ArtField*> field_map;
- ConstructFieldMap(dex_file, *dex_class_def, klass, field_map);
- for (size_t i = 0; it.HasNext(); i++, it.Next()) {
- it.ReadValueToField(field_map.Get(i));
+ if (!it.HasNext()) {
+ return false;
+ } else {
+ if (can_init_statics) {
+ // We reordered the fields, so we need to be able to map the field indexes to the right fields.
+ SafeMap<uint32_t, mirror::ArtField*> field_map;
+ ConstructFieldMap(dex_file, *dex_class_def, klass, field_map);
+ for (size_t i = 0; it.HasNext(); i++, it.Next()) {
+ it.ReadValueToField(field_map.Get(i));
+ }
}
return true;
}
- return false;
}
bool ClassLinker::LinkClass(SirtRef<mirror::Class>& klass,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index d0cc562..fdf17a2 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -443,7 +443,7 @@
bool InitializeSuperClass(mirror::Class* klass, bool can_run_clinit, bool can_init_fields)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Initialize static fields, returns true if fields were initialized.
- bool InitializeStaticFields(mirror::Class* klass)
+ bool InitializeStaticFields(mirror::Class* klass, bool can_init_statics)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsSameDescriptorInDifferentClassContexts(const char* descriptor,