Use a per-thread VerifierDeps.

Avoid lock contention on a singleton VerifierDeps by allocating
temporary per-thread VerifierDeps that get merged after verification.

This saves around ~35% compile-times on interpret-only.

Only the creation of extra strings is guarded by a lock, for simplicity.

Test: test-art-host, test-art-target
bug: 32641252
bug: 30937355

Change-Id: I11a2367da882b58e39afa7b42cba2e74a209b75d
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c62e214..e155e10 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1980,8 +1980,16 @@
   // non boot image compilation. The verifier will need it to record the new dependencies.
   // Then dex2oat can update the vdex file with these new dependencies.
   if (!GetCompilerOptions().IsBootImage()) {
+    // Create the main VerifierDeps, and set it to this thread.
     Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
         new verifier::VerifierDeps(dex_files));
+    Thread::Current()->SetVerifierDeps(
+        Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps());
+    // Create per-thread VerifierDeps to avoid contention on the main one.
+    // We will merge them after verification.
+    for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
+      worker->GetThread()->SetVerifierDeps(new verifier::VerifierDeps(dex_files));
+    }
   }
   // Note: verification should not be pulling in classes anymore when compiling the boot image,
   //       as all should have been resolved before. As such, doing this in parallel should still
@@ -1995,6 +2003,19 @@
                   parallel_thread_count_,
                   timings);
   }
+
+  if (!GetCompilerOptions().IsBootImage()) {
+    verifier::VerifierDeps* main_deps =
+        Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+    // Merge all VerifierDeps into the main one.
+    for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) {
+      verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps();
+      worker->GetThread()->SetVerifierDeps(nullptr);
+      main_deps->MergeWith(*thread_deps, dex_files);;
+      delete thread_deps;
+    }
+    Thread::Current()->SetVerifierDeps(nullptr);
+  }
 }
 
 class VerifyClassVisitor : public CompilationVisitor {
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 03d3f4e..dcf3619 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -154,6 +154,7 @@
     }
     CHECK(method != nullptr);
 
+    Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps());
     MethodVerifier verifier(Thread::Current(),
                             primary_dex_file_,
                             dex_cache_handle,
@@ -169,6 +170,7 @@
                             false /* verify to dump */,
                             true /* allow_thread_suspension */);
     verifier.Verify();
+    Thread::Current()->SetVerifierDeps(nullptr);
     return !verifier.HasFailures();
   }
 
@@ -230,7 +232,6 @@
     const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str());
     DCHECK(type_id != nullptr);
     dex::TypeIndex index = primary_dex_file_->GetIndexForTypeId(*type_id);
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (const auto& dex_dep : verifier_deps_->dex_deps_) {
       for (dex::TypeIndex entry : dex_dep.second->unverified_classes_) {
         if (index == entry) {
@@ -246,7 +247,6 @@
   bool HasAssignable(const std::string& expected_destination,
                      const std::string& expected_source,
                      bool expected_is_assignable) {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       const DexFile& dex_file = *dex_dep.first;
       auto& storage = expected_is_assignable ? dex_dep.second->assignable_types_
@@ -268,7 +268,6 @@
   bool HasClass(const std::string& expected_klass,
                 bool expected_resolved,
                 const std::string& expected_access_flags = "") {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       for (auto& entry : dex_dep.second->classes_) {
         if (expected_resolved != entry.IsResolved()) {
@@ -303,7 +302,6 @@
                 bool expected_resolved,
                 const std::string& expected_access_flags = "",
                 const std::string& expected_decl_klass = "") {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       for (auto& entry : dex_dep.second->fields_) {
         if (expected_resolved != entry.IsResolved()) {
@@ -357,7 +355,6 @@
                  bool expected_resolved,
                  const std::string& expected_access_flags = "",
                  const std::string& expected_decl_klass = "") {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     for (auto& dex_dep : verifier_deps_->dex_deps_) {
       auto& storage = (expected_kind == "direct") ? dex_dep.second->direct_methods_
                           : (expected_kind == "virtual") ? dex_dep.second->virtual_methods_
@@ -406,13 +403,10 @@
   }
 
   size_t NumberOfCompiledDexFiles() {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
     return verifier_deps_->dex_deps_.size();
   }
 
   size_t HasEachKindOfRecord() {
-    MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
     bool has_strings = false;
     bool has_assignability = false;
     bool has_classes = false;
@@ -463,8 +457,6 @@
   ScopedObjectAccess soa(Thread::Current());
   LoadDexFile(&soa);
 
-  MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_);
-
   uint32_t id_Main1 = verifier_deps_->GetIdFromString(*primary_dex_file_, "LMain;");
   ASSERT_LT(id_Main1, primary_dex_file_->NumStringIds());
   ASSERT_EQ("LMain;", verifier_deps_->GetStringFromId(*primary_dex_file_, id_Main1));