Prevent verifier from creating unused compilation data.
The verifier used to create data which may be unused like GC map. This is the
case for non-compiled method (which are interpreted). This CL aims to optimize
this.
Here are the changes:
- Move compilation selection to MethodVerifier::IsCandidateForCompilation.
- Compiler and verifier use this method to know if a method must be compiled.
- Only create compilation data while compiling using Runtime::IsCompiler.
- Do not create internal structures concerning GC map, ... in Runtime::Init and
Runtime::Shutdown when we are not compiling.
- Checks we are compiling when accessing these structures.
- Add missing destruction of MethodVerifier::safecast_map_lock_ and
MethodVerifier::safecast_map_ in Runtime::Shutdown.
- Call Runtime::Shutdown just before Runtime instance is destroyed to avoid a
crash.
- Add missing "GUARDED_BY" macro for MethodVerifier::rejected_classes_ field.
- Add "has_check_casts" to avoid the safecast pass if there is no check-cast
instruction.
- Add "has_virtual_or_interface_invokes" to avoid the devirtualization pass if
there is no invoke-virtual/range nor invoke-interface/range instructions.
Bug: 9987437
Change-Id: I418ee99f63e4203409cf5b7d2c2295b22fcf24c1
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 2aa2a98..8ce0aa9 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2240,18 +2240,8 @@
CHECK(compiled_method != NULL);
} else if ((access_flags & kAccAbstract) != 0) {
} else {
- // In small mode we only compile image classes.
- bool dont_compile = (Runtime::Current()->IsSmallMode() &&
- ((image_classes_.get() == NULL) || (image_classes_->size() == 0)));
-
- // Don't compile class initializers, ever.
- if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
- dont_compile = true;
- } else if (code_item->insns_size_in_code_units_ < Runtime::Current()->GetSmallModeMethodDexSizeLimit()) {
- // Do compile small methods.
- dont_compile = false;
- }
- if (!dont_compile) {
+ bool compile = verifier::MethodVerifier::IsCandidateForCompilation(code_item, access_flags);
+ if (compile) {
CompilerFn compiler = compiler_;
#ifdef ART_SEA_IR_MODE
bool use_sea = Runtime::Current()->IsSeaIRMode();
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index cf6e537..48ee127 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -134,10 +134,10 @@
delete java_vm_;
Thread::Shutdown();
QuasiAtomic::Shutdown();
+ verifier::MethodVerifier::Shutdown();
// TODO: acquire a static mutex on Runtime to avoid racing.
CHECK(instance_ == NULL || instance_ == this);
instance_ = NULL;
- verifier::MethodVerifier::Shutdown();
}
struct AbortState {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index e182af7..3549945 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -282,7 +282,9 @@
new_instance_count_(0),
monitor_enter_count_(0),
can_load_classes_(can_load_classes),
- allow_soft_failures_(allow_soft_failures) {
+ allow_soft_failures_(allow_soft_failures),
+ has_check_casts_(false),
+ has_virtual_or_interface_invokes_(false) {
}
void MethodVerifier::FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc,
@@ -470,6 +472,13 @@
new_instance_count++;
} else if (opcode == Instruction::MONITOR_ENTER) {
monitor_enter_count++;
+ } else if (opcode == Instruction::CHECK_CAST) {
+ has_check_casts_ = true;
+ } else if ((inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
+ (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
+ (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
+ (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE)) {
+ has_virtual_or_interface_invokes_ = true;
}
size_t inst_size = inst->SizeInCodeUnits();
insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size);
@@ -1002,27 +1011,37 @@
return false;
}
- /* Generate a register map and add it to the method. */
- UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap());
- if (map.get() == NULL) {
- DCHECK_NE(failures_.size(), 0U);
- return false; // Not a real failure, but a failure to encode
- }
- if (kIsDebugBuild) {
- VerifyGcMap(*map);
- }
- MethodReference ref(dex_file_, dex_method_idx_);
- const std::vector<uint8_t>* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get()));
- verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map);
+ // Compute information for compiler.
+ if (Runtime::Current()->IsCompiler()) {
+ MethodReference ref(dex_file_, dex_method_idx_);
+ bool compile = IsCandidateForCompilation(code_item_, method_access_flags_);
+ if (compile) {
+ /* Generate a register map and add it to the method. */
+ UniquePtr<const std::vector<uint8_t> > map(GenerateGcMap());
+ if (map.get() == NULL) {
+ DCHECK_NE(failures_.size(), 0U);
+ return false; // Not a real failure, but a failure to encode
+ }
+ if (kIsDebugBuild) {
+ VerifyGcMap(*map);
+ }
+ const std::vector<uint8_t>* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get()));
+ verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map);
+ }
- MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet();
- if (method_to_safe_casts != NULL) {
- SetSafeCastMap(ref, method_to_safe_casts);
- }
+ if (has_check_casts_) {
+ MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet();
+ if(method_to_safe_casts != NULL ) {
+ SetSafeCastMap(ref, method_to_safe_casts);
+ }
+ }
- MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap();
- if (pc_to_concrete_method != NULL) {
- SetDevirtMap(ref, pc_to_concrete_method);
+ if (has_virtual_or_interface_invokes_) {
+ MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap();
+ if(pc_to_concrete_method != NULL ) {
+ SetDevirtMap(ref, pc_to_concrete_method);
+ }
+ }
}
return true;
}
@@ -3948,6 +3967,7 @@
}
void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>& gc_map) {
+ DCHECK(Runtime::Current()->IsCompiler());
{
WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
DexGcMapTable::iterator it = dex_gc_maps_->find(ref);
@@ -3962,6 +3982,7 @@
void MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) {
+ DCHECK(Runtime::Current()->IsCompiler());
MutexLock mu(Thread::Current(), *safecast_map_lock_);
SafeCastMap::iterator it = safecast_map_->find(ref);
if (it != safecast_map_->end()) {
@@ -3970,10 +3991,11 @@
}
safecast_map_->Put(ref, cast_set);
- CHECK(safecast_map_->find(ref) != safecast_map_->end());
+ DCHECK(safecast_map_->find(ref) != safecast_map_->end());
}
bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) {
+ DCHECK(Runtime::Current()->IsCompiler());
MutexLock mu(Thread::Current(), *safecast_map_lock_);
SafeCastMap::const_iterator it = safecast_map_->find(ref);
if (it == safecast_map_->end()) {
@@ -3986,6 +4008,7 @@
}
const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(MethodReference ref) {
+ DCHECK(Runtime::Current()->IsCompiler());
ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref);
if (it == dex_gc_maps_->end()) {
@@ -3998,6 +4021,7 @@
void MethodVerifier::SetDevirtMap(MethodReference ref,
const PcToConcreteMethodMap* devirt_map) {
+ DCHECK(Runtime::Current()->IsCompiler());
WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_);
DevirtualizationMapTable::iterator it = devirt_maps_->find(ref);
if (it != devirt_maps_->end()) {
@@ -4006,11 +4030,12 @@
}
devirt_maps_->Put(ref, devirt_map);
- CHECK(devirt_maps_->find(ref) != devirt_maps_->end());
+ DCHECK(devirt_maps_->find(ref) != devirt_maps_->end());
}
const MethodReference* MethodVerifier::GetDevirtMap(const MethodReference& ref,
uint32_t dex_pc) {
+ DCHECK(Runtime::Current()->IsCompiler());
ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_);
DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref);
if (it == devirt_maps_->end()) {
@@ -4070,6 +4095,24 @@
return result;
}
+bool MethodVerifier::IsCandidateForCompilation(const DexFile::CodeItem* code_item,
+ const uint32_t access_flags) {
+ // Don't compile class initializers, ever.
+ if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
+ return false;
+ }
+
+ const Runtime* runtime = Runtime::Current();
+ if (runtime->IsSmallMode() && runtime->UseCompileTimeClassPath()) {
+ // In Small mode, we only compile small methods.
+ const uint32_t code_size = code_item->insns_size_in_code_units_;
+ return (code_size < runtime->GetSmallModeMethodDexSizeLimit());
+ } else {
+ // In normal mode, we compile everything.
+ return true;
+ }
+}
+
ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL;
MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL;
@@ -4083,65 +4126,79 @@
MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL;
void MethodVerifier::Init() {
- dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock");
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- dex_gc_maps_ = new MethodVerifier::DexGcMapTable;
- }
+ if (Runtime::Current()->IsCompiler()) {
+ dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock");
+ Thread* self = Thread::Current();
+ {
+ WriterMutexLock mu(self, *dex_gc_maps_lock_);
+ dex_gc_maps_ = new MethodVerifier::DexGcMapTable;
+ }
- safecast_map_lock_ = new Mutex("verifier Cast Elision lock");
- {
- MutexLock mu(self, *safecast_map_lock_);
- safecast_map_ = new MethodVerifier::SafeCastMap();
- }
+ safecast_map_lock_ = new Mutex("verifier Cast Elision lock");
+ {
+ MutexLock mu(self, *safecast_map_lock_);
+ safecast_map_ = new MethodVerifier::SafeCastMap();
+ }
- devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock");
+ devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock");
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
- }
+ {
+ WriterMutexLock mu(self, *devirt_maps_lock_);
+ devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
+ }
- rejected_classes_lock_ = new Mutex("verifier rejected classes lock");
- {
- MutexLock mu(self, *rejected_classes_lock_);
- rejected_classes_ = new MethodVerifier::RejectedClassesTable;
+ rejected_classes_lock_ = new Mutex("verifier rejected classes lock");
+ {
+ MutexLock mu(self, *rejected_classes_lock_);
+ rejected_classes_ = new MethodVerifier::RejectedClassesTable;
+ }
}
art::verifier::RegTypeCache::Init();
}
void MethodVerifier::Shutdown() {
- Thread* self = Thread::Current();
- {
- WriterMutexLock mu(self, *dex_gc_maps_lock_);
- STLDeleteValues(dex_gc_maps_);
- delete dex_gc_maps_;
- dex_gc_maps_ = NULL;
- }
- delete dex_gc_maps_lock_;
- dex_gc_maps_lock_ = NULL;
+ if (Runtime::Current()->IsCompiler()) {
+ Thread* self = Thread::Current();
+ {
+ WriterMutexLock mu(self, *dex_gc_maps_lock_);
+ STLDeleteValues(dex_gc_maps_);
+ delete dex_gc_maps_;
+ dex_gc_maps_ = NULL;
+ }
+ delete dex_gc_maps_lock_;
+ dex_gc_maps_lock_ = NULL;
- {
- WriterMutexLock mu(self, *devirt_maps_lock_);
- STLDeleteValues(devirt_maps_);
- delete devirt_maps_;
- devirt_maps_ = NULL;
- }
- delete devirt_maps_lock_;
- devirt_maps_lock_ = NULL;
+ {
+ MutexLock mu(self, *safecast_map_lock_);
+ STLDeleteValues(safecast_map_);
+ delete safecast_map_;
+ safecast_map_ = NULL;
+ }
+ delete safecast_map_lock_;
+ safecast_map_lock_ = NULL;
- {
- MutexLock mu(self, *rejected_classes_lock_);
- delete rejected_classes_;
- rejected_classes_ = NULL;
+ {
+ WriterMutexLock mu(self, *devirt_maps_lock_);
+ STLDeleteValues(devirt_maps_);
+ delete devirt_maps_;
+ devirt_maps_ = NULL;
+ }
+ delete devirt_maps_lock_;
+ devirt_maps_lock_ = NULL;
+
+ {
+ MutexLock mu(self, *rejected_classes_lock_);
+ delete rejected_classes_;
+ rejected_classes_ = NULL;
+ }
+ delete rejected_classes_lock_;
+ rejected_classes_lock_ = NULL;
}
- delete rejected_classes_lock_;
- rejected_classes_lock_ = NULL;
verifier::RegTypeCache::ShutDown();
}
void MethodVerifier::AddRejectedClass(ClassReference ref) {
+ DCHECK(Runtime::Current()->IsCompiler());
{
MutexLock mu(Thread::Current(), *rejected_classes_lock_);
rejected_classes_->insert(ref);
@@ -4150,6 +4207,7 @@
}
bool MethodVerifier::IsClassRejected(ClassReference ref) {
+ DCHECK(Runtime::Current()->IsCompiler());
MutexLock mu(Thread::Current(), *rejected_classes_lock_);
return (rejected_classes_->find(ref) != rejected_classes_->end());
}
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 57d630d..fdd90cc 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -237,6 +237,9 @@
// Describe VRegs at the given dex pc.
std::vector<int32_t> DescribeVRegs(uint32_t dex_pc);
+ static bool IsCandidateForCompilation(const DexFile::CodeItem* code_item,
+ const uint32_t access_flags);
+
private:
// Adds the given string to the beginning of the last failure message.
void PrependToLastFailMessage(std::string);
@@ -654,7 +657,7 @@
LOCKS_EXCLUDED(devirt_maps_lock_);
typedef std::set<ClassReference> RejectedClassesTable;
static Mutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- static RejectedClassesTable* rejected_classes_;
+ static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_);
static void AddRejectedClass(ClassReference ref)
LOCKS_EXCLUDED(rejected_classes_lock_);
@@ -717,6 +720,13 @@
// Converts soft failures to hard failures when false. Only false when the compiler isn't
// running and the verifier is called from the class linker.
const bool allow_soft_failures_;
+
+ // Indicates if the method being verified contains at least one check-cast instruction.
+ bool has_check_casts_;
+
+ // Indicates if the method being verified contains at least one invoke-virtual/range
+ // or invoke-interface/range.
+ bool has_virtual_or_interface_invokes_;
};
std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs);