Adds a DEX-to-DEX compilation level.
This CL adds a DEX-to-DEX compilation level which allows the DEX-to-DEX
compiler to ensure correctness on classes with soft-failed verification.
Bug: 9307738
Change-Id: If051336bf81370bca55872c8c75ccd573d8ca391
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 3c491ce..1ee29cb 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -36,9 +36,11 @@
class DexCompiler {
public:
DexCompiler(art::CompilerDriver& compiler,
- const DexCompilationUnit& unit)
+ const DexCompilationUnit& unit,
+ DexToDexCompilationLevel dex_to_dex_compilation_level)
: driver_(compiler),
- unit_(unit) {}
+ unit_(unit),
+ dex_to_dex_compilation_level_(dex_to_dex_compilation_level) {}
~DexCompiler() {}
@@ -55,6 +57,10 @@
return *const_cast<DexFile*>(unit_.GetDexFile());
}
+ bool PerformOptimizations() const {
+ return dex_to_dex_compilation_level_ >= kOptimize;
+ }
+
// Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
// a barrier is required.
void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
@@ -84,6 +90,7 @@
CompilerDriver& driver_;
const DexCompilationUnit& unit_;
+ const DexToDexCompilationLevel dex_to_dex_compilation_level_;
DISALLOW_COPY_AND_ASSIGN(DexCompiler);
};
@@ -138,6 +145,7 @@
};
void DexCompiler::Compile() {
+ DCHECK_GE(dex_to_dex_compilation_level_, kRequired);
const DexFile::CodeItem* code_item = unit_.GetCodeItem();
const uint16_t* insns = code_item->insns_;
const uint32_t insns_size = code_item->insns_size_in_code_units_;
@@ -220,7 +228,7 @@
}
Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
- if (!kEnableCheckCastEllision) {
+ if (!kEnableCheckCastEllision || !PerformOptimizations()) {
return inst;
}
MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex());
@@ -253,7 +261,7 @@
uint32_t dex_pc,
Instruction::Code new_opcode,
bool is_put) {
- if (!kEnableQuickening) {
+ if (!kEnableQuickening || !PerformOptimizations()) {
return;
}
uint32_t field_idx = inst->VRegC_22c();
@@ -280,7 +288,7 @@
uint32_t dex_pc,
Instruction::Code new_opcode,
bool is_range) {
- if (!kEnableQuickening) {
+ if (!kEnableQuickening || !PerformOptimizations()) {
return;
}
uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
@@ -320,14 +328,15 @@
} // namespace optimizer
} // namespace art
-extern "C" art::CompiledMethod*
- ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
+extern "C" void ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
uint32_t access_flags, art::InvokeType invoke_type,
uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
- const art::DexFile& dex_file) {
- art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
- dex_file, code_item, class_def_idx, method_idx, access_flags);
- art::optimizer::DexCompiler dex_compiler(compiler, unit);
- dex_compiler.Compile();
- return NULL;
+ const art::DexFile& dex_file,
+ art::DexToDexCompilationLevel dex_to_dex_compilation_level) {
+ if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) {
+ art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
+ dex_file, code_item, class_def_idx, method_idx, access_flags);
+ art::optimizer::DexCompiler dex_compiler(compiler, unit, dex_to_dex_compilation_level);
+ dex_compiler.Compile();
+ }
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 49aba4d..38d00a0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -369,7 +369,7 @@
compiler_ = reinterpret_cast<CompilerFn>(ArtQuickCompileMethod);
}
- dex_to_dex_compiler_ = reinterpret_cast<CompilerFn>(ArtCompileDEX);
+ dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX);
#ifdef ART_SEA_IR_MODE
sea_ir_compiler_ = NULL;
@@ -505,16 +505,10 @@
}
}
-static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader,
- const DexFile& dex_file,
- const DexFile::ClassDef& class_def)
+static DexToDexCompilationLevel GetDexToDexCompilationlevel(mirror::ClassLoader* class_loader,
+ const DexFile& dex_file,
+ const DexFile::ClassDef& class_def)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the
- // verifier from passing on "quick" instruction at compilation time. It must
- // only pass on quick instructions at runtime.
- if (class_loader == NULL) {
- return false;
- }
const char* descriptor = dex_file.GetClassDescriptor(class_def);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
mirror::Class* klass = class_linker->FindClass(descriptor, class_loader);
@@ -522,10 +516,27 @@
Thread* self = Thread::Current();
CHECK(self->IsExceptionPending());
self->ClearException();
- return false;
+ return kDontDexToDexCompile;
}
- // DEX-to-DEX compilation is only allowed on preverified classes.
- return klass->IsVerified();
+ // The verifier can only run on "quick" instructions at runtime (see usage of
+ // FindAccessedFieldAtDexPc and FindInvokedMethodAtDexPc in ThrowNullPointerExceptionFromDexPC
+ // function). Since image classes can be verified again while compiling an application,
+ // we must prevent the DEX-to-DEX compiler from introducing them.
+ // TODO: find a way to enable "quick" instructions for image classes and remove this check.
+ bool compiling_image_classes = (class_loader == NULL);
+ if (compiling_image_classes) {
+ return kRequired;
+ } else if (klass->IsVerified()) {
+ // Class is verified so we can enable DEX-to-DEX compilation for performance.
+ return kOptimize;
+ } else if (klass->IsCompileTimeVerified()) {
+ // Class verification has soft-failed. Anyway, ensure at least correctness.
+ DCHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
+ return kRequired;
+ } else {
+ // Class verification has failed: do not run DEX-to-DEX compilation.
+ return kDontDexToDexCompile;
+ }
}
void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, base::TimingLogger& timings) {
@@ -556,15 +567,15 @@
uint32_t method_idx = method->GetDexMethodIndex();
const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
// Can we run DEX-to-DEX compiler on this class ?
- bool allow_dex_compilation;
+ DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile;
{
ScopedObjectAccess soa(Thread::Current());
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
- allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def);
+ dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def);
}
CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(),
- class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation);
+ class_def_idx, method_idx, jclass_loader, *dex_file, dex_to_dex_compilation_level);
self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
@@ -2171,11 +2182,11 @@
return;
}
// Can we run DEX-to-DEX compiler on this class ?
- bool allow_dex_compilation;
+ DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile;
{
ScopedObjectAccess soa(Thread::Current());
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
- allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def);
+ dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def);
}
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
@@ -2198,7 +2209,7 @@
previous_direct_method_idx = method_idx;
manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, jclass_loader, dex_file, allow_dex_compilation);
+ method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
it.Next();
}
// Compile virtual methods
@@ -2214,7 +2225,7 @@
previous_virtual_method_idx = method_idx;
manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, jclass_loader, dex_file, allow_dex_compilation);
+ method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
it.Next();
}
DCHECK(!it.HasNext());
@@ -2231,7 +2242,7 @@
InvokeType invoke_type, uint32_t class_def_idx,
uint32_t method_idx, jobject class_loader,
const DexFile& dex_file,
- bool allow_dex_to_dex_compilation) {
+ DexToDexCompilationLevel dex_to_dex_compilation_level) {
CompiledMethod* compiled_method = NULL;
uint64_t start_ns = NanoTime();
@@ -2253,13 +2264,12 @@
compiled_method = (*compiler)(*this, code_item, access_flags, invoke_type, class_def_idx,
method_idx, class_loader, dex_file);
CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file);
- } else if (allow_dex_to_dex_compilation) {
+ } else if (dex_to_dex_compilation_level != kDontDexToDexCompile) {
// TODO: add a mode to disable DEX-to-DEX compilation ?
- compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags,
- invoke_type, class_def_idx,
- method_idx, class_loader, dex_file);
- // No native code is generated.
- CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file);
+ (*dex_to_dex_compiler_)(*this, code_item, access_flags,
+ invoke_type, class_def_idx,
+ method_idx, class_loader, dex_file,
+ dex_to_dex_compilation_level);
}
}
uint64_t duration_ns = NanoTime() - start_ns;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index a7a47ed..18f852d 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -48,6 +48,12 @@
kNoBackend
};
+enum DexToDexCompilationLevel {
+ kDontDexToDexCompile, // Only meaning wrt image time interpretation.
+ kRequired, // Dex-to-dex compilation required for correctness.
+ kOptimize // Perform required transformation and peep-hole optimizations.
+};
+
// Thread-local storage compiler worker threads
class CompilerTls {
public:
@@ -324,7 +330,7 @@
void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx,
jobject class_loader, const DexFile& dex_file,
- bool allow_dex_to_dex_compilation)
+ DexToDexCompilationLevel dex_to_dex_compilation_level)
LOCKS_EXCLUDED(compiled_methods_lock_);
static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
@@ -375,12 +381,19 @@
uint32_t access_flags, InvokeType invoke_type,
uint32_t class_dex_idx, uint32_t method_idx,
jobject class_loader, const DexFile& dex_file);
+
+ typedef void (*DexToDexCompilerFn)(CompilerDriver& driver,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags, InvokeType invoke_type,
+ uint32_t class_dex_idx, uint32_t method_idx,
+ jobject class_loader, const DexFile& dex_file,
+ DexToDexCompilationLevel dex_to_dex_compilation_level);
CompilerFn compiler_;
#ifdef ART_SEA_IR_MODE
CompilerFn sea_ir_compiler_;
#endif
- CompilerFn dex_to_dex_compiler_;
+ DexToDexCompilerFn dex_to_dex_compiler_;
void* compiler_context_;