Refactor CompilerDriver::CompileAll().
Treat verification results and image classes as mutable
only in CompilerDriver::PreCompile(), and treat them as
immutable during compilation, accessed through the
CompilerOptions. This severs the dependency of the inliner
on the CompilerDriver.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Change-Id: I594a0213ca6a5003c19b4bd488af98db4358d51d
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 792f508..63dcb46 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -99,11 +99,6 @@
return std::make_pair(fast_get, fast_put);
}
-inline VerificationResults* CompilerDriver::GetVerificationResults() const {
- DCHECK(Runtime::Current()->IsAotCompiler());
- return verification_results_;
-}
-
} // namespace art
#endif // ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 67cabef..18f7105 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -245,16 +245,12 @@
CompilerDriver::CompilerDriver(
const CompilerOptions* compiler_options,
- VerificationResults* verification_results,
Compiler::Kind compiler_kind,
- HashSet<std::string>* image_classes,
size_t thread_count,
int swap_fd)
: compiler_options_(compiler_options),
- verification_results_(verification_results),
compiler_(Compiler::Create(this, compiler_kind)),
compiler_kind_(compiler_kind),
- image_classes_(std::move(image_classes)),
number_of_soft_verifier_failures_(0),
had_hard_verifier_failure_(false),
parallel_thread_count_(thread_count),
@@ -266,10 +262,6 @@
compiler_->Init();
- if (GetCompilerOptions().IsBootImage()) {
- CHECK(image_classes_ != nullptr) << "Expected image classes for boot image";
- }
-
compiled_method_storage_.SetDedupeEnabled(compiler_options_->DeduplicateCode());
}
@@ -325,9 +317,8 @@
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
- InitializeThreadPools();
+ CheckThreadPools();
- PreCompile(class_loader, dex_files, timings);
if (GetCompilerOptions().IsBootImage()) {
// We don't need to setup the intrinsics for non boot image compilation, as
// those compilations will pick up a boot image that have the ArtMethod already
@@ -343,8 +334,6 @@
if (GetCompilerOptions().GetDumpStats()) {
stats_->Dump();
}
-
- FreeThreadPools();
}
static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel(
@@ -496,7 +485,7 @@
optimizer::DexToDexCompiler* const compiler = &driver->GetDexToDexCompiler();
if (compiler->ShouldCompileMethod(method_ref)) {
- VerificationResults* results = driver->GetVerificationResults();
+ const VerificationResults* results = driver->GetCompilerOptions().GetVerificationResults();
DCHECK(results != nullptr);
const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
// Do not optimize if a VerifiedMethod is missing. SafeCast elision,
@@ -574,7 +563,7 @@
} else if ((access_flags & kAccAbstract) != 0) {
// Abstract methods don't have code.
} else {
- VerificationResults* results = driver->GetVerificationResults();
+ const VerificationResults* results = driver->GetCompilerOptions().GetVerificationResults();
DCHECK(results != nullptr);
const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
bool compile =
@@ -881,7 +870,9 @@
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
+ TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes,
+ /*out*/ VerificationResults* verification_results) {
CheckThreadPools();
VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
@@ -898,7 +889,7 @@
// 6) Update the set of image classes.
// 7) For deterministic boot image, initialize bitstrings for type checking.
- LoadImageClasses(timings);
+ LoadImageClasses(timings, image_classes);
VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
if (compiler_options_->IsAnyCompilationEnabled()) {
@@ -932,7 +923,7 @@
ResolveConstStrings(dex_files, /*only_startup_strings=*/ true, timings);
}
- Verify(class_loader, dex_files, timings);
+ Verify(class_loader, dex_files, timings, verification_results);
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -957,7 +948,7 @@
VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
}
- UpdateImageClasses(timings);
+ UpdateImageClasses(timings, image_classes);
VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
if (kBitstringSubtypeCheckEnabled &&
@@ -1071,7 +1062,8 @@
};
// Make a list of descriptors for classes to include in the image
-void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
+void CompilerDriver::LoadImageClasses(TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes) {
CHECK(timings != nullptr);
if (!GetCompilerOptions().IsBootImage()) {
return;
@@ -1082,15 +1074,15 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- CHECK(image_classes_ != nullptr);
- for (auto it = image_classes_->begin(), end = image_classes_->end(); it != end;) {
+ CHECK(image_classes != nullptr);
+ for (auto it = image_classes->begin(), end = image_classes->end(); it != end;) {
const std::string& descriptor(*it);
StackHandleScope<1> hs(self);
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str())));
if (klass == nullptr) {
VLOG(compiler) << "Failed to find class " << descriptor;
- it = image_classes_->erase(it);
+ it = image_classes->erase(it);
self->ClearException();
} else {
++it;
@@ -1140,10 +1132,10 @@
// We walk the roots looking for classes so that we'll pick up the
// above classes plus any classes them depend on such super
// classes, interfaces, and the required ClassLinker roots.
- RecordImageClassesVisitor visitor(image_classes_);
+ RecordImageClassesVisitor visitor(image_classes);
class_linker->VisitClasses(&visitor);
- CHECK(!image_classes_->empty());
+ CHECK(!image_classes->empty());
}
static void MaybeAddToImageClasses(Thread* self,
@@ -1312,7 +1304,8 @@
DISALLOW_COPY_AND_ASSIGN(ClinitImageUpdate);
};
-void CompilerDriver::UpdateImageClasses(TimingLogger* timings) {
+void CompilerDriver::UpdateImageClasses(TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes) {
if (GetCompilerOptions().IsBootImage()) {
TimingLogger::ScopedTiming t("UpdateImageClasses", timings);
@@ -1324,7 +1317,7 @@
VariableSizedHandleScope hs(Thread::Current());
std::string error_msg;
std::unique_ptr<ClinitImageUpdate> update(ClinitImageUpdate::Create(hs,
- image_classes_,
+ image_classes,
Thread::Current(),
runtime->GetClassLinker()));
@@ -1393,12 +1386,6 @@
}
}
-const VerifiedMethod* CompilerDriver::GetVerifiedMethod(const DexFile* dex_file,
- uint32_t method_idx) const {
- MethodReference ref(dex_file, method_idx);
- return verification_results_->GetVerifiedMethod(ref);
-}
-
bool CompilerDriver::IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc) {
if (!compiler_options_->IsVerificationEnabled()) {
// If we didn't verify, every cast has to be treated as non-safe.
@@ -1764,7 +1751,8 @@
bool CompilerDriver::FastVerify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results) {
verifier::VerifierDeps* verifier_deps =
Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
// If there exist VerifierDeps that aren't the ones we just created to output, use them to verify.
@@ -1812,7 +1800,7 @@
// - Quickening will not do checkcast ellision.
// TODO(ngeoffray): Reconsider this once we refactor compiler filters.
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
- verification_results_->CreateVerifiedMethodFor(method.GetReference());
+ verification_results->CreateVerifiedMethodFor(method.GetReference());
}
}
} else if (!compiler_only_verifies) {
@@ -1830,8 +1818,9 @@
void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
- if (FastVerify(jclass_loader, dex_files, timings)) {
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results) {
+ if (FastVerify(jclass_loader, dex_files, timings, verification_results)) {
return;
}
@@ -2530,7 +2519,7 @@
// SetVerificationAttempted so that the access flags are set. If we do not do this they get
// changed at runtime resulting in more dirty image pages.
// Also create conflict tables.
- // Only useful if we are compiling an image (image_classes_ is not null).
+ // Only useful if we are compiling an image.
ScopedObjectAccess soa(Thread::Current());
VariableSizedHandleScope hs(soa.Self());
InitializeArrayClassesAndCreateConflictTablesVisitor visitor(hs);
@@ -2569,8 +2558,9 @@
ClassReference ref(&dex_file, class_def_index);
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
ClassAccessor accessor(dex_file, class_def_index);
+ CompilerDriver* const driver = context.GetCompiler();
// Skip compiling classes with generic verifier failures since they will still fail at runtime
- if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) {
+ if (driver->GetCompilerOptions().GetVerificationResults()->IsClassRejected(ref)) {
return;
}
// Use a scoped object access to perform to the quick SkipClass check.
@@ -2602,8 +2592,6 @@
// Go to native so that we don't block GC during compilation.
ScopedThreadSuspension sts(soa.Self(), kNative);
- CompilerDriver* const driver = context.GetCompiler();
-
// Can we run DEX-to-DEX compiler on this class ?
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);
@@ -2775,31 +2763,6 @@
return compiled_method;
}
-bool CompilerDriver::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
- uint16_t class_def_idx,
- const DexFile& dex_file) const {
- const VerifiedMethod* verified_method = GetVerifiedMethod(&dex_file, method_idx);
- if (verified_method != nullptr) {
- return !verified_method->HasVerificationFailures();
- }
-
- // If we can't find verification metadata, check if this is a system class (we trust that system
- // classes have their methods verified). If it's not, be conservative and assume the method
- // has not been verified successfully.
-
- // TODO: When compiling the boot image it should be safe to assume that everything is verified,
- // even if methods are not found in the verification cache.
- const char* descriptor = dex_file.GetClassDescriptor(dex_file.GetClassDef(class_def_idx));
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
- bool is_system_class = class_linker->FindSystemClass(self, descriptor) != nullptr;
- if (!is_system_class) {
- self->ClearException();
- }
- return is_system_class;
-}
-
std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
std::ostringstream oss;
const gc::Heap* const heap = Runtime::Current()->GetHeap();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 714b2d1..7c0fc64 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -76,7 +76,6 @@
class TimingLogger;
class VdexFile;
class VerificationResults;
-class VerifiedMethod;
enum EntryPointCallingConvention {
// ABI of invocations to a method's interpreter entry point.
@@ -95,9 +94,7 @@
// can assume will be in the image, with null implying all available
// classes.
CompilerDriver(const CompilerOptions* compiler_options,
- VerificationResults* verification_results,
Compiler::Kind compiler_kind,
- HashSet<std::string>* image_classes,
size_t thread_count,
int swap_fd);
@@ -106,6 +103,17 @@
// Set dex files classpath.
void SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files);
+ // Initialize and destroy thread pools. This is exposed because we do not want
+ // to do this twice, for PreCompile() and CompileAll().
+ void InitializeThreadPools();
+ void FreeThreadPools();
+
+ void PreCompile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings,
+ /*inout*/ HashSet<std::string>* image_classes,
+ /*out*/ VerificationResults* verification_results)
+ REQUIRES(!Locks::mutator_lock_);
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
@@ -124,8 +132,6 @@
Handle<mirror::ClassLoader> h_class_loader)
REQUIRES(!Locks::mutator_lock_);
- VerificationResults* GetVerificationResults() const;
-
const CompilerOptions& GetCompilerOptions() const {
return *compiler_options_;
}
@@ -194,7 +200,6 @@
REQUIRES_SHARED(Locks::mutator_lock_);
- const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
size_t GetThreadCount() const {
@@ -219,12 +224,6 @@
void RecordClassStatus(const ClassReference& ref, ClassStatus status);
- // Checks if the specified method has been verified without failures. Returns
- // false if the method is not in the verification results (GetVerificationResults).
- bool IsMethodVerifiedWithoutFailures(uint32_t method_idx,
- uint16_t class_def_idx,
- const DexFile& dex_file) const;
-
// Get memory usage during compilation.
std::string GetMemoryUsageString(bool extended) const;
@@ -265,13 +264,9 @@
}
private:
- void PreCompile(jobject class_loader,
- const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings)
+ void LoadImageClasses(TimingLogger* timings, /*inout*/ HashSet<std::string>* image_classes)
REQUIRES(!Locks::mutator_lock_);
- void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
-
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
// ordering semantics.
@@ -291,11 +286,13 @@
// verification was successful.
bool FastVerify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings);
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results);
void Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings);
+ TimingLogger* timings,
+ /*out*/ VerificationResults* verification_results);
void VerifyDexFile(jobject class_loader,
const DexFile& dex_file,
@@ -326,14 +323,13 @@
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
+ void UpdateImageClasses(TimingLogger* timings, /*inout*/ HashSet<std::string>* image_classes)
+ REQUIRES(!Locks::mutator_lock_);
void Compile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings);
- void InitializeThreadPools();
- void FreeThreadPools();
void CheckThreadPools();
// Resolve const string literals that are loaded from dex code. If only_startup_strings is
@@ -343,7 +339,6 @@
/*inout*/ TimingLogger* timings);
const CompilerOptions* const compiler_options_;
- VerificationResults* const verification_results_;
std::unique_ptr<Compiler> compiler_;
Compiler::Kind compiler_kind_;
@@ -359,12 +354,6 @@
// All method references that this compiler has compiled.
MethodTable compiled_methods_;
- // Image classes to be updated by PreCompile().
- // TODO: Remove this member which is a non-const pointer to the CompilerOptions' data.
- // Pass this explicitly to the PreCompile() which should be called directly from
- // Dex2Oat rather than implicitly by CompileAll().
- HashSet<std::string>* image_classes_;
-
std::atomic<uint32_t> number_of_soft_verifier_failures_;
bool had_hard_verifier_failure_;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index fe1568d..b924129 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -42,20 +42,18 @@
class CompilerDriverTest : public CommonCompilerTest {
protected:
- void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
- TimingLogger timings("CompilerDriverTest::CompileAll", false, false);
- TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
+ void CompileAllAndMakeExecutable(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
+ TimingLogger timings("CompilerDriverTest::CompileAllAndMakeExecutable", false, false);
dex_files_ = GetDexFiles(class_loader);
- SetDexFilesForOatFile(dex_files_);
- compiler_driver_->CompileAll(class_loader, dex_files_, &timings);
- t.NewTiming("MakeAllExecutable");
+ CompileAll(class_loader, dex_files_, &timings);
+ TimingLogger::ScopedTiming t("MakeAllExecutable", &timings);
MakeAllExecutable(class_loader);
}
void EnsureCompiled(jobject class_loader, const char* class_name, const char* method,
const char* signature, bool is_virtual)
REQUIRES(!Locks::mutator_lock_) {
- CompileAll(class_loader);
+ CompileAllAndMakeExecutable(class_loader);
Thread::Current()->TransitionFromSuspendedToRunnable();
bool started = runtime_->Start();
CHECK(started);
@@ -106,7 +104,7 @@
// Disabled due to 10 second runtime on host
// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598
TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) {
- CompileAll(nullptr);
+ CompileAllAndMakeExecutable(nullptr);
// All libcore references should resolve
ScopedObjectAccess soa(Thread::Current());
@@ -266,7 +264,7 @@
ASSERT_TRUE(dex_file->EnableWrite());
}
- CompileAll(class_loader);
+ CompileAllAndMakeExecutable(class_loader);
std::unordered_set<std::string> m = GetExpectedMethodsForClass("Main");
std::unordered_set<std::string> s = GetExpectedMethodsForClass("Second");
@@ -310,7 +308,7 @@
}
ASSERT_NE(class_loader, nullptr);
- CompileAll(class_loader);
+ CompileAllAndMakeExecutable(class_loader);
CheckVerifiedClass(class_loader, "LMain;");
CheckVerifiedClass(class_loader, "LSecond;");
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index b28c7e0..8d1ae3d 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -24,9 +24,14 @@
#include "arch/instruction_set_features.h"
#include "base/runtime_debug.h"
#include "base/variant_map.h"
+#include "class_linker.h"
#include "cmdline_parser.h"
#include "compiler_options_map-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/verification_results.h"
+#include "dex/verified_method.h"
#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
#include "simple_compiler_options_map.h"
namespace art {
@@ -44,6 +49,7 @@
no_inline_from_(),
dex_files_for_oat_file_(),
image_classes_(),
+ verification_results_(nullptr),
image_type_(ImageType::kNone),
compiling_with_core_image_(false),
baseline_(false),
@@ -141,4 +147,40 @@
return image_classes_.find(StringPiece(descriptor)) != image_classes_.end();
}
+const VerificationResults* CompilerOptions::GetVerificationResults() const {
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ return verification_results_;
+}
+
+const VerifiedMethod* CompilerOptions::GetVerifiedMethod(const DexFile* dex_file,
+ uint32_t method_idx) const {
+ MethodReference ref(dex_file, method_idx);
+ return verification_results_->GetVerifiedMethod(ref);
+}
+
+bool CompilerOptions::IsMethodVerifiedWithoutFailures(uint32_t method_idx,
+ uint16_t class_def_idx,
+ const DexFile& dex_file) const {
+ const VerifiedMethod* verified_method = GetVerifiedMethod(&dex_file, method_idx);
+ if (verified_method != nullptr) {
+ return !verified_method->HasVerificationFailures();
+ }
+
+ // If we can't find verification metadata, check if this is a system class (we trust that system
+ // classes have their methods verified). If it's not, be conservative and assume the method
+ // has not been verified successfully.
+
+ // TODO: When compiling the boot image it should be safe to assume that everything is verified,
+ // even if methods are not found in the verification cache.
+ const char* descriptor = dex_file.GetClassDescriptor(dex_file.GetClassDef(class_def_idx));
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ bool is_system_class = class_linker->FindSystemClass(self, descriptor) != nullptr;
+ if (!is_system_class) {
+ self->ClearException();
+ }
+ return is_system_class;
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a8f246d..bd12bf7 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -47,6 +47,8 @@
enum class InstructionSet;
class InstructionSetFeatures;
class ProfileCompilationInfo;
+class VerificationResults;
+class VerifiedMethod;
// Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared.
enum class ProfileMethodsCheck : uint8_t {
@@ -283,6 +285,16 @@
bool IsImageClass(const char* descriptor) const;
+ const VerificationResults* GetVerificationResults() const;
+
+ const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
+
+ // Checks if the specified method has been verified without failures. Returns
+ // false if the method is not in the verification results (GetVerificationResults).
+ bool IsMethodVerifiedWithoutFailures(uint32_t method_idx,
+ uint16_t class_def_idx,
+ const DexFile& dex_file) const;
+
bool ParseCompilerOptions(const std::vector<std::string>& options,
bool ignore_unrecognized,
std::string* error_msg);
@@ -381,6 +393,9 @@
// Must not be empty for real boot image, only for tests pretending to compile boot image.
HashSet<std::string> image_classes_;
+ // Results of AOT verification.
+ const VerificationResults* verification_results_;
+
ImageType image_type_;
bool compiling_with_core_image_;
bool baseline_;