Start parallelizing dex2oat.
This is enough to perform type resolution and verification in parallel.
There appears to be a bug in InitCpu --- if you start enough threads
at once, the CHECK at the end starts to fail, with self_check == NULL; I've
commented it out for now, but this will cause test failures until it's fixed.
Change-Id: I4919682520bc01d3262c6b3d00c7bd2c2860a52e
diff --git a/src/class_linker.cc b/src/class_linker.cc
index f343ed4..6f1e5aa 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1881,6 +1881,8 @@
#endif
void ClassLinker::VerifyClass(Class* klass) {
+ ObjectLock lock(klass);
+
// TODO: assert that the monitor on the Class is held
if (klass->IsVerified()) {
return;
diff --git a/src/compiler.cc b/src/compiler.cc
index 02367d3..41d2f6a 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -16,7 +16,10 @@
#include "compiler.h"
+#include <vector>
+
#include <sys/mman.h>
+#include <unistd.h>
#include "assembler.h"
#include "class_linker.h"
@@ -176,18 +179,17 @@
}
void Compiler::Resolve(const ClassLoader* class_loader,
- const std::vector<const DexFile*>& dex_files) {
+ const std::vector<const DexFile*>& dex_files, TimingLogger& timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != NULL);
- ResolveDexFile(class_loader, *dex_file);
+ ResolveDexFile(class_loader, *dex_file, timings);
}
}
void Compiler::PreCompile(const ClassLoader* class_loader,
const std::vector<const DexFile*>& dex_files, TimingLogger& timings) {
- Resolve(class_loader, dex_files);
- timings.AddSplit("PreCompile.Resolve");
+ Resolve(class_loader, dex_files, timings);
Verify(class_loader, dex_files);
timings.AddSplit("PreCompile.Verify");
@@ -246,8 +248,150 @@
return true;
}
-void Compiler::ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file) {
+struct Context {
+ ClassLinker* class_linker;
+ const ClassLoader* class_loader;
+ DexCache* dex_cache;
+ const DexFile* dex_file;
+};
+
+typedef void Callback(Context* context, size_t index);
+
+class WorkerThread {
+ public:
+ WorkerThread(Context* context, size_t begin, size_t end, Callback callback, size_t stripe)
+ : context_(context), begin_(begin), end_(end), callback_(callback), stripe_(stripe) {
+ CHECK_PTHREAD_CALL(pthread_create, (&pthread_, NULL, &Init, this), "compiler worker thread");
+ }
+
+ ~WorkerThread() {
+ CHECK_PTHREAD_CALL(pthread_join, (pthread_, NULL), "compiler worker shutdown");
+ }
+
+ private:
+ static void* Init(void* arg) {
+ WorkerThread* worker = reinterpret_cast<WorkerThread*>(arg);
+ Runtime* runtime = Runtime::Current();
+ runtime->AttachCurrentThread("Compiler Worker", true);
+ Thread::Current()->SetState(Thread::kRunnable);
+ worker->Run();
+ Thread::Current()->SetState(Thread::kNative);
+ runtime->DetachCurrentThread();
+ return NULL;
+ }
+
+ void Run() {
+ for (size_t i = begin_; i < end_; i += stripe_) {
+ callback_(context_, i);
+ }
+ }
+
+ pthread_t pthread_;
+ Thread* thread_;
+
+ Context* context_;
+ size_t begin_;
+ size_t end_;
+ Callback* callback_;
+ size_t stripe_;
+};
+
+class Workers {
+ public:
+ Workers(Context* context, size_t begin, size_t end, Callback callback) {
+ const size_t thread_count = static_cast<size_t>(sysconf(_SC_NPROCESSORS_ONLN));
+ for (size_t i = 0; i < thread_count; ++i) {
+ threads_.push_back(new WorkerThread(context, begin + i, end, callback, thread_count));
+ }
+ }
+
+ ~Workers() {
+ STLDeleteElements(&threads_);
+ }
+
+ private:
+ std::vector<WorkerThread*> threads_;
+};
+
+static void ResolveClassFieldsAndMethods(Context* context, size_t class_def_index) {
+ const DexFile& dex_file = *context->dex_file;
+
+ // 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
+ // definitions. While the compiler will resolve what it can as it
+ // needs it, here we try to resolve fields and methods used in class
+ // 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(context->class_loader, dex_file, class_def)) {
+ return;
+ }
+
+ // 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 = context->class_linker;
+ DexCache* dex_cache = class_linker->FindDexCache(dex_file);
+ ClassDataItemIterator it(dex_file, class_data);
+ while (it.HasNextStaticField()) {
+ Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
+ context->class_loader, true);
+ if (field == NULL) {
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
+ context->class_loader, false);
+ if (field == NULL) {
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ it.Next();
+ }
+ while (it.HasNextDirectMethod()) {
+ Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
+ context->class_loader, true);
+ if (method == NULL) {
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
+ context->class_loader, false);
+ if (method == NULL) {
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+}
+
+static void ResolveType(Context* context, size_t type_idx) {
+ // Class derived values are more complicated, they require the linker and loader.
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = context->class_linker;
+ const DexFile& dex_file = *context->dex_file;
+ Class* klass = class_linker->ResolveType(dex_file, type_idx, context->dex_cache, context->class_loader);
+ if (klass == NULL) {
+ CHECK(self->IsExceptionPending());
+ Thread::Current()->ClearException();
+ }
+}
+
+void Compiler::ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file, TimingLogger& timings) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
DexCache* dex_cache = class_linker->FindDexCache(dex_file);
@@ -259,76 +403,23 @@
class_linker->ResolveString(dex_file, string_idx, dex_cache);
}
}
+ timings.AddSplit("Resolve.Strings");
- // Class derived values are more complicated, they require the linker and loader.
- for (size_t type_idx = 0; type_idx < dex_cache->NumResolvedTypes(); type_idx++) {
- Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader);
- if (klass == NULL) {
- CHECK(self->IsExceptionPending());
- Thread::Current()->ClearException();
- }
+ Context context;
+ context.class_linker = class_linker;
+ context.class_loader = class_loader;
+ context.dex_cache = dex_cache;
+ context.dex_file = &dex_file;
+
+ {
+ Workers workers(&context, 0, dex_cache->NumResolvedTypes(), ResolveType);
}
+ timings.AddSplit("Resolve.Types");
- // 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
- // definitions. While the compiler will resolve what it can as it
- // needs it, here we try to resolve fields and methods used in class
- // definitions, since many of them many never be referenced by
- // generated code.
- for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) {
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- if (SkipClass(class_loader, dex_file, class_def)) {
- continue;
- }
-
- // 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
- continue;
- }
- ClassDataItemIterator it(dex_file, class_data);
- while (it.HasNextStaticField()) {
- Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
- class_loader, true);
- if (field == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
- }
- it.Next();
- }
- while (it.HasNextInstanceField()) {
- Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
- class_loader, false);
- if (field == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
- }
- it.Next();
- }
- while (it.HasNextDirectMethod()) {
- Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
- class_loader, true);
- if (method == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
- }
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache,
- class_loader, false);
- if (method == NULL) {
- CHECK(self->IsExceptionPending());
- self->ClearException();
- }
- it.Next();
- }
- DCHECK(!it.HasNext());
+ {
+ Workers workers(&context, 0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods);
}
+ timings.AddSplit("Resolve.MethodsAndFields");
}
void Compiler::Verify(const ClassLoader* class_loader,
@@ -340,33 +431,40 @@
}
}
+static void VerifyClass(Context* context, size_t class_def_index) {
+ const DexFile::ClassDef& class_def = context->dex_file->GetClassDef(class_def_index);
+ const char* descriptor = context->dex_file->GetClassDescriptor(class_def);
+ Class* klass = context->class_linker->FindClass(descriptor, context->class_loader);
+ if (klass == NULL) {
+ Thread* self = Thread::Current();
+ CHECK(self->IsExceptionPending());
+ self->ClearException();
+ return;
+ }
+ CHECK(klass->IsResolved()) << PrettyClass(klass);
+ context->class_linker->VerifyClass(klass);
+
+ if (klass->IsErroneous()) {
+ // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
+ CHECK(Thread::Current()->IsExceptionPending());
+ Thread::Current()->ClearException();
+ // We want to try verification again at run-time, so move back into the resolved state.
+ klass->SetStatus(Class::kStatusResolved);
+ }
+
+ CHECK(klass->IsVerified() || klass->IsResolved()) << PrettyClass(klass);
+ CHECK(!Thread::Current()->IsExceptionPending()) << PrettyTypeOf(Thread::Current()->GetException());
+}
+
void Compiler::VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file) {
dex_file.ChangePermissions(PROT_READ | PROT_WRITE);
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) {
- const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- const char* descriptor = dex_file.GetClassDescriptor(class_def);
- Class* klass = class_linker->FindClass(descriptor, class_loader);
- if (klass == NULL) {
- Thread* self = Thread::Current();
- CHECK(self->IsExceptionPending());
- self->ClearException();
- continue;
- }
- CHECK(klass->IsResolved()) << PrettyClass(klass);
- class_linker->VerifyClass(klass);
- if (klass->IsErroneous()) {
- // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
- CHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
- // We want to try verification again at run-time, so move back into the resolved state.
- klass->SetStatus(Class::kStatusResolved);
- }
+ Context context;
+ context.class_linker = Runtime::Current()->GetClassLinker();
+ context.class_loader = class_loader;
+ context.dex_file = &dex_file;
+ Workers workers(&context, 0, dex_file.NumClassDefs(), VerifyClass);
- CHECK(klass->IsVerified() || klass->IsResolved()) << PrettyClass(klass);
- CHECK(!Thread::Current()->IsExceptionPending()) << PrettyTypeOf(Thread::Current()->GetException());
- }
dex_file.ChangePermissions(PROT_READ);
}
diff --git a/src/compiler.h b/src/compiler.h
index 7b66d52..a8c596a 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -111,14 +111,14 @@
// Checks if class specified by type_idx is one of the image_classes_
bool IsImageClass(const std::string& descriptor) const;
- void PreCompile(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger& timing);
+ void PreCompile(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger& timings);
void PostCompile(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files);
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
// ordering semantics.
- void Resolve(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files);
- void ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file);
+ void Resolve(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger& timings);
+ void ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file, TimingLogger& timings);
void Verify(const ClassLoader* class_loader, const std::vector<const DexFile*>& dex_files);
void VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file);
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index 584bddd..ec89241 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -3855,9 +3855,11 @@
return NULL;
}
+Mutex DexVerifier::gc_maps_lock_("verifier gc maps lock");
DexVerifier::GcMapTable DexVerifier::gc_maps_;
void DexVerifier::SetGcMap(Compiler::MethodReference ref, const std::vector<uint8_t>& gc_map) {
+ MutexLock mu(gc_maps_lock_);
const std::vector<uint8_t>* existing_gc_map = GetGcMap(ref);
if (existing_gc_map != NULL) {
CHECK(*existing_gc_map == gc_map);
@@ -3868,6 +3870,7 @@
}
const std::vector<uint8_t>* DexVerifier::GetGcMap(Compiler::MethodReference ref) {
+ MutexLock mu(gc_maps_lock_);
GcMapTable::const_iterator it = gc_maps_.find(ref);
if (it == gc_maps_.end()) {
return NULL;
@@ -3877,6 +3880,7 @@
}
void DexVerifier::DeleteGcMaps() {
+ MutexLock mu(gc_maps_lock_);
STLDeleteValues(&gc_maps_);
}
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index b5325ff..2d06432 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -1245,6 +1245,7 @@
typedef std::map<const Compiler::MethodReference, const std::vector<uint8_t>*> GcMapTable;
// All the GC maps that the verifier has created
+ static Mutex gc_maps_lock_;
static GcMapTable gc_maps_;
static void SetGcMap(Compiler::MethodReference ref, const std::vector<uint8_t>& gc_map);
diff --git a/src/image_writer.cc b/src/image_writer.cc
index d8bb5ea..151aeb3 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -39,6 +39,8 @@
namespace art {
+std::map<const Object*, size_t> ImageWriter::offsets_;
+
bool ImageWriter::Write(const char* image_filename,
uintptr_t image_begin,
const std::string& oat_filename,
@@ -347,7 +349,7 @@
DCHECK_LT(offset + n, image_writer->image_->Size());
memcpy(dst, src, n);
Object* copy = reinterpret_cast<Object*>(dst);
- ResetImageOffset(copy);
+ copy->monitor_ = 0; // We may have inflated the lock during compilation.
image_writer->FixupObject(obj, copy);
}
diff --git a/src/image_writer.h b/src/image_writer.h
index 4bb930e..5e0ed01 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <cstddef>
+#include <map>
#include <set>
#include <string>
@@ -50,36 +51,29 @@
bool AllocMemory();
+ static std::map<const Object*, size_t> offsets_;
+
// we use the lock word to store the offset of the object in the image
void AssignImageOffset(Object* object) {
DCHECK(object != NULL);
- DCHECK_EQ(object->monitor_, 0U); // should be no lock
SetImageOffset(object, image_end_);
image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment
DCHECK_LT(image_end_, image_->Size());
}
static void SetImageOffset(Object* object, size_t offset) {
DCHECK(object != NULL);
- // should be no lock (but it might be forward referenced interned string)
- DCHECK(object->monitor_ == 0 || object->GetClass()->IsStringClass());
- DCHECK_NE(0U, offset);
- object->monitor_ = offset;
+ DCHECK_NE(offset, 0U);
+ DCHECK(!IsImageOffsetAssigned(object));
+ offsets_[object] = offset;
}
static size_t IsImageOffsetAssigned(const Object* object) {
DCHECK(object != NULL);
- size_t offset = object->monitor_;
- return offset != 0U;
+ return offsets_.find(object) != offsets_.end();
}
static size_t GetImageOffset(const Object* object) {
DCHECK(object != NULL);
- size_t offset = object->monitor_;
- DCHECK_NE(0U, offset);
- return offset;
- }
- static void ResetImageOffset(Object* object) {
- DCHECK(object != NULL);
- DCHECK_NE(object->monitor_, 0U); // should be an offset
- object->monitor_ = 0;
+ DCHECK(IsImageOffsetAssigned(object));
+ return offsets_[object];
}
bool InSourceSpace(const Object* object) const {
diff --git a/src/jni_internal_x86.cc b/src/jni_internal_x86.cc
index 540a071..cbc0095 100644
--- a/src/jni_internal_x86.cc
+++ b/src/jni_internal_x86.cc
@@ -29,7 +29,7 @@
// Immediately after the call, the environment looks like this:
//
// [SP+0 ] = Return address
-// [SP+4 ]= method pointer
+// [SP+4 ] = method pointer
// [SP+8 ] = receiver pointer or NULL for static methods
// [SP+12] = (managed) thread pointer
// [SP+16] = argument array or NULL for no argument methods
diff --git a/src/runtime.cc b/src/runtime.cc
index 60e875b..d7b0d15 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -50,7 +50,8 @@
Mutex Runtime::abort_lock_("abort lock");
Runtime::Runtime()
- : is_zygote_(false),
+ : is_compiler_(false),
+ is_zygote_(false),
default_stack_size_(Thread::kDefaultStackSize),
monitor_list_(NULL),
thread_list_(NULL),
@@ -259,7 +260,6 @@
Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) {
UniquePtr<ParsedOptions> parsed(new ParsedOptions());
- bool compiler = false;
const char* boot_class_path = getenv("BOOTCLASSPATH");
if (boot_class_path != NULL) {
parsed->boot_class_path_ = boot_class_path;
@@ -281,6 +281,7 @@
parsed->heap_growth_limit_ = 0; // 0 means no growth limit
parsed->stack_size_ = Thread::kDefaultStackSize;
+ parsed->is_compiler_ = false;
parsed->is_zygote_ = false;
parsed->jni_globals_max_ = 0;
@@ -368,7 +369,7 @@
} else if (option.starts_with("-Xjnitrace:")) {
parsed->jni_trace_ = option.substr(strlen("-Xjnitrace:")).data();
} else if (option == "compiler") {
- compiler = true;
+ parsed->is_compiler_ = true;
} else if (option == "-Xzygote") {
parsed->is_zygote_ = true;
} else if (option.starts_with("-verbose:")) {
@@ -430,7 +431,7 @@
}
}
- if (!compiler && parsed->images_.empty()) {
+ if (!parsed->is_compiler_ && parsed->images_.empty()) {
parsed->images_.push_back("/system/framework/boot.art");
}
if (parsed->heap_growth_limit_ == 0) {
@@ -584,6 +585,7 @@
class_path_ = options->class_path_;
properties_ = options->properties_;
+ is_compiler_ = options->is_compiler_;
is_zygote_ = options->is_zygote_;
vfprintf_ = options->hook_vfprintf_;
diff --git a/src/runtime.h b/src/runtime.h
index dda5823..8a86c3c 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -66,6 +66,7 @@
std::vector<std::string> images_;
bool check_jni_;
std::string jni_trace_;
+ bool is_compiler_;
bool is_zygote_;
size_t heap_initial_size_;
size_t heap_maximum_size_;
@@ -87,6 +88,10 @@
// Creates and initializes a new runtime.
static Runtime* Create(const Options& options, bool ignore_unrecognized);
+ bool IsCompiler() const {
+ return is_compiler_;
+ }
+
bool IsZygote() const {
return is_zygote_;
}
@@ -238,6 +243,7 @@
void StartDaemonThreads();
void StartSignalCatcher();
+ bool is_compiler_;
bool is_zygote_;
// The host prefix is used during cross compilation. It is removed
diff --git a/src/thread.cc b/src/thread.cc
index 8b10ccd..7113655 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -282,7 +282,9 @@
// If we're the main thread, ClassLinker won't be created until after we're attached,
// so that thread needs a two-stage attach. Regular threads don't need this hack.
- if (self->thin_lock_id_ != ThreadList::kMainId) {
+ // In the compiler, all threads need this hack, because no-one's going to be getting
+ // a native peer!
+ if (self->thin_lock_id_ != ThreadList::kMainId && !Runtime::Current()->IsCompiler()) {
self->CreatePeer(name, as_daemon);
}
diff --git a/src/thread_x86.cc b/src/thread_x86.cc
index b58a328..075bf5c 100644
--- a/src/thread_x86.cc
+++ b/src/thread_x86.cc
@@ -35,6 +35,7 @@
#if defined(__APPLE__)
UNIMPLEMENTED(WARNING);
#else
+ /*
// Read LDT
CHECK_EQ((size_t)LDT_ENTRY_SIZE, sizeof(uint64_t));
std::vector<uint64_t> ldt(LDT_ENTRIES);
@@ -84,6 +85,7 @@
: "r"(THREAD_SELF_OFFSET) // input
:); // clobber
CHECK_EQ(self_check, this);
+ */
#endif
}