Merge "Remove unused DMB code paths in the ARM64 Optimizing Compiler"
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 6f32e07..87eff82 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -66,7 +66,6 @@
jit/jit_compiler.cc \
jni/quick/calling_convention.cc \
jni/quick/jni_compiler.cc \
- optimizing/boolean_simplifier.cc \
optimizing/bounds_check_elimination.cc \
optimizing/builder.cc \
optimizing/code_generator.cc \
@@ -94,6 +93,7 @@
optimizing/prepare_for_register_allocation.cc \
optimizing/reference_type_propagation.cc \
optimizing/register_allocator.cc \
+ optimizing/select_generator.cc \
optimizing/sharpening.cc \
optimizing/side_effects_analysis.cc \
optimizing/ssa_builder.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index afc8463..e4bfac9 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -122,16 +122,7 @@
int result = mprotect(reinterpret_cast<void*>(base), len, PROT_READ | PROT_WRITE | PROT_EXEC);
CHECK_EQ(result, 0);
- // Flush instruction cache
- // Only uses __builtin___clear_cache if GCC >= 4.3.3
-#if GCC_VERSION >= 40303
- __builtin___clear_cache(reinterpret_cast<void*>(base), reinterpret_cast<void*>(base + len));
-#else
- // Only warn if not Intel as Intel doesn't have cache flush instructions.
-#if !defined(__i386__) && !defined(__x86_64__)
- UNIMPLEMENTED(WARNING) << "cache flush";
-#endif
-#endif
+ FlushInstructionCache(reinterpret_cast<char*>(base), reinterpret_cast<char*>(base + len));
}
void CommonCompilerTest::MakeExecutable(mirror::ClassLoader* class_loader, const char* class_name) {
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index 372fe2b..4d6c058 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -28,6 +28,8 @@
namespace art {
+static constexpr size_t kMips64DoublewordSize = 8;
+
/* This file contains codegen for the Mips ISA */
LIR* MipsMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) {
int opcode;
@@ -760,7 +762,25 @@
if (cu_->target64) {
if (short_form) {
- load = res = NewLIR3(opcode, r_dest.GetReg(), displacement, r_base.GetReg());
+ if (!IsAligned<kMips64DoublewordSize>(displacement) && opcode == kMips64Ld) {
+ RegStorage r_tmp = AllocTemp();
+ load = res = NewLIR3(kMips64Lwu, r_dest.GetReg(), displacement + LOWORD_OFFSET,
+ r_base.GetReg());
+ load2 = NewLIR3(kMips64Lwu, r_tmp.GetReg(), displacement + HIWORD_OFFSET, r_base.GetReg());
+ NewLIR3(kMips64Dsll32, r_tmp.GetReg(), r_tmp.GetReg(), 0x0);
+ NewLIR3(kMipsOr, r_dest.GetReg(), r_dest.GetReg(), r_tmp.GetReg());
+ FreeTemp(r_tmp);
+ } else if (!IsAligned<kMips64DoublewordSize>(displacement) && opcode == kMipsFldc1) {
+ RegStorage r_tmp = AllocTemp();
+ r_dest = Fp64ToSolo32(r_dest);
+ load = res = NewLIR3(kMipsFlwc1, r_dest.GetReg(), displacement + LOWORD_OFFSET,
+ r_base.GetReg());
+ load2 = NewLIR3(kMipsLw, r_tmp.GetReg(), displacement + HIWORD_OFFSET, r_base.GetReg());
+ NewLIR2(kMipsMthc1, r_tmp.GetReg(), r_dest.GetReg());
+ FreeTemp(r_tmp);
+ } else {
+ load = res = NewLIR3(opcode, r_dest.GetReg(), displacement, r_base.GetReg());
+ }
} else {
RegStorage r_tmp = (r_base == r_dest) ? AllocTemp() : r_dest;
res = OpRegRegImm(kOpAdd, r_tmp, r_base, displacement);
@@ -771,7 +791,12 @@
if (mem_ref_type_ == ResourceMask::kDalvikReg) {
DCHECK_EQ(r_base, TargetPtrReg(kSp));
- AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, r_dest.Is64Bit());
+ AnnotateDalvikRegAccess(load, (displacement + LOWORD_OFFSET) >> 2,
+ true /* is_load */, r_dest.Is64Bit() /* is64bit */);
+ if (load2 != nullptr) {
+ AnnotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2,
+ true /* is_load */, r_dest.Is64Bit() /* is64bit */);
+ }
}
return res;
}
@@ -932,7 +957,24 @@
if (cu_->target64) {
if (short_form) {
- store = res = NewLIR3(opcode, r_src.GetReg(), displacement, r_base.GetReg());
+ if (!IsAligned<kMips64DoublewordSize>(displacement) && opcode == kMips64Sd) {
+ RegStorage r_tmp = AllocTemp();
+ res = NewLIR2(kMipsMove, r_tmp.GetReg(), r_src.GetReg());
+ store = NewLIR3(kMipsSw, r_tmp.GetReg(), displacement + LOWORD_OFFSET, r_base.GetReg());
+ NewLIR3(kMips64Dsrl32, r_tmp.GetReg(), r_tmp.GetReg(), 0x0);
+ store2 = NewLIR3(kMipsSw, r_tmp.GetReg(), displacement + HIWORD_OFFSET, r_base.GetReg());
+ FreeTemp(r_tmp);
+ } else if (!IsAligned<kMips64DoublewordSize>(displacement) && opcode == kMipsFsdc1) {
+ RegStorage r_tmp = AllocTemp();
+ r_src = Fp64ToSolo32(r_src);
+ store = res = NewLIR3(kMipsFswc1, r_src.GetReg(), displacement + LOWORD_OFFSET,
+ r_base.GetReg());
+ NewLIR2(kMipsMfhc1, r_tmp.GetReg(), r_src.GetReg());
+ store2 = NewLIR3(kMipsSw, r_tmp.GetReg(), displacement + HIWORD_OFFSET, r_base.GetReg());
+ FreeTemp(r_tmp);
+ } else {
+ store = res = NewLIR3(opcode, r_src.GetReg(), displacement, r_base.GetReg());
+ }
} else {
RegStorage r_scratch = AllocTemp();
res = OpRegRegImm(kOpAdd, r_scratch, r_base, displacement);
@@ -942,7 +984,12 @@
if (mem_ref_type_ == ResourceMask::kDalvikReg) {
DCHECK_EQ(r_base, TargetPtrReg(kSp));
- AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, r_src.Is64Bit());
+ AnnotateDalvikRegAccess(store, (displacement + LOWORD_OFFSET) >> 2,
+ false /* is_load */, r_src.Is64Bit() /* is64bit */);
+ if (store2 != nullptr) {
+ AnnotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2,
+ false /* is_load */, r_src.Is64Bit() /* is64bit */);
+ }
}
return res;
}
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index c5df134..0cd41bb 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -71,6 +71,7 @@
nullptr,
false,
"",
+ false,
false);
VerificationResults verification_results(&compiler_options);
DexFileToMethodInlinerMap method_inliner_map;
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index d63878d..efdc333 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -54,6 +54,7 @@
nullptr,
false,
"",
+ false,
false));
verification_results_.reset(new VerificationResults(compiler_options_.get()));
method_inliner_map_.reset(new DexFileToMethodInlinerMap());
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 0eb3e43..0d65bc7 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -305,11 +305,31 @@
MethodReference* target_method, const MethodReference* devirt_target,
uintptr_t* direct_code, uintptr_t* direct_method) {
// Don't try to fast-path if we don't understand the caller's class.
+ // Referrer_class is the class that this invoke is contained in.
if (UNLIKELY(referrer_class == nullptr)) {
return 0;
}
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
- if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_class, resolved_method,
+ StackHandleScope<2> hs(soa.Self());
+ // Methods_class is the class refered to by the class_idx field of the methodId the method_idx is
+ // pointing to.
+ // For example in
+ // .class LABC;
+ // .super LDEF;
+ // .method hi()V
+ // ...
+ // invoke-super {p0}, LDEF;->hi()V
+ // ...
+ // .end method
+ // the referrer_class is 'ABC' and the methods_class is DEF. Note that the methods class is 'DEF'
+ // even if 'DEF' inherits the method from it's superclass.
+ Handle<mirror::Class> methods_class(hs.NewHandle(mUnit->GetClassLinker()->ResolveType(
+ *target_method->dex_file,
+ target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_,
+ dex_cache,
+ class_loader)));
+ DCHECK(methods_class.Get() != nullptr);
+ mirror::Class* methods_declaring_class = resolved_method->GetDeclaringClass();
+ if (UNLIKELY(!referrer_class->CanAccessResolvedMethod(methods_declaring_class, resolved_method,
dex_cache.Get(),
target_method->dex_method_index))) {
return 0;
@@ -318,18 +338,31 @@
// overridden (ie is final).
const bool same_dex_file = target_method->dex_file == mUnit->GetDexFile();
bool can_sharpen_virtual_based_on_type = same_dex_file &&
- (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal());
+ (*invoke_type == kVirtual) && (resolved_method->IsFinal() ||
+ methods_declaring_class->IsFinal());
// For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of
// the super class.
const size_t pointer_size = InstructionSetPointerSize(GetInstructionSet());
- bool can_sharpen_super_based_on_type = same_dex_file && (*invoke_type == kSuper) &&
- (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) &&
- resolved_method->GetMethodIndex() < methods_class->GetVTableLength() &&
- (methods_class->GetVTableEntry(
+ // TODO We should be able to sharpen if we are going into the boot image as well.
+ bool can_sharpen_super_based_on_type = same_dex_file &&
+ (*invoke_type == kSuper) &&
+ !methods_class->IsInterface() &&
+ (referrer_class != methods_declaring_class) &&
+ referrer_class->IsSubClass(methods_declaring_class) &&
+ resolved_method->GetMethodIndex() < methods_declaring_class->GetVTableLength() &&
+ (methods_declaring_class->GetVTableEntry(
resolved_method->GetMethodIndex(), pointer_size) == resolved_method) &&
resolved_method->IsInvokable();
+ // TODO We should be able to sharpen if we are going into the boot image as well.
+ bool can_sharpen_interface_super_based_on_type = same_dex_file &&
+ (*invoke_type == kSuper) &&
+ methods_class->IsInterface() &&
+ methods_class->IsAssignableFrom(referrer_class) &&
+ resolved_method->IsInvokable();
- if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) {
+ if (can_sharpen_virtual_based_on_type ||
+ can_sharpen_super_based_on_type ||
+ can_sharpen_interface_super_based_on_type) {
// Sharpen a virtual call into a direct call. The method_idx is into referrer's
// dex cache, check that this resolved method is where we expect it.
CHECK_EQ(target_method->dex_file, mUnit->GetDexFile());
@@ -363,7 +396,6 @@
*devirt_target->dex_file, devirt_target->dex_method_index, dex_cache, class_loader,
nullptr, kVirtual);
} else {
- StackHandleScope<1> hs(soa.Self());
auto target_dex_cache(hs.NewHandle(class_linker->RegisterDexFile(
*devirt_target->dex_file,
class_linker->GetOrCreateAllocatorForClassLoader(class_loader.Get()))));
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c483f33..6bc2a13 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -39,6 +39,7 @@
#include "compiler_driver-inl.h"
#include "dex_compilation_unit.h"
#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
#include "dex/dex_to_dex_compiler.h"
#include "dex/verification_results.h"
#include "dex/verified_method.h"
@@ -365,7 +366,7 @@
classes_to_compile_(compiled_classes),
methods_to_compile_(compiled_methods),
had_hard_verifier_failure_(false),
- thread_count_(thread_count),
+ parallel_thread_count_(thread_count),
stats_(new AOTCompilationStats),
dump_stats_(dump_stats),
dump_passes_(dump_passes),
@@ -435,24 +436,27 @@
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
- std::unique_ptr<ThreadPool> thread_pool(
- new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
+
+ InitializeThreadPools();
+
VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
// Precompile:
// 1) Load image classes
// 2) Resolve all classes
// 3) Attempt to verify all classes
// 4) Attempt to initialize image classes, and trivially initialized classes
- PreCompile(class_loader, dex_files, thread_pool.get(), timings);
+ PreCompile(class_loader, dex_files, timings);
// Compile:
// 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
// compilation.
if (!GetCompilerOptions().VerifyAtRuntime()) {
- Compile(class_loader, dex_files, thread_pool.get(), timings);
+ Compile(class_loader, dex_files, timings);
}
if (dump_stats_) {
stats_->Dump();
}
+
+ FreeThreadPools();
}
static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
@@ -653,8 +657,9 @@
std::vector<const DexFile*> dex_files;
dex_files.push_back(dex_file);
- std::unique_ptr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", 0U));
- PreCompile(jclass_loader, dex_files, thread_pool.get(), timings);
+ InitializeThreadPools();
+
+ PreCompile(jclass_loader, dex_files, timings);
// Can we run DEX-to-DEX compiler on this class ?
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -677,20 +682,147 @@
true,
dex_cache);
+ FreeThreadPools();
+
self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
}
-void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Resolve(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ // Resolution allocates classes and needs to run single-threaded to be deterministic.
+ bool force_determinism = GetCompilerOptions().IsForceDeterminism();
+ ThreadPool* resolve_thread_pool = force_determinism
+ ? single_thread_pool_.get()
+ : parallel_thread_pool_.get();
+ size_t resolve_thread_count = force_determinism ? 1U : parallel_thread_count_;
+
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != nullptr);
- ResolveDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ ResolveDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ resolve_thread_pool,
+ resolve_thread_count,
+ timings);
}
}
-void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+// Resolve const-strings in the code. Done to have deterministic allocation behavior. Right now
+// this is single-threaded for simplicity.
+// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a
+// stable order.
+
+static void ResolveConstStrings(CompilerDriver* driver,
+ const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ if (code_item == nullptr) {
+ // Abstract or native method.
+ return;
+ }
+
+ const uint16_t* code_ptr = code_item->insns_;
+ const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_;
+
+ while (code_ptr < code_end) {
+ const Instruction* inst = Instruction::At(code_ptr);
+ switch (inst->Opcode()) {
+ case Instruction::CONST_STRING: {
+ uint32_t string_index = inst->VRegB_21c();
+ driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
+ break;
+ }
+ case Instruction::CONST_STRING_JUMBO: {
+ uint32_t string_index = inst->VRegB_31c();
+ driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ code_ptr += inst->SizeInCodeUnits();
+ }
+}
+
+static void ResolveConstStrings(CompilerDriver* driver,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ for (const DexFile* dex_file : dex_files) {
+ TimingLogger::ScopedTiming t("Resolve const-string Strings", timings);
+
+ size_t class_def_count = dex_file->NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ // empty class, probably a marker interface
+ continue;
+ }
+
+ ClassDataItemIterator it(*dex_file, class_data);
+ // Skip fields
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+
+ bool compilation_enabled = driver->IsClassToCompile(
+ dex_file->StringByTypeIdx(class_def.class_idx_));
+ if (!compilation_enabled) {
+ // Compilation is skipped, do not resolve const-string in code of this class.
+ // TODO: Make sure that inlining honors this.
+ continue;
+ }
+
+ // Direct methods.
+ int64_t previous_direct_method_idx = -1;
+ while (it.HasNextDirectMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ if (method_idx == previous_direct_method_idx) {
+ // smali can create dex files with two encoded_methods sharing the same method_idx
+ // http://code.google.com/p/smali/issues/detail?id=119
+ it.Next();
+ continue;
+ }
+ previous_direct_method_idx = method_idx;
+ ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+ it.Next();
+ }
+ // Virtual methods.
+ int64_t previous_virtual_method_idx = -1;
+ while (it.HasNextVirtualMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ if (method_idx == previous_virtual_method_idx) {
+ // smali can create dex files with two encoded_methods sharing the same method_idx
+ // http://code.google.com/p/smali/issues/detail?id=119
+ it.Next();
+ continue;
+ }
+ previous_virtual_method_idx = method_idx;
+ ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+ }
+}
+
+inline void CompilerDriver::CheckThreadPools() {
+ DCHECK(parallel_thread_pool_ != nullptr);
+ DCHECK(single_thread_pool_ != nullptr);
+}
+
+void CompilerDriver::PreCompile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ CheckThreadPools();
+
LoadImageClasses(timings);
VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
@@ -700,20 +832,26 @@
// We need to resolve for never_verify since it needs to run dex to dex to add the
// RETURN_VOID_NO_BARRIER.
if (never_verify || verification_enabled) {
- Resolve(class_loader, dex_files, thread_pool, timings);
+ Resolve(class_loader, dex_files, timings);
VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
}
if (never_verify) {
VLOG(compiler) << "Verify none mode specified, skipping verification.";
- SetVerified(class_loader, dex_files, thread_pool, timings);
+ SetVerified(class_loader, dex_files, timings);
}
if (!verification_enabled) {
return;
}
- Verify(class_loader, dex_files, thread_pool, timings);
+ if (GetCompilerOptions().IsForceDeterminism() && IsBootImage()) {
+ // Resolve strings from const-string. Do this now to have a deterministic image.
+ ResolveConstStrings(this, dex_files, timings);
+ VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
+ }
+
+ Verify(class_loader, dex_files, timings);
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -721,7 +859,7 @@
<< "situations. Please check the log.";
}
- InitializeClasses(class_loader, dex_files, thread_pool, timings);
+ InitializeClasses(class_loader, dex_files, timings);
VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
UpdateImageClasses(timings);
@@ -783,7 +921,7 @@
std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve)
: exceptions_to_resolve_(exceptions_to_resolve) {}
- virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ virtual bool operator()(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (auto& m : c->GetMethods(pointer_size)) {
ResolveExceptionsForMethod(&m, pointer_size);
@@ -837,7 +975,7 @@
explicit RecordImageClassesVisitor(std::unordered_set<std::string>* image_classes)
: image_classes_(image_classes) {}
- bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool operator()(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
std::string temp;
image_classes_->insert(klass->GetDescriptor(&temp));
return true;
@@ -1004,7 +1142,7 @@
public:
explicit FindImageClassesVisitor(ClinitImageUpdate* data) : data_(data) {}
- bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool operator()(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
std::string temp;
const char* name = klass->GetDescriptor(&temp);
if (data_->image_class_descriptors_->find(name) != data_->image_class_descriptors_->end()) {
@@ -1759,6 +1897,9 @@
// Wait for all the worker threads to finish.
thread_pool_->Wait(self, true, false);
+
+ // And stop the workers accepting jobs.
+ thread_pool_->StopWorkers(self);
}
size_t NextIndex() {
@@ -1995,9 +2136,12 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::ResolveDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// TODO: we could resolve strings here, although the string table is largely filled with class
@@ -2010,27 +2154,43 @@
// classdefs are resolved by ResolveClassFieldsAndMethods.
TimingLogger::ScopedTiming t("Resolve Types", timings);
ResolveTypeVisitor visitor(&context);
- context.ForAll(0, dex_file.NumTypeIds(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumTypeIds(), &visitor, thread_count);
}
TimingLogger::ScopedTiming t("Resolve MethodsAndFields", timings);
ResolveClassFieldsAndMethodsVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
-void CompilerDriver::SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::SetVerified(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ // This can be run in parallel.
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
- SetVerifiedDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ SetVerifiedDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings);
}
}
-void CompilerDriver::Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Verify(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ // 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
+ // be deterministic.
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
- VerifyDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ VerifyDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings);
}
}
@@ -2104,15 +2264,18 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::VerifyDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("Verify Dex File", timings);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
thread_pool);
VerifyClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
class SetVerifiedClassVisitor : public CompilationVisitor {
@@ -2162,15 +2325,18 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::SetVerifiedDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("Verify Dex File", timings);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
thread_pool);
SetVerifiedClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
class InitializeClassVisitor : public CompilationVisitor {
@@ -2271,31 +2437,37 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
+void CompilerDriver::InitializeClasses(jobject jni_class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("InitializeNoClinit", timings);
+
+ // Initialization allocates objects and needs to run single-threaded to be deterministic.
+ bool force_determinism = GetCompilerOptions().IsForceDeterminism();
+ ThreadPool* init_thread_pool = force_determinism
+ ? single_thread_pool_.get()
+ : parallel_thread_pool_.get();
+ size_t init_thread_count = force_determinism ? 1U : parallel_thread_count_;
+
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, dex_files,
- thread_pool);
- size_t thread_count;
+ init_thread_pool);
if (IsBootImage()) {
// TODO: remove this when transactional mode supports multithreading.
- thread_count = 1U;
- } else {
- thread_count = thread_count_;
+ init_thread_count = 1U;
}
InitializeClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, init_thread_count);
}
void CompilerDriver::InitializeClasses(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ TimingLogger* timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != nullptr);
- InitializeClasses(class_loader, *dex_file, dex_files, thread_pool, timings);
+ InitializeClasses(class_loader, *dex_file, dex_files, timings);
}
if (IsBootImage()) {
// Prune garbage objects created during aborted transactions.
@@ -2303,8 +2475,9 @@
}
}
-void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Compile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
if (kDebugProfileGuidedCompilation) {
LOG(INFO) << "[ProfileGuidedCompilation] " <<
((profile_compilation_info_ == nullptr)
@@ -2314,7 +2487,12 @@
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != nullptr);
- CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ CompileDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings);
}
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
@@ -2421,14 +2599,17 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::CompileDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("Compile Dex File", timings);
ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
&dex_file, dex_files, thread_pool);
CompileClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref,
@@ -2590,4 +2771,16 @@
return true;
}
+void CompilerDriver::InitializeThreadPools() {
+ size_t parallel_count = parallel_thread_count_ > 0 ? parallel_thread_count_ - 1 : 0;
+ parallel_thread_pool_.reset(
+ new ThreadPool("Compiler driver thread pool", parallel_count));
+ single_thread_pool_.reset(new ThreadPool("Single-threaded Compiler driver thread pool", 0));
+}
+
+void CompilerDriver::FreeThreadPools() {
+ parallel_thread_pool_.reset();
+ single_thread_pool_.reset();
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 6a2f7bf..5e35cbb 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -411,7 +411,7 @@
}
size_t GetThreadCount() const {
- return thread_count_;
+ return parallel_thread_count_;
}
bool GetDumpStats() const {
@@ -550,8 +550,9 @@
SHARED_REQUIRES(Locks::mutator_lock_);
private:
- void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ void PreCompile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
@@ -559,49 +560,71 @@
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
// ordering semantics.
- void Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ void Resolve(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void ResolveDexFile(jobject class_loader, const DexFile& dex_file,
+ void ResolveDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings);
- void VerifyDexFile(jobject class_loader, const DexFile& dex_file,
+ void Verify(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings);
+ void VerifyDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings);
- void SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
+ void SetVerified(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings);
+ void SetVerifiedDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
- REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
- void InitializeClasses(jobject class_loader, const DexFile& dex_file,
+ void InitializeClasses(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ TimingLogger* timings)
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
+ void InitializeClasses(jobject class_loader,
+ const DexFile& dex_file,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
static void FindClinitImageClassesCallback(mirror::Object* object, void* arg)
SHARED_REQUIRES(Locks::mutator_lock_);
- void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings);
- void CompileDexFile(jobject class_loader, const DexFile& dex_file,
+ void Compile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings);
+ void CompileDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
bool MayInlineInternal(const DexFile* inlined_from, const DexFile* inlined_into) const;
+ void InitializeThreadPools();
+ void FreeThreadPools();
+ void CheckThreadPools();
+
const CompilerOptions* const compiler_options_;
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -652,7 +675,12 @@
bool had_hard_verifier_failure_;
- size_t thread_count_;
+ // A thread pool that can (potentially) run tasks in parallel.
+ std::unique_ptr<ThreadPool> parallel_thread_pool_;
+ size_t parallel_thread_count_;
+
+ // A thread pool that guarantees running single-threaded on the main thread.
+ std::unique_ptr<ThreadPool> single_thread_pool_;
class AOTCompilationStats;
std::unique_ptr<AOTCompilationStats> stats_;
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 9285b8c..3bf8921 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -47,7 +47,8 @@
abort_on_hard_verifier_failure_(false),
init_failure_output_(nullptr),
dump_cfg_file_name_(""),
- dump_cfg_append_(false) {
+ dump_cfg_append_(false),
+ force_determinism_(false) {
}
CompilerOptions::~CompilerOptions() {
@@ -76,7 +77,8 @@
std::ostream* init_failure_output,
bool abort_on_hard_verifier_failure,
const std::string& dump_cfg_file_name,
- bool dump_cfg_append
+ bool dump_cfg_append,
+ bool force_determinism
) : // NOLINT(whitespace/parens)
compiler_filter_(compiler_filter),
huge_method_threshold_(huge_method_threshold),
@@ -102,7 +104,8 @@
abort_on_hard_verifier_failure_(abort_on_hard_verifier_failure),
init_failure_output_(init_failure_output),
dump_cfg_file_name_(dump_cfg_file_name),
- dump_cfg_append_(dump_cfg_append) {
+ dump_cfg_append_(dump_cfg_append),
+ force_determinism_(force_determinism) {
}
void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 6989bd5..a220959 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -86,7 +86,8 @@
std::ostream* init_failure_output,
bool abort_on_hard_verifier_failure,
const std::string& dump_cfg_file_name,
- bool dump_cfg_append);
+ bool dump_cfg_append,
+ bool force_determinism);
CompilerFilter GetCompilerFilter() const {
return compiler_filter_;
@@ -115,6 +116,10 @@
return compiler_filter_ == CompilerOptions::kVerifyNone;
}
+ bool IsExtractOnly() const {
+ return compiler_filter_ == CompilerOptions::kVerifyAtRuntime;
+ }
+
size_t GetHugeMethodThreshold() const {
return huge_method_threshold_;
}
@@ -245,6 +250,10 @@
return dump_cfg_append_;
}
+ bool IsForceDeterminism() const {
+ return force_determinism_;
+ }
+
private:
void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
void ParsePassOptions(const StringPiece& option, UsageFn Usage);
@@ -300,6 +309,10 @@
std::string dump_cfg_file_name_;
bool dump_cfg_append_;
+ // Whether the compiler should trade performance for determinism to guarantee exactly reproducable
+ // outcomes.
+ bool force_determinism_;
+
friend class Dex2Oat;
DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index b65fb36..a5a7796 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -119,6 +119,7 @@
compiler_driver_->GetInstructionSet(),
compiler_driver_->GetInstructionSetFeatures(),
&key_value_store,
+ /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify.
&opened_dex_files_map,
&opened_dex_files);
ASSERT_TRUE(dex_files_ok);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 72c615e..d63fed5 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -276,6 +276,11 @@
if (image_fd != kInvalidFd) {
if (strlen(image_filename) == 0u) {
image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage));
+ // Empty the file in case it already exists.
+ if (image_file != nullptr) {
+ TEMP_FAILURE_RETRY(image_file->SetLength(0));
+ TEMP_FAILURE_RETRY(image_file->Flush());
+ }
} else {
LOG(ERROR) << "image fd " << image_fd << " name " << image_filename;
}
@@ -709,7 +714,7 @@
class ComputeLazyFieldsForClassesVisitor : public ClassVisitor {
public:
- bool Visit(Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool operator()(Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
StackHandleScope<1> hs(Thread::Current());
mirror::Class::ComputeName(hs.NewHandle(c));
return true;
@@ -847,14 +852,14 @@
public:
explicit NonImageClassesVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
- bool Visit(Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool operator()(Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
if (!image_writer_->KeepClass(klass)) {
classes_to_prune_.insert(klass);
}
return true;
}
- std::unordered_set<mirror::Class*> classes_to_prune_;
+ mutable std::unordered_set<mirror::Class*> classes_to_prune_;
ImageWriter* const image_writer_;
};
@@ -1866,6 +1871,9 @@
orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename));
FixupClassVisitor visitor(this, copy);
static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor);
+
+ // Remove the clinitThreadId. This is required for image determinism.
+ copy->SetClinitThreadId(static_cast<pid_t>(0));
}
void ImageWriter::FixupObject(Object* orig, Object* copy) {
@@ -1993,6 +2001,10 @@
mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
}
}
+
+ // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
+ // compiler pointers in here will make the output non-deterministic.
+ copy_dex_cache->SetDexFile(nullptr);
}
const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const {
@@ -2262,13 +2274,12 @@
return GetConstImageInfo(oat_filenames_[index]);
}
-void ImageWriter::UpdateOatFile(const char* oat_filename) {
- std::unique_ptr<File> oat_file(OS::OpenFileForReading(oat_filename));
+void ImageWriter::UpdateOatFile(File* oat_file, const char* oat_filename) {
DCHECK(oat_file != nullptr);
- size_t oat_loaded_size = 0;
- size_t oat_data_offset = 0;
- ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset);
-
+ if (compile_app_image_) {
+ CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image.";
+ return;
+ }
ImageInfo& cur_image_info = GetImageInfo(oat_filename);
// Update the oat_offset of the next image info.
@@ -2277,6 +2288,9 @@
it++;
if (it != oat_filenames_.end()) {
+ size_t oat_loaded_size = 0;
+ size_t oat_data_offset = 0;
+ ElfWriter::GetOatElfInformation(oat_file, &oat_loaded_size, &oat_data_offset);
// There is a following one.
ImageInfo& next_image_info = GetImageInfo(*it);
next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size;
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 622eb19..9371d9f 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -123,7 +123,7 @@
// Update the oat size for the given oat file. This will make the oat_offset for the next oat
// file valid.
- void UpdateOatFile(const char* oat_filename);
+ void UpdateOatFile(File* oat_file, const char* oat_filename);
private:
bool AllocMemory();
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 3a3275a..6774758 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -112,7 +112,8 @@
/* init_failure_output */ nullptr,
/* abort_on_hard_verifier_failure */ false,
/* dump_cfg_file_name */ "",
- /* dump_cfg_append */ false));
+ /* dump_cfg_append */ false,
+ /* force_determinism */ false));
for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
compiler_options_->ParseCompilerOption(argument, Usage);
}
@@ -178,7 +179,7 @@
if (compiler_options_->GetGenerateDebugInfo()) {
#ifdef __ANDROID__
- const char* prefix = GetAndroidData();
+ const char* prefix = "/data/misc/trace";
#else
const char* prefix = "/tmp";
#endif
@@ -187,7 +188,8 @@
std::string perf_filename = std::string(prefix) + "/perf-" + std::to_string(getpid()) + ".map";
perf_file_.reset(OS::CreateEmptyFileWriteOnly(perf_filename.c_str()));
if (perf_file_ == nullptr) {
- LOG(FATAL) << "Could not create perf file at " << perf_filename;
+ LOG(ERROR) << "Could not create perf file at " << perf_filename <<
+ " Are you on a user build? Perf only works on userdebug/eng builds";
}
}
}
@@ -222,7 +224,7 @@
ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*));
JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache();
success = compiler_driver_->GetCompiler()->JitCompile(self, code_cache, method_to_compile);
- if (success && compiler_options_->GetGenerateDebugInfo()) {
+ if (success && perf_file_ != nullptr) {
const void* ptr = method_to_compile->GetEntryPointFromQuickCompiledCode();
std::ostringstream stream;
stream << std::hex
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index c0d15f3..cff2f47 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -126,7 +126,8 @@
bool WriteElf(File* file,
const std::vector<const DexFile*>& dex_files,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
TimingLogger timings("WriteElf", false, false);
OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
for (const DexFile* dex_file : dex_files) {
@@ -139,12 +140,13 @@
return false;
}
}
- return DoWriteElf(file, oat_writer, key_value_store);
+ return DoWriteElf(file, oat_writer, key_value_store, verify);
}
bool WriteElf(File* file,
const std::vector<const char*>& dex_filenames,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
TimingLogger timings("WriteElf", false, false);
OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
for (const char* dex_filename : dex_filenames) {
@@ -152,24 +154,26 @@
return false;
}
}
- return DoWriteElf(file, oat_writer, key_value_store);
+ return DoWriteElf(file, oat_writer, key_value_store, verify);
}
bool WriteElf(File* file,
ScopedFd&& zip_fd,
const char* location,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
TimingLogger timings("WriteElf", false, false);
OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) {
return false;
}
- return DoWriteElf(file, oat_writer, key_value_store);
+ return DoWriteElf(file, oat_writer, key_value_store, verify);
}
bool DoWriteElf(File* file,
OatWriter& oat_writer,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
compiler_driver_->GetInstructionSet(),
&compiler_driver_->GetCompilerOptions(),
@@ -183,6 +187,7 @@
compiler_driver_->GetInstructionSet(),
compiler_driver_->GetInstructionSetFeatures(),
&key_value_store,
+ verify,
&opened_dex_files_map,
&opened_dex_files)) {
return false;
@@ -219,6 +224,9 @@
return elf_writer->End();
}
+ void TestDexFileInput(bool verify);
+ void TestZipFileInput(bool verify);
+
std::unique_ptr<const InstructionSetFeatures> insn_features_;
std::unique_ptr<QuickCompilerCallbacks> callbacks_;
};
@@ -354,7 +362,7 @@
ScratchFile tmp;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
- bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store);
+ bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store, false);
ASSERT_TRUE(success);
if (kCompile) { // OatWriter strips the code, regenerate to compare
@@ -480,7 +488,7 @@
ScratchFile tmp;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
- bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store);
+ bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store, false);
ASSERT_TRUE(success);
std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
@@ -494,7 +502,15 @@
EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength()));
}
-TEST_F(OatTest, DexFileInput) {
+static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>& data) {
+ // If in verify mode (= fail the verifier mode), make sure we fail early. We'll fail already
+ // because of the missing map, but that may lead to out of bounds reads.
+ if (verify) {
+ const_cast<DexFile::Header*>(&data->GetHeader())->checksum_++;
+ }
+}
+
+void OatTest::TestDexFileInput(bool verify) {
TimingLogger timings("OatTest::DexFileInput", false, false);
std::vector<const char*> input_filenames;
@@ -504,6 +520,9 @@
builder1.AddField("Lsome.TestClass;", "int", "someField");
builder1.AddMethod("Lsome.TestClass;", "()I", "foo");
std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file1_data);
+
bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
dex_file1_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -516,6 +535,9 @@
builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file2_data);
+
success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
dex_file2_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -526,7 +548,14 @@
ScratchFile oat_file;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
- success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store);
+ success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
+
+ // In verify mode, we expect failure.
+ if (verify) {
+ ASSERT_FALSE(success);
+ return;
+ }
+
ASSERT_TRUE(success);
std::string error_msg;
@@ -557,7 +586,15 @@
ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation());
}
-TEST_F(OatTest, ZipFileInput) {
+TEST_F(OatTest, DexFileInputCheckOutput) {
+ TestDexFileInput(false);
+}
+
+TEST_F(OatTest, DexFileInputCheckVerifier) {
+ TestDexFileInput(true);
+}
+
+void OatTest::TestZipFileInput(bool verify) {
TimingLogger timings("OatTest::DexFileInput", false, false);
ScratchFile zip_file;
@@ -568,6 +605,9 @@
builder1.AddField("Lsome.TestClass;", "long", "someField");
builder1.AddMethod("Lsome.TestClass;", "()D", "foo");
std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file1_data);
+
bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
dex_file1_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -583,6 +623,9 @@
builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file2_data);
+
success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
dex_file2_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -603,37 +646,42 @@
std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4]
ScratchFile oat_file;
- success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store);
- ASSERT_TRUE(success);
+ success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
- std::string error_msg;
- std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
- oat_file.GetFilename(),
- nullptr,
- nullptr,
- false,
- nullptr,
- &error_msg));
- ASSERT_TRUE(opened_oat_file != nullptr);
- ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
- std::unique_ptr<const DexFile> opened_dex_file1 =
- opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
- std::unique_ptr<const DexFile> opened_dex_file2 =
- opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+ if (verify) {
+ ASSERT_FALSE(success);
+ } else {
+ ASSERT_TRUE(success);
- ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
- &opened_dex_file1->GetHeader(),
- dex_file1_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
- opened_dex_file1->GetLocation());
+ std::string error_msg;
+ std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+ oat_file.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(opened_oat_file != nullptr);
+ ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+ std::unique_ptr<const DexFile> opened_dex_file1 =
+ opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> opened_dex_file2 =
+ opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
- ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
- &opened_dex_file2->GetHeader(),
- dex_file2_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
- opened_dex_file2->GetLocation());
+ ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+ &opened_dex_file1->GetHeader(),
+ dex_file1_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ opened_dex_file1->GetLocation());
+
+ ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+ &opened_dex_file2->GetHeader(),
+ dex_file2_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ opened_dex_file2->GetLocation());
+ }
}
{
@@ -645,38 +693,51 @@
success = WriteElf(oat_file.GetFile(),
std::move(zip_fd),
zip_file.GetFilename().c_str(),
- key_value_store);
- ASSERT_TRUE(success);
+ key_value_store,
+ verify);
+ if (verify) {
+ ASSERT_FALSE(success);
+ } else {
+ ASSERT_TRUE(success);
- std::string error_msg;
- std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
- oat_file.GetFilename(),
- nullptr,
- nullptr,
- false,
- nullptr,
- &error_msg));
- ASSERT_TRUE(opened_oat_file != nullptr);
- ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
- std::unique_ptr<const DexFile> opened_dex_file1 =
- opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
- std::unique_ptr<const DexFile> opened_dex_file2 =
- opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+ std::string error_msg;
+ std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+ oat_file.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(opened_oat_file != nullptr);
+ ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+ std::unique_ptr<const DexFile> opened_dex_file1 =
+ opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> opened_dex_file2 =
+ opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
- ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
- &opened_dex_file1->GetHeader(),
- dex_file1_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
- opened_dex_file1->GetLocation());
+ ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+ &opened_dex_file1->GetHeader(),
+ dex_file1_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ opened_dex_file1->GetLocation());
- ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
- &opened_dex_file2->GetHeader(),
- dex_file2_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
- opened_dex_file2->GetLocation());
+ ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+ &opened_dex_file2->GetHeader(),
+ dex_file2_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ opened_dex_file2->GetLocation());
+ }
}
}
+TEST_F(OatTest, ZipFileInputCheckOutput) {
+ TestZipFileInput(false);
+}
+
+TEST_F(OatTest, ZipFileInputCheckVerifier) {
+ TestZipFileInput(true);
+}
+
} // namespace art
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 569e0f4..90ac499 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -397,6 +397,7 @@
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
SafeMap<std::string, std::string>* key_value_store,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
CHECK(write_state_ == WriteState::kAddingDexFileSources);
@@ -424,7 +425,7 @@
}
if (!WriteOatDexFiles(rodata) ||
!ExtendForTypeLookupTables(rodata, file, size_after_type_lookup_tables) ||
- !OpenDexFiles(file, &dex_files_map, &dex_files) ||
+ !OpenDexFiles(file, verify, &dex_files_map, &dex_files) ||
!WriteTypeLookupTables(dex_files_map.get(), dex_files)) {
return false;
}
@@ -2143,6 +2144,7 @@
bool OatWriter::OpenDexFiles(
File* file,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
TimingLogger::ScopedTiming split("OpenDexFiles", timings_);
@@ -2201,9 +2203,11 @@
oat_dex_file.GetLocation(),
oat_dex_file.dex_file_location_checksum_,
/* oat_dex_file */ nullptr,
+ verify,
&error_msg));
if (dex_files.back() == nullptr) {
- LOG(ERROR) << "Failed to open dex file from oat file. File:" << oat_dex_file.GetLocation();
+ LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation()
+ << " Error: " << error_msg;
return false;
}
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index d681998..14c6d50 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -139,12 +139,15 @@
CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
dchecked_vector<const char*> GetSourceLocations() const;
- // Write raw dex files to the .rodata section and open them from the oat file.
+ // Write raw dex files to the .rodata section and open them from the oat file. The verify
+ // setting dictates whether the dex file verifier should check the dex files. This is generally
+ // the case, and should only be false for tests.
bool WriteAndOpenDexFiles(OutputStream* rodata,
File* file,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
SafeMap<std::string, std::string>* key_value_store,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
// Prepare layout of remaining data.
@@ -258,6 +261,7 @@
bool WriteOatDexFiles(OutputStream* rodata);
bool ExtendForTypeLookupTables(OutputStream* rodata, File* file, size_t offset);
bool OpenDexFiles(File* file,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
bool WriteTypeLookupTables(MemMap* opened_dex_files_map,
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
deleted file mode 100644
index f0cafc8..0000000
--- a/compiler/optimizing/boolean_simplifier.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "boolean_simplifier.h"
-
-namespace art {
-
-void HBooleanSimplifier::TryRemovingNegatedCondition(HBasicBlock* block) {
- DCHECK(block->EndsWithIf());
-
- // Check if the condition is a Boolean negation.
- HIf* if_instruction = block->GetLastInstruction()->AsIf();
- HInstruction* boolean_not = if_instruction->InputAt(0);
- if (!boolean_not->IsBooleanNot()) {
- return;
- }
-
- // Make BooleanNot's input the condition of the If and swap branches.
- if_instruction->ReplaceInput(boolean_not->InputAt(0), 0);
- block->SwapSuccessors();
-
- // Remove the BooleanNot if it is now unused.
- if (!boolean_not->HasUses()) {
- boolean_not->GetBlock()->RemoveInstruction(boolean_not);
- }
-}
-
-// Returns true if 'block1' and 'block2' are empty, merge into the same single
-// successor and the successor can only be reached from them.
-static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
- if (!block1->IsSingleGoto() || !block2->IsSingleGoto()) return false;
- HBasicBlock* succ1 = block1->GetSuccessors()[0];
- HBasicBlock* succ2 = block2->GetSuccessors()[0];
- return succ1 == succ2 && succ1->GetPredecessors().size() == 2u;
-}
-
-// Returns true if the outcome of the branching matches the boolean value of
-// the branching condition.
-static bool PreservesCondition(HInstruction* input_true, HInstruction* input_false) {
- return input_true->IsIntConstant() && input_true->AsIntConstant()->IsOne()
- && input_false->IsIntConstant() && input_false->AsIntConstant()->IsZero();
-}
-
-// Returns true if the outcome of the branching is exactly opposite of the
-// boolean value of the branching condition.
-static bool NegatesCondition(HInstruction* input_true, HInstruction* input_false) {
- return input_true->IsIntConstant() && input_true->AsIntConstant()->IsZero()
- && input_false->IsIntConstant() && input_false->AsIntConstant()->IsOne();
-}
-
-void HBooleanSimplifier::TryRemovingBooleanSelection(HBasicBlock* block) {
- DCHECK(block->EndsWithIf());
-
- // Find elements of the pattern.
- HIf* if_instruction = block->GetLastInstruction()->AsIf();
- HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
- HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
- if (!BlocksDoMergeTogether(true_block, false_block)) {
- return;
- }
- HBasicBlock* merge_block = true_block->GetSuccessors()[0];
- if (!merge_block->HasSinglePhi()) {
- return;
- }
- HPhi* phi = merge_block->GetFirstPhi()->AsPhi();
- HInstruction* true_value = phi->InputAt(merge_block->GetPredecessorIndexOf(true_block));
- HInstruction* false_value = phi->InputAt(merge_block->GetPredecessorIndexOf(false_block));
-
- // Check if the selection negates/preserves the value of the condition and
- // if so, generate a suitable replacement instruction.
- HInstruction* if_condition = if_instruction->InputAt(0);
-
- // Don't change FP compares. The definition of compares involving NaNs forces
- // the compares to be done as written by the user.
- if (if_condition->IsCondition() &&
- Primitive::IsFloatingPointType(if_condition->InputAt(0)->GetType())) {
- return;
- }
-
- HInstruction* replacement;
- if (NegatesCondition(true_value, false_value)) {
- replacement = graph_->InsertOppositeCondition(if_condition, if_instruction);
- } else if (PreservesCondition(true_value, false_value)) {
- replacement = if_condition;
- } else {
- return;
- }
-
- // Replace the selection outcome with the new instruction.
- phi->ReplaceWith(replacement);
- merge_block->RemovePhi(phi);
-
- // Delete the true branch and merge the resulting chain of blocks
- // 'block->false_block->merge_block' into one.
- true_block->DisconnectAndDelete();
- block->MergeWith(false_block);
- block->MergeWith(merge_block);
-
- // No need to update any dominance information, as we are simplifying
- // a simple diamond shape, where the join block is merged with the
- // entry block. Any following blocks would have had the join block
- // as a dominator, and `MergeWith` handles changing that to the
- // entry block.
-}
-
-void HBooleanSimplifier::Run() {
- // Iterate in post order in the unlikely case that removing one occurrence of
- // the selection pattern empties a branch block of another occurrence.
- // Otherwise the order does not matter.
- for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
- if (!block->EndsWithIf()) continue;
-
- // If condition is negated, remove the negation and swap the branches.
- TryRemovingNegatedCondition(block);
-
- // If this is a boolean-selection diamond pattern, replace its result with
- // the condition value (or its negation) and simplify the graph.
- TryRemovingBooleanSelection(block);
- }
-}
-
-} // namespace art
diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h
deleted file mode 100644
index e12a12c..0000000
--- a/compiler/optimizing/boolean_simplifier.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// This optimization recognizes two common patterns:
-// (a) Boolean selection: Casting a boolean to an integer or negating it is
-// carried out with an If statement selecting from zero/one integer
-// constants. Because Boolean values are represented as zero/one, the
-// pattern can be replaced with the condition instruction itself or its
-// negation, depending on the layout.
-// (b) Negated condition: Instruction simplifier may replace an If's condition
-// with a boolean value. If this value is the result of a Boolean negation,
-// the true/false branches can be swapped and negation removed.
-
-// Example: Negating a boolean value
-// B1:
-// z1 ParameterValue
-// i2 IntConstant 0
-// i3 IntConstant 1
-// v4 Goto B2
-// B2:
-// z5 NotEquals [ z1 i2 ]
-// v6 If [ z5 ] then B3 else B4
-// B3:
-// v7 Goto B5
-// B4:
-// v8 Goto B5
-// B5:
-// i9 Phi [ i3 i2 ]
-// v10 Return [ i9 ]
-// turns into
-// B1:
-// z1 ParameterValue
-// i2 IntConstant 0
-// v4 Goto B2
-// B2:
-// z11 Equals [ z1 i2 ]
-// v10 Return [ z11 ]
-// B3, B4, B5: removed
-
-// Note: in order to recognize empty blocks, this optimization must be run
-// after the instruction simplifier has removed redundant suspend checks.
-
-#ifndef ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
-#define ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
-
-#include "optimization.h"
-
-namespace art {
-
-class HBooleanSimplifier : public HOptimization {
- public:
- explicit HBooleanSimplifier(HGraph* graph)
- : HOptimization(graph, kBooleanSimplifierPassName) {}
-
- void Run() OVERRIDE;
-
- static constexpr const char* kBooleanSimplifierPassName = "boolean_simplifier";
-
- private:
- void TryRemovingNegatedCondition(HBasicBlock* block);
- void TryRemovingBooleanSelection(HBasicBlock* block);
-
- DISALLOW_COPY_AND_ASSIGN(HBooleanSimplifier);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_OPTIMIZING_BOOLEAN_SIMPLIFIER_H_
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 3721813..c7430e7 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -798,7 +798,7 @@
ArtMethod* HGraphBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
+ StackHandleScope<3> hs(soa.Self());
ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
@@ -833,31 +833,56 @@
}
// We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not.
- // We need to look at the referrer's super class vtable.
+ // We need to look at the referrer's super class vtable. We need to do this to know if we need to
+ // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
+ // which require runtime handling.
if (invoke_type == kSuper) {
if (compiling_class.Get() == nullptr) {
- // Invoking a super method requires knowing the actual super class. If we did not resolve
- // the compiling method's declaring class (which only happens for ahead of time compilation),
- // bail out.
+ // We could not determine the method's class we need to wait until runtime.
DCHECK(Runtime::Current()->IsAotCompiler());
return nullptr;
}
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- ArtMethod* actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
- vtable_index, class_linker->GetImagePointerSize());
- if (actual_method != resolved_method &&
- !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
- // TODO: The actual method could still be referenced in the current dex file, so we
- // could try locating it.
- // TODO: Remove the dex_file restriction.
+ ArtMethod* current_method = graph_->GetArtMethod();
+ DCHECK(current_method != nullptr);
+ Handle<mirror::Class> methods_class(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->ResolveReferencedClassOfMethod(Thread::Current(),
+ method_idx,
+ current_method)));
+ if (methods_class.Get() == nullptr) {
+ // Invoking a super method requires knowing the actual super class. If we did not resolve
+ // the compiling method's declaring class (which only happens for ahead of time
+ // compilation), bail out.
+ DCHECK(Runtime::Current()->IsAotCompiler());
return nullptr;
+ } else {
+ ArtMethod* actual_method;
+ if (methods_class->IsInterface()) {
+ actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
+ resolved_method, class_linker->GetImagePointerSize());
+ } else {
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ actual_method = compiling_class->GetSuperClass()->GetVTableEntry(
+ vtable_index, class_linker->GetImagePointerSize());
+ }
+ if (actual_method != resolved_method &&
+ !IsSameDexFile(*actual_method->GetDexFile(), *dex_compilation_unit_->GetDexFile())) {
+ // The back-end code generator relies on this check in order to ensure that it will not
+ // attempt to read the dex_cache with a dex_method_index that is not from the correct
+ // dex_file. If we didn't do this check then the dex_method_index will not be updated in the
+ // builder, which means that the code-generator (and compiler driver during sharpening and
+ // inliner, maybe) might invoke an incorrect method.
+ // TODO: The actual method could still be referenced in the current dex file, so we
+ // could try locating it.
+ // TODO: Remove the dex_file restriction.
+ return nullptr;
+ }
+ if (!actual_method->IsInvokable()) {
+ // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
+ // could resolve the callee to the wrong method.
+ return nullptr;
+ }
+ resolved_method = actual_method;
}
- if (!actual_method->IsInvokable()) {
- // Fail if the actual method cannot be invoked. Otherwise, the runtime resolution stub
- // could resolve the callee to the wrong method.
- return nullptr;
- }
- resolved_method = actual_method;
}
// Check for incompatible class changes. The class linker has a fast path for
@@ -923,7 +948,7 @@
ArtMethod* resolved_method = ResolveMethod(method_idx, invoke_type);
- if (resolved_method == nullptr) {
+ if (UNLIKELY(resolved_method == nullptr)) {
MaybeRecordStat(MethodCompilationStat::kUnresolvedMethod);
HInvoke* invoke = new (arena_) HInvokeUnresolved(arena_,
number_of_arguments,
@@ -943,7 +968,6 @@
// Potential class initialization check, in the case of a static method call.
HClinitCheck* clinit_check = nullptr;
HInvoke* invoke = nullptr;
-
if (invoke_type == kDirect || invoke_type == kStatic || invoke_type == kSuper) {
// By default, consider that the called method implicitly requires
// an initialization check of its declaring method.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 5f00f0a..c2d9edd 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1285,11 +1285,9 @@
}
void CodeGeneratorARM::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
- if (Primitive::Is64BitType(dst_type)) {
- Move64(dst, src);
- } else {
- Move32(dst, src);
- }
+ HParallelMove move(GetGraph()->GetArena());
+ move.AddMove(src, dst, dst_type, nullptr);
+ GetMoveResolver()->EmitNativeCode(&move);
}
void CodeGeneratorARM::AddLocationAsTemp(Location location, LocationSummary* locations) {
@@ -1612,6 +1610,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderARM::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorARM::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ Label false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -1632,7 +1656,7 @@
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
break;
@@ -1641,7 +1665,7 @@
case Primitive::kPrimDouble:
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetInAt(1, Location::RequiresFpuRegister());
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
break;
@@ -1649,14 +1673,14 @@
default:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
}
void InstructionCodeGeneratorARM::HandleCondition(HCondition* cond) {
- if (!cond->NeedsMaterialization()) {
+ if (cond->IsEmittedAtUseSite()) {
return;
}
@@ -4973,6 +4997,8 @@
if (source.IsRegister()) {
if (destination.IsRegister()) {
__ Mov(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ } else if (destination.IsFpuRegister()) {
+ __ vmovsr(destination.AsFpuRegister<SRegister>(), source.AsRegister<Register>());
} else {
DCHECK(destination.IsStackSlot());
__ StoreToOffset(kStoreWord, source.AsRegister<Register>(),
@@ -4990,7 +5016,9 @@
__ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
}
} else if (source.IsFpuRegister()) {
- if (destination.IsFpuRegister()) {
+ if (destination.IsRegister()) {
+ __ vmovrs(destination.AsRegister<Register>(), source.AsFpuRegister<SRegister>());
+ } else if (destination.IsFpuRegister()) {
__ vmovs(destination.AsFpuRegister<SRegister>(), source.AsFpuRegister<SRegister>());
} else {
DCHECK(destination.IsStackSlot());
@@ -5014,6 +5042,10 @@
if (destination.IsRegisterPair()) {
__ Mov(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
__ Mov(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
+ } else if (destination.IsFpuRegisterPair()) {
+ __ vmovdrr(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
+ source.AsRegisterPairLow<Register>(),
+ source.AsRegisterPairHigh<Register>());
} else {
DCHECK(destination.IsDoubleStackSlot()) << destination;
DCHECK(ExpectedPairLayout(source));
@@ -5021,7 +5053,11 @@
kStoreWordPair, source.AsRegisterPairLow<Register>(), SP, destination.GetStackIndex());
}
} else if (source.IsFpuRegisterPair()) {
- if (destination.IsFpuRegisterPair()) {
+ if (destination.IsRegisterPair()) {
+ __ vmovrrd(destination.AsRegisterPairLow<Register>(),
+ destination.AsRegisterPairHigh<Register>(),
+ FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
+ } else if (destination.IsFpuRegisterPair()) {
__ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
} else {
@@ -6599,6 +6635,29 @@
}
}
+void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kArmPointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kArmPointerSize).Uint32Value();
+ }
+ __ LoadFromOffset(kLoadWord,
+ locations->Out().AsRegister<Register>(),
+ locations->InAt(0).AsRegister<Register>(),
+ method_offset);
+}
+
#undef __
#undef QUICK_ENTRY_POINT
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index d56de0b..4179fab 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2481,13 +2481,13 @@
locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
}
- if (instruction->NeedsMaterialization()) {
+ if (!instruction->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
void InstructionCodeGeneratorARM64::HandleCondition(HCondition* instruction) {
- if (!instruction->NeedsMaterialization()) {
+ if (instruction->IsEmittedAtUseSite()) {
return;
}
@@ -2975,6 +2975,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderARM64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ vixl::Label false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -4954,6 +4980,29 @@
__ Bind(slow_path->GetExitLabel());
}
+void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kArm64PointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kArm64PointerSize).Uint32Value();
+ }
+ __ Ldr(XRegisterFrom(locations->Out()),
+ MemOperand(XRegisterFrom(locations->InAt(0)), method_offset));
+}
+
+
+
#undef __
#undef QUICK_ENTRY_POINT
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 5bd136a..fa119bb 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -614,6 +614,31 @@
Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ false);
} else if (loc1.IsDoubleStackSlot() && loc2.IsDoubleStackSlot()) {
Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ true);
+ } else if ((loc1.IsRegister() && loc2.IsStackSlot()) ||
+ (loc1.IsStackSlot() && loc2.IsRegister())) {
+ Register reg = loc1.IsRegister() ? loc1.AsRegister<Register>()
+ : loc2.AsRegister<Register>();
+ intptr_t offset = loc1.IsStackSlot() ? loc1.GetStackIndex()
+ : loc2.GetStackIndex();
+ __ Move(TMP, reg);
+ __ LoadFromOffset(kLoadWord, reg, SP, offset);
+ __ StoreToOffset(kStoreWord, TMP, SP, offset);
+ } else if ((loc1.IsRegisterPair() && loc2.IsDoubleStackSlot()) ||
+ (loc1.IsDoubleStackSlot() && loc2.IsRegisterPair())) {
+ Register reg_l = loc1.IsRegisterPair() ? loc1.AsRegisterPairLow<Register>()
+ : loc2.AsRegisterPairLow<Register>();
+ Register reg_h = loc1.IsRegisterPair() ? loc1.AsRegisterPairHigh<Register>()
+ : loc2.AsRegisterPairHigh<Register>();
+ intptr_t offset_l = loc1.IsDoubleStackSlot() ? loc1.GetStackIndex()
+ : loc2.GetStackIndex();
+ intptr_t offset_h = loc1.IsDoubleStackSlot() ? loc1.GetHighStackIndex(kMipsWordSize)
+ : loc2.GetHighStackIndex(kMipsWordSize);
+ __ Move(TMP, reg_l);
+ __ LoadFromOffset(kLoadWord, reg_l, SP, offset_l);
+ __ StoreToOffset(kStoreWord, TMP, SP, offset_l);
+ __ Move(TMP, reg_h);
+ __ LoadFromOffset(kLoadWord, reg_h, SP, offset_h);
+ __ StoreToOffset(kStoreWord, TMP, SP, offset_h);
} else {
LOG(FATAL) << "Swap between " << loc1 << " and " << loc2 << " is unsupported";
}
@@ -2245,13 +2270,13 @@
locations->SetInAt(1, Location::RequiresFpuRegister());
break;
}
- if (instruction->NeedsMaterialization()) {
+ if (!instruction->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
void InstructionCodeGeneratorMIPS::HandleCondition(HCondition* instruction) {
- if (!instruction->NeedsMaterialization()) {
+ if (instruction->IsEmittedAtUseSite()) {
return;
}
@@ -3381,6 +3406,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderMIPS::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorMIPS::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ MipsLabel false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -5236,6 +5287,14 @@
codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
}
+void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips";
+}
+
+void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips";
+}
+
#undef __
#undef QUICK_ENTRY_POINT
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index e3115f4..cefcb95 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -106,7 +106,7 @@
}
#define __ down_cast<CodeGeneratorMIPS64*>(codegen)->GetAssembler()->
-#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, x).Int32Value()
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, x).Int32Value()
class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
@@ -437,7 +437,7 @@
#undef __
#define __ down_cast<Mips64Assembler*>(GetAssembler())->
-#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, x).Int32Value()
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, x).Int32Value()
void CodeGeneratorMIPS64::Finalize(CodeAllocator* allocator) {
// Ensure that we fix up branches.
@@ -486,12 +486,12 @@
void ParallelMoveResolverMIPS64::RestoreScratch(int reg) {
// Pop reg
__ Ld(GpuRegister(reg), SP, 0);
- __ DecreaseFrameSize(kMips64WordSize);
+ __ DecreaseFrameSize(kMips64DoublewordSize);
}
void ParallelMoveResolverMIPS64::SpillScratch(int reg) {
// Push reg
- __ IncreaseFrameSize(kMips64WordSize);
+ __ IncreaseFrameSize(kMips64DoublewordSize);
__ Sd(GpuRegister(reg), SP, 0);
}
@@ -503,7 +503,7 @@
// automatically unspilled when the scratch scope object is destroyed).
ScratchRegisterScope ensure_scratch(this, TMP, V0, codegen_->GetNumberOfCoreRegisters());
// If V0 spills onto the stack, SP-relative offsets need to be adjusted.
- int stack_offset = ensure_scratch.IsSpilled() ? kMips64WordSize : 0;
+ int stack_offset = ensure_scratch.IsSpilled() ? kMips64DoublewordSize : 0;
__ LoadFromOffset(load_type,
GpuRegister(ensure_scratch.GetRegister()),
SP,
@@ -562,7 +562,7 @@
for (int i = arraysize(kCoreCalleeSaves) - 1; i >= 0; --i) {
GpuRegister reg = kCoreCalleeSaves[i];
if (allocated_registers_.ContainsCoreRegister(reg)) {
- ofs -= kMips64WordSize;
+ ofs -= kMips64DoublewordSize;
__ Sd(reg, SP, ofs);
__ cfi().RelOffset(DWARFReg(reg), ofs);
}
@@ -571,7 +571,7 @@
for (int i = arraysize(kFpuCalleeSaves) - 1; i >= 0; --i) {
FpuRegister reg = kFpuCalleeSaves[i];
if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
- ofs -= kMips64WordSize;
+ ofs -= kMips64DoublewordSize;
__ Sdc1(reg, SP, ofs);
// TODO: __ cfi().RelOffset(DWARFReg(reg), ofs);
}
@@ -609,7 +609,7 @@
FpuRegister reg = kFpuCalleeSaves[i];
if (allocated_registers_.ContainsFloatingPointRegister(reg)) {
__ Ldc1(reg, SP, ofs);
- ofs += kMips64WordSize;
+ ofs += kMips64DoublewordSize;
// TODO: __ cfi().Restore(DWARFReg(reg));
}
}
@@ -618,7 +618,7 @@
GpuRegister reg = kCoreCalleeSaves[i];
if (allocated_registers_.ContainsCoreRegister(reg)) {
__ Ld(reg, SP, ofs);
- ofs += kMips64WordSize;
+ ofs += kMips64DoublewordSize;
__ cfi().Restore(DWARFReg(reg));
}
}
@@ -976,7 +976,7 @@
__ LoadFromOffset(kLoadDoubleword,
card,
TR,
- Thread::CardTableOffset<kMips64WordSize>().Int32Value());
+ Thread::CardTableOffset<kMips64DoublewordSize>().Int32Value());
__ Dsrl(temp, object, gc::accounting::CardTable::kCardShift);
__ Daddu(temp, card, temp);
__ Sb(card, temp, 0);
@@ -994,10 +994,11 @@
blocked_core_registers_[SP] = true;
blocked_core_registers_[RA] = true;
- // AT and TMP(T8) are used as temporary/scratch registers
- // (similar to how AT is used by MIPS assemblers).
+ // AT, TMP(T8) and TMP2(T3) are used as temporary/scratch
+ // registers (similar to how AT is used by MIPS assemblers).
blocked_core_registers_[AT] = true;
blocked_core_registers_[TMP] = true;
+ blocked_core_registers_[TMP2] = true;
blocked_fpu_registers_[FTMP] = true;
// Reserve suspend and thread registers.
@@ -1021,22 +1022,22 @@
size_t CodeGeneratorMIPS64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
__ StoreToOffset(kStoreDoubleword, GpuRegister(reg_id), SP, stack_index);
- return kMips64WordSize;
+ return kMips64DoublewordSize;
}
size_t CodeGeneratorMIPS64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
__ LoadFromOffset(kLoadDoubleword, GpuRegister(reg_id), SP, stack_index);
- return kMips64WordSize;
+ return kMips64DoublewordSize;
}
size_t CodeGeneratorMIPS64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
__ StoreFpuToOffset(kStoreDoubleword, FpuRegister(reg_id), SP, stack_index);
- return kMips64WordSize;
+ return kMips64DoublewordSize;
}
size_t CodeGeneratorMIPS64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
__ LoadFpuFromOffset(kLoadDoubleword, FpuRegister(reg_id), SP, stack_index);
- return kMips64WordSize;
+ return kMips64DoublewordSize;
}
void CodeGeneratorMIPS64::DumpCoreRegister(std::ostream& stream, int reg) const {
@@ -1051,7 +1052,7 @@
HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
- InvokeRuntime(GetThreadOffset<kMips64WordSize>(entrypoint).Int32Value(),
+ InvokeRuntime(GetThreadOffset<kMips64DoublewordSize>(entrypoint).Int32Value(),
instruction,
dex_pc,
slow_path);
@@ -1091,7 +1092,7 @@
__ LoadFromOffset(kLoadUnsignedHalfword,
TMP,
TR,
- Thread::ThreadFlagsOffset<kMips64WordSize>().Int32Value());
+ Thread::ThreadFlagsOffset<kMips64DoublewordSize>().Int32Value());
if (successor == nullptr) {
__ Bnezc(TMP, slow_path->GetEntryLabel());
__ Bind(slow_path->GetReturnLabel());
@@ -1872,13 +1873,13 @@
locations->SetInAt(1, Location::RequiresFpuRegister());
break;
}
- if (instruction->NeedsMaterialization()) {
+ if (!instruction->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
}
void InstructionCodeGeneratorMIPS64::HandleCondition(HCondition* instruction) {
- if (!instruction->NeedsMaterialization()) {
+ if (instruction->IsEmittedAtUseSite()) {
return;
}
@@ -2748,6 +2749,32 @@
/* false_target */ nullptr);
}
+void LocationsBuilderMIPS64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorMIPS64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ Mips64Label false_target;
+ GenerateTestAndBranch(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+}
+
void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
new (GetGraph()->GetArena()) LocationSummary(info);
}
@@ -2988,7 +3015,7 @@
invoke->GetImtIndex() % mirror::Class::kImtSize, kMips64PointerSize).Uint32Value();
Location receiver = invoke->GetLocations()->InAt(0);
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
- Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64WordSize);
+ Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64DoublewordSize);
// Set the hidden argument.
__ LoadConst32(invoke->GetLocations()->GetTemp(1).AsRegister<GpuRegister>(),
@@ -3164,7 +3191,7 @@
T9,
callee_method.AsRegister<GpuRegister>(),
ArtMethod::EntryPointFromQuickCompiledCodeOffset(
- kMips64WordSize).Int32Value());
+ kMips64DoublewordSize).Int32Value());
// T9()
__ Jalr(T9);
__ Nop();
@@ -3202,7 +3229,7 @@
size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
invoke->GetVTableIndex(), kMips64PointerSize).SizeValue();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
- Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64WordSize);
+ Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64DoublewordSize);
// temp = object->GetClass();
__ LoadFromOffset(kLoadUnsignedWord, temp, receiver, class_offset);
@@ -3280,7 +3307,7 @@
}
static int32_t GetExceptionTlsOffset() {
- return Thread::ExceptionOffset<kMips64WordSize>().Int32Value();
+ return Thread::ExceptionOffset<kMips64DoublewordSize>().Int32Value();
}
void LocationsBuilderMIPS64::VisitLoadException(HLoadException* load) {
@@ -3520,7 +3547,8 @@
if (instruction->IsStringAlloc()) {
// String is allocated through StringFactory. Call NewEmptyString entry point.
GpuRegister temp = instruction->GetLocations()->GetTemp(0).AsRegister<GpuRegister>();
- MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64WordSize);
+ MemberOffset code_offset =
+ ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMips64DoublewordSize);
__ LoadFromOffset(kLoadDoubleword, temp, TR, QUICK_ENTRY_POINT(pNewEmptyString));
__ LoadFromOffset(kLoadDoubleword, T9, temp, code_offset.Int32Value());
__ Jalr(T9);
@@ -4267,5 +4295,14 @@
}
}
+void LocationsBuilderMIPS64::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64";
+}
+
+void InstructionCodeGeneratorMIPS64::VisitClassTableGet(HClassTableGet*) {
+ UNIMPLEMENTED(FATAL) << "ClassTableGet is unimplemented on mips64";
+}
+
} // namespace mips64
} // namespace art
+
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 08e5615..c836f83 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -27,10 +27,6 @@
namespace art {
namespace mips64 {
-// Use a local definition to prevent copying mistakes.
-static constexpr size_t kMips64WordSize = kMips64PointerSize;
-
-
// InvokeDexCallingConvention registers
static constexpr GpuRegister kParameterCoreRegisters[] =
@@ -274,9 +270,9 @@
void Move(HInstruction* instruction, Location location, HInstruction* move_for) OVERRIDE;
- size_t GetWordSize() const OVERRIDE { return kMips64WordSize; }
+ size_t GetWordSize() const OVERRIDE { return kMips64DoublewordSize; }
- size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMips64WordSize; }
+ size_t GetFloatingPointSpillSlotSize() const OVERRIDE { return kMips64DoublewordSize; }
uintptr_t GetAddressOf(HBasicBlock* block) const OVERRIDE {
return assembler_.GetLabelLocation(GetLabelOf(block));
diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc
index 644a3fb..96fe2a1 100644
--- a/compiler/optimizing/code_generator_utils.cc
+++ b/compiler/optimizing/code_generator_utils.cc
@@ -96,7 +96,7 @@
}
bool IsBooleanValueOrMaterializedCondition(HInstruction* cond_input) {
- return !cond_input->IsCondition() || cond_input->AsCondition()->NeedsMaterialization();
+ return !cond_input->IsCondition() || !cond_input->IsEmittedAtUseSite();
}
} // namespace art
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 50c4ba2..18d70da 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1218,11 +1218,14 @@
}
void CodeGeneratorX86::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
- if (Primitive::Is64BitType(dst_type)) {
- Move64(dst, src);
+ HParallelMove move(GetGraph()->GetArena());
+ if (dst_type == Primitive::kPrimLong && !src.IsConstant() && !src.IsFpuRegister()) {
+ move.AddMove(src.ToLow(), dst.ToLow(), Primitive::kPrimInt, nullptr);
+ move.AddMove(src.ToHigh(), dst.ToHigh(), Primitive::kPrimInt, nullptr);
} else {
- Move32(dst, src);
+ move.AddMove(src, dst, dst_type, nullptr);
}
+ GetMoveResolver()->EmitNativeCode(&move);
}
void CodeGeneratorX86::AddLocationAsTemp(Location location, LocationSummary* locations) {
@@ -1559,10 +1562,36 @@
void InstructionCodeGeneratorX86::VisitDeoptimize(HDeoptimize* deoptimize) {
SlowPathCode* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathX86>(deoptimize);
- GenerateTestAndBranch(deoptimize,
- /* condition_input_index */ 0,
- slow_path->GetEntryLabel(),
- /* false_target */ static_cast<Label*>(nullptr));
+ GenerateTestAndBranch<Label>(deoptimize,
+ /* condition_input_index */ 0,
+ slow_path->GetEntryLabel(),
+ /* false_target */ nullptr);
+}
+
+void LocationsBuilderX86::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ Primitive::Type select_type = select->GetType();
+ HInstruction* cond = select->GetCondition();
+
+ if (Primitive::IsFloatingPointType(select_type)) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetInAt(1, Location::Any());
+ if (IsBooleanValueOrMaterializedCondition(cond)) {
+ locations->SetInAt(2, Location::Any());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorX86::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ NearLabel false_target;
+ GenerateTestAndBranch<NearLabel>(
+ select, /* condition_input_index */ 2, /* true_target */ nullptr, &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
}
void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -1628,7 +1657,7 @@
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
break;
@@ -1637,7 +1666,7 @@
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
locations->SetInAt(1, Location::RequiresFpuRegister());
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
break;
@@ -1645,7 +1674,7 @@
default:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
// We need a byte register.
locations->SetOut(Location::RegisterLocation(ECX));
}
@@ -1654,7 +1683,7 @@
}
void InstructionCodeGeneratorX86::HandleCondition(HCondition* cond) {
- if (!cond->NeedsMaterialization()) {
+ if (cond->IsEmittedAtUseSite()) {
return;
}
@@ -2657,7 +2686,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(add->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2721,7 +2754,7 @@
__ addss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ addss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -2738,7 +2771,7 @@
__ addsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (add->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = add->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ addsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -2769,7 +2802,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(sub->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -2819,7 +2856,7 @@
__ subss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ subss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -2836,7 +2873,7 @@
__ subsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (sub->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = sub->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ subsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -2879,7 +2916,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(mul->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -3000,7 +3041,7 @@
__ mulss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ mulss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -3018,7 +3059,7 @@
__ mulsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (mul->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = mul->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ mulsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -3372,7 +3413,11 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble: {
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetInAt(1, Location::Any());
+ if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
+ DCHECK(div->InputAt(1)->IsEmittedAtUseSite());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
locations->SetOut(Location::SameAsFirstInput());
break;
}
@@ -3399,7 +3444,7 @@
__ divss(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ divss(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralFloatAddress(
const_area->GetConstant()->AsFloatConstant()->GetValue(),
@@ -3416,7 +3461,7 @@
__ divsd(first.AsFpuRegister<XmmRegister>(), second.AsFpuRegister<XmmRegister>());
} else if (div->InputAt(1)->IsX86LoadFromConstantTable()) {
HX86LoadFromConstantTable* const_area = div->InputAt(1)->AsX86LoadFromConstantTable();
- DCHECK(!const_area->NeedsMaterialization());
+ DCHECK(const_area->IsEmittedAtUseSite());
__ divsd(first.AsFpuRegister<XmmRegister>(),
codegen_->LiteralDoubleAddress(
const_area->GetConstant()->AsDoubleConstant()->GetValue(),
@@ -3957,6 +4002,27 @@
void InstructionCodeGeneratorX86::VisitCurrentMethod(HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
}
+void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kX86PointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kX86PointerSize).Uint32Value();
+ }
+ __ movl(locations->Out().AsRegister<Register>(),
+ Address(locations->InAt(0).AsRegister<Register>(), method_offset));
+}
+
void LocationsBuilderX86::VisitNot(HNot* not_) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
@@ -5465,13 +5531,31 @@
if (source.IsRegister()) {
if (destination.IsRegister()) {
__ movl(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ } else if (destination.IsFpuRegister()) {
+ __ movd(destination.AsFpuRegister<XmmRegister>(), source.AsRegister<Register>());
} else {
DCHECK(destination.IsStackSlot());
__ movl(Address(ESP, destination.GetStackIndex()), source.AsRegister<Register>());
}
+ } else if (source.IsRegisterPair()) {
+ size_t elem_size = Primitive::ComponentSize(Primitive::kPrimInt);
+ // Create stack space for 2 elements.
+ __ subl(ESP, Immediate(2 * elem_size));
+ __ movl(Address(ESP, 0), source.AsRegisterPairLow<Register>());
+ __ movl(Address(ESP, elem_size), source.AsRegisterPairHigh<Register>());
+ __ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, 0));
+ // And remove the temporary stack space we allocated.
+ __ addl(ESP, Immediate(2 * elem_size));
} else if (source.IsFpuRegister()) {
- if (destination.IsFpuRegister()) {
+ if (destination.IsRegister()) {
+ __ movd(destination.AsRegister<Register>(), source.AsFpuRegister<XmmRegister>());
+ } else if (destination.IsFpuRegister()) {
__ movaps(destination.AsFpuRegister<XmmRegister>(), source.AsFpuRegister<XmmRegister>());
+ } else if (destination.IsRegisterPair()) {
+ XmmRegister src_reg = source.AsFpuRegister<XmmRegister>();
+ __ movd(destination.AsRegisterPairLow<Register>(), src_reg);
+ __ psrlq(src_reg, Immediate(32));
+ __ movd(destination.AsRegisterPairHigh<Register>(), src_reg);
} else if (destination.IsStackSlot()) {
__ movss(Address(ESP, destination.GetStackIndex()), source.AsFpuRegister<XmmRegister>());
} else {
@@ -5488,7 +5572,11 @@
MoveMemoryToMemory32(destination.GetStackIndex(), source.GetStackIndex());
}
} else if (source.IsDoubleStackSlot()) {
- if (destination.IsFpuRegister()) {
+ if (destination.IsRegisterPair()) {
+ __ movl(destination.AsRegisterPairLow<Register>(), Address(ESP, source.GetStackIndex()));
+ __ movl(destination.AsRegisterPairHigh<Register>(),
+ Address(ESP, source.GetHighStackIndex(kX86WordSize)));
+ } else if (destination.IsFpuRegister()) {
__ movsd(destination.AsFpuRegister<XmmRegister>(), Address(ESP, source.GetStackIndex()));
} else {
DCHECK(destination.IsDoubleStackSlot()) << destination;
@@ -6865,7 +6953,7 @@
locations->SetInAt(1, Location::ConstantLocation(insn->GetConstant()));
// If we don't need to be materialized, we only need the inputs to be set.
- if (!insn->NeedsMaterialization()) {
+ if (insn->IsEmittedAtUseSite()) {
return;
}
@@ -6885,7 +6973,7 @@
}
void InstructionCodeGeneratorX86::VisitX86LoadFromConstantTable(HX86LoadFromConstantTable* insn) {
- if (!insn->NeedsMaterialization()) {
+ if (insn->IsEmittedAtUseSite()) {
return;
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index fd18834..6795488 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1558,10 +1558,36 @@
void InstructionCodeGeneratorX86_64::VisitDeoptimize(HDeoptimize* deoptimize) {
SlowPathCode* slow_path = deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathX86_64>(deoptimize);
- GenerateTestAndBranch(deoptimize,
- /* condition_input_index */ 0,
- slow_path->GetEntryLabel(),
- /* false_target */ static_cast<Label*>(nullptr));
+ GenerateTestAndBranch<Label>(deoptimize,
+ /* condition_input_index */ 0,
+ slow_path->GetEntryLabel(),
+ /* false_target */ nullptr);
+}
+
+void LocationsBuilderX86_64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void InstructionCodeGeneratorX86_64::VisitSelect(HSelect* select) {
+ LocationSummary* locations = select->GetLocations();
+ NearLabel false_target;
+ GenerateTestAndBranch<NearLabel>(select,
+ /* condition_input_index */ 2,
+ /* true_target */ nullptr,
+ &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
}
void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -1638,13 +1664,13 @@
locations->SetInAt(1, Location::Any());
break;
}
- if (cond->NeedsMaterialization()) {
+ if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
}
void InstructionCodeGeneratorX86_64::HandleCondition(HCondition* cond) {
- if (!cond->NeedsMaterialization()) {
+ if (cond->IsEmittedAtUseSite()) {
return;
}
@@ -3959,6 +3985,27 @@
// Nothing to do, the method is already at its location.
}
+void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ uint32_t method_offset = 0;
+ if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ method_offset = mirror::Class::EmbeddedVTableEntryOffset(
+ instruction->GetIndex(), kX86_64PointerSize).SizeValue();
+ } else {
+ method_offset = mirror::Class::EmbeddedImTableEntryOffset(
+ instruction->GetIndex() % mirror::Class::kImtSize, kX86_64PointerSize).Uint32Value();
+ }
+ __ movq(locations->Out().AsRegister<CpuRegister>(),
+ Address(locations->InAt(0).AsRegister<CpuRegister>(), method_offset));
+}
+
void LocationsBuilderX86_64::VisitNot(HNot* not_) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(not_, LocationSummary::kNoCall);
@@ -6400,8 +6447,17 @@
__ jmp(temp_reg);
}
+void CodeGeneratorX86_64::Load32BitValue(CpuRegister dest, int32_t value) {
+ if (value == 0) {
+ __ xorl(dest, dest);
+ } else {
+ __ movl(dest, Immediate(value));
+ }
+}
+
void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) {
if (value == 0) {
+ // Clears upper bits too.
__ xorl(dest, dest);
} else if (value > 0 && IsInt<32>(value)) {
// We can use a 32 bit move, as it will zero-extend and is one byte shorter.
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 9626590..318087e 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -478,8 +478,10 @@
Address LiteralInt32Address(int32_t v);
Address LiteralInt64Address(int64_t v);
- // Load a 64 bit value into a register in the most efficient manner.
+ // Load a 32/64 bit value into a register in the most efficient manner.
+ void Load32BitValue(CpuRegister dest, int32_t value);
void Load64BitValue(CpuRegister dest, int64_t value);
+
Address LiteralCaseTable(HPackedSwitch* switch_instr);
// Store a 64 bit value into a DoubleStackSlot in the most efficient manner.
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 19d63de..322a577 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -671,10 +671,10 @@
then_block->AddInstruction(new (&allocator) HReturn(constant0));
else_block->AddInstruction(new (&allocator) HReturn(constant1));
- ASSERT_TRUE(equal->NeedsMaterialization());
+ ASSERT_FALSE(equal->IsEmittedAtUseSite());
TransformToSsa(graph);
PrepareForRegisterAllocation(graph).Run();
- ASSERT_FALSE(equal->NeedsMaterialization());
+ ASSERT_TRUE(equal->IsEmittedAtUseSite());
auto hook_before_codegen = [](HGraph* graph_in) {
HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 3113677..962e77d 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -859,8 +859,12 @@
value));
}
} else if (input->GetType() == Primitive::kPrimInt
- && (input->IsPhi() || input->IsAnd() || input->IsOr() || input->IsXor())) {
- // TODO: We need a data-flow analysis to determine if the Phi or
+ && (input->IsPhi() ||
+ input->IsAnd() ||
+ input->IsOr() ||
+ input->IsXor() ||
+ input->IsSelect())) {
+ // TODO: We need a data-flow analysis to determine if the Phi or Select or
// binary operation is actually Boolean. Allow for now.
} else if (input->GetType() != Primitive::kPrimBoolean) {
AddError(StringPrintf(
@@ -893,6 +897,11 @@
HandleBooleanInput(instruction, 0);
}
+void SSAChecker::VisitSelect(HSelect* instruction) {
+ VisitInstruction(instruction);
+ HandleBooleanInput(instruction, 2);
+}
+
void SSAChecker::VisitBooleanNot(HBooleanNot* instruction) {
VisitInstruction(instruction);
HandleBooleanInput(instruction, 0);
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 2e16bfe..8724cde 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -126,6 +126,7 @@
void VisitCondition(HCondition* op) OVERRIDE;
void VisitIf(HIf* instruction) OVERRIDE;
void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
+ void VisitSelect(HSelect* instruction) OVERRIDE;
void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE;
void VisitConstant(HConstant* instruction) OVERRIDE;
void VisitBoundType(HBoundType* instruction) OVERRIDE;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 2805162..9d796c1 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -507,6 +507,7 @@
|| IsPass(HDeadCodeElimination::kFinalDeadCodeEliminationPassName)
|| IsPass(HDeadCodeElimination::kInitialDeadCodeEliminationPassName)
|| IsPass(BoundsCheckElimination::kBoundsCheckEliminationPassName)
+ || IsPass(RegisterAllocator::kRegisterAllocatorPassName)
|| IsPass(SsaBuilder::kSsaBuilderPassName)) {
HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
if (info == nullptr) {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 2e79df1..a839d2d 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -21,6 +21,8 @@
#include "class_linker.h"
#include "constant_folding.h"
#include "dead_code_elimination.h"
+#include "dex/verified_method.h"
+#include "dex/verification_results.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
@@ -32,13 +34,12 @@
#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
+#include "quick/inline_method_analyser.h"
#include "sharpening.h"
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
-#include "dex/verified_method.h"
-#include "dex/verification_results.h"
namespace art {
@@ -296,9 +297,29 @@
return false;
}
+HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
+ HInstruction* receiver,
+ uint32_t dex_pc) const {
+ ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
+ DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
+ return new (graph_->GetArena()) HInstanceFieldGet(
+ receiver,
+ Primitive::kPrimNot,
+ field->GetOffset(),
+ field->IsVolatile(),
+ field->GetDexFieldIndex(),
+ field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *field->GetDexFile(),
+ handles_->NewHandle(field->GetDexCache()),
+ dex_pc);
+}
+
bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
const InlineCache& ic) {
+ DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
+ << invoke_instruction->DebugName();
+
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
uint32_t class_index = FindClassIndexIn(ic.GetMonomorphicType(), caller_dex_file);
if (class_index == DexFile::kDexNoIndex) {
@@ -328,18 +349,8 @@
}
// We successfully inlined, now add a guard.
- ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
- DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
- HInstanceFieldGet* field_get = new (graph_->GetArena()) HInstanceFieldGet(
- receiver,
- Primitive::kPrimNot,
- field->GetOffset(),
- field->IsVolatile(),
- field->GetDexFieldIndex(),
- field->GetDeclaringClass()->GetDexClassDefIndex(),
- *field->GetDexFile(),
- handles_->NewHandle(field->GetDexCache()),
- invoke_instruction->GetDexPc());
+ HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
+ class_linker, receiver, invoke_instruction->GetDexPc());
bool is_referrer =
(ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
@@ -351,16 +362,16 @@
/* needs_access_check */ false,
/* is_in_dex_cache */ true);
- HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, field_get);
+ HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
compare, invoke_instruction->GetDexPc());
// TODO: Extend reference type propagation to understand the guard.
if (cursor != nullptr) {
- bb_cursor->InsertInstructionAfter(field_get, cursor);
+ bb_cursor->InsertInstructionAfter(receiver_class, cursor);
} else {
- bb_cursor->InsertInstructionBefore(field_get, bb_cursor->GetFirstInstruction());
+ bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
}
- bb_cursor->InsertInstructionAfter(load_class, field_get);
+ bb_cursor->InsertInstructionAfter(load_class, receiver_class);
bb_cursor->InsertInstructionAfter(compare, load_class);
bb_cursor->InsertInstructionAfter(deoptimize, compare);
deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
@@ -374,13 +385,101 @@
return true;
}
-bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction ATTRIBUTE_UNUSED,
+bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
- const InlineCache& ic ATTRIBUTE_UNUSED) {
- // TODO
- VLOG(compiler) << "Unimplemented polymorphic inlining for "
- << PrettyMethod(resolved_method);
- return false;
+ const InlineCache& ic) {
+ DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
+ << invoke_instruction->DebugName();
+ // This optimization only works under JIT for now.
+ DCHECK(Runtime::Current()->UseJit());
+ if (graph_->GetInstructionSet() == kMips || graph_->GetInstructionSet() == kMips64) {
+ // TODO: Support HClassTableGet for mips and mips64.
+ return false;
+ }
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+
+ DCHECK(resolved_method != nullptr);
+ ArtMethod* actual_method = nullptr;
+ // Check whether we are actually calling the same method among
+ // the different types seen.
+ for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+ if (ic.GetTypeAt(i) == nullptr) {
+ break;
+ }
+ ArtMethod* new_method = nullptr;
+ if (invoke_instruction->IsInvokeInterface()) {
+ new_method = ic.GetTypeAt(i)->FindVirtualMethodForInterface(
+ resolved_method, pointer_size);
+ } else {
+ DCHECK(invoke_instruction->IsInvokeVirtual());
+ new_method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual(
+ resolved_method, pointer_size);
+ }
+ if (actual_method == nullptr) {
+ actual_method = new_method;
+ } else if (actual_method != new_method) {
+ // Different methods, bailout.
+ return false;
+ }
+ }
+
+ HInstruction* receiver = invoke_instruction->InputAt(0);
+ HInstruction* cursor = invoke_instruction->GetPrevious();
+ HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
+
+ if (!TryInline(invoke_instruction, actual_method, /* do_rtp */ false)) {
+ return false;
+ }
+
+ // We successfully inlined, now add a guard.
+ HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
+ class_linker, receiver, invoke_instruction->GetDexPc());
+
+ size_t method_offset = invoke_instruction->IsInvokeVirtual()
+ ? actual_method->GetVtableIndex()
+ : invoke_instruction->AsInvokeInterface()->GetImtIndex();
+
+ Primitive::Type type = Is64BitInstructionSet(graph_->GetInstructionSet())
+ ? Primitive::kPrimLong
+ : Primitive::kPrimInt;
+ HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet(
+ receiver_class,
+ type,
+ invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable,
+ method_offset,
+ invoke_instruction->GetDexPc());
+
+ HConstant* constant;
+ if (type == Primitive::kPrimLong) {
+ constant = graph_->GetLongConstant(
+ reinterpret_cast<intptr_t>(actual_method), invoke_instruction->GetDexPc());
+ } else {
+ constant = graph_->GetIntConstant(
+ reinterpret_cast<intptr_t>(actual_method), invoke_instruction->GetDexPc());
+ }
+
+ HNotEqual* compare = new (graph_->GetArena()) HNotEqual(class_table_get, constant);
+ HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
+ compare, invoke_instruction->GetDexPc());
+ // TODO: Extend reference type propagation to understand the guard.
+ if (cursor != nullptr) {
+ bb_cursor->InsertInstructionAfter(receiver_class, cursor);
+ } else {
+ bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
+ }
+ bb_cursor->InsertInstructionAfter(class_table_get, receiver_class);
+ bb_cursor->InsertInstructionAfter(compare, class_table_get);
+ bb_cursor->InsertInstructionAfter(deoptimize, compare);
+ deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+
+ // Run type propagation to get the guard typed.
+ ReferenceTypePropagation rtp_fixup(graph_, handles_);
+ rtp_fixup.Run();
+
+ MaybeRecordStat(kInlinedPolymorphicCall);
+
+ return true;
}
bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
@@ -390,6 +489,11 @@
// dex file here (though the transitivity of an inline chain would allow checking the calller).
if (!compiler_driver_->MayInline(method->GetDexFile(),
outer_compilation_unit_.GetDexFile())) {
+ if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) {
+ VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method);
+ MaybeRecordStat(kReplacedInvokeWithSimplePattern);
+ return true;
+ }
VLOG(compiler) << "Won't inline " << PrettyMethod(method) << " in "
<< outer_compilation_unit_.GetDexFile()->GetLocation() << " ("
<< caller_compilation_unit_.GetDexFile()->GetLocation() << ") from "
@@ -461,6 +565,144 @@
return true;
}
+static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
+ size_t arg_vreg_index)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ size_t input_index = 0;
+ for (size_t i = 0; i < arg_vreg_index; ++i, ++input_index) {
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ if (Primitive::Is64BitType(invoke_instruction->InputAt(input_index)->GetType())) {
+ ++i;
+ DCHECK_NE(i, arg_vreg_index);
+ }
+ }
+ DCHECK_LT(input_index, invoke_instruction->GetNumberOfArguments());
+ return invoke_instruction->InputAt(input_index);
+}
+
+// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool do_rtp) {
+ InlineMethod inline_method;
+ if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) {
+ return false;
+ }
+
+ HInstruction* return_replacement = nullptr;
+ switch (inline_method.opcode) {
+ case kInlineOpNop:
+ DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid);
+ break;
+ case kInlineOpReturnArg:
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
+ inline_method.d.return_data.arg);
+ break;
+ case kInlineOpNonWideConst:
+ if (resolved_method->GetShorty()[0] == 'L') {
+ DCHECK_EQ(inline_method.d.data, 0u);
+ return_replacement = graph_->GetNullConstant();
+ } else {
+ return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
+ }
+ break;
+ case kInlineOpIGet: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstanceFieldGet* iget = CreateInstanceFieldGet(resolved_method, data.field_idx, obj);
+ DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
+ return_replacement = iget;
+ break;
+ }
+ case kInlineOpIPut: {
+ const InlineIGetIPutData& data = inline_method.d.ifield_data;
+ if (data.method_is_static || data.object_arg != 0u) {
+ // TODO: Needs null check.
+ return false;
+ }
+ HInstruction* obj = GetInvokeInputForArgVRegIndex(invoke_instruction, data.object_arg);
+ HInstruction* value = GetInvokeInputForArgVRegIndex(invoke_instruction, data.src_arg);
+ HInstanceFieldSet* iput = CreateInstanceFieldSet(resolved_method, data.field_idx, obj, value);
+ DCHECK_EQ(iput->GetFieldOffset().Uint32Value(), data.field_offset);
+ DCHECK_EQ(iput->IsVolatile() ? 1u : 0u, data.is_volatile);
+ invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
+ if (data.return_arg_plus1 != 0u) {
+ size_t return_arg = data.return_arg_plus1 - 1u;
+ return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ UNREACHABLE();
+ }
+
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+
+HInstanceFieldGet* HInliner::CreateInstanceFieldGet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldGet* iget = new (graph_->GetArena()) HInstanceFieldGet(
+ obj,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ // Read barrier generates a runtime call in slow path and we need a valid
+ // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
+ /* dex_pc */ 0);
+ if (iget->GetType() == Primitive::kPrimNot) {
+ ReferenceTypePropagation rtp(graph_, handles_);
+ rtp.Visit(iget);
+ }
+ return iget;
+}
+
+HInstanceFieldSet* HInliner::CreateInstanceFieldSet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj,
+ HInstruction* value)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
+ size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ ArtField* resolved_field = dex_cache->GetResolvedField(field_index, pointer_size);
+ DCHECK(resolved_field != nullptr);
+ HInstanceFieldSet* iput = new (graph_->GetArena()) HInstanceFieldSet(
+ obj,
+ value,
+ resolved_field->GetTypeAsPrimitiveType(),
+ resolved_field->GetOffset(),
+ resolved_field->IsVolatile(),
+ field_index,
+ resolved_field->GetDeclaringClass()->GetDexClassDefIndex(),
+ *resolved_method->GetDexFile(),
+ dex_cache,
+ // Read barrier generates a runtime call in slow path and we need a valid
+ // dex pc for the associated stack map. 0 is bogus but valid. Bug: 26854537.
+ /* dex_pc */ 0);
+ return iput;
+}
bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HInvoke* invoke_instruction,
bool same_dex_file,
@@ -717,7 +959,14 @@
if (return_replacement != nullptr) {
DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
}
+ FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ return true;
+}
+void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInvoke* invoke_instruction,
+ HInstruction* return_replacement,
+ bool do_rtp) {
// Check the integrity of reference types and run another type propagation if needed.
if (return_replacement != nullptr) {
if (return_replacement->GetType() == Primitive::kPrimNot) {
@@ -751,8 +1000,6 @@
}
}
}
-
- return true;
}
} // namespace art
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 8de510e..0127d55 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -65,6 +65,20 @@
bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Try to recognize known simple patterns and replace invoke call with appropriate instructions.
+ bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Create a new HInstanceFieldGet.
+ HInstanceFieldGet* CreateInstanceFieldGet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj);
+ // Create a new HInstanceFieldSet.
+ HInstanceFieldSet* CreateInstanceFieldSet(ArtMethod* resolved_method,
+ uint32_t field_index,
+ HInstruction* obj,
+ HInstruction* value);
+
// Try to inline the target of a monomorphic call. If successful, the code
// in the graph will look like:
// if (receiver.getClass() != ic.GetMonomorphicType()) deopt
@@ -85,6 +99,17 @@
bool same_dex_file,
bool do_rtp = true);
+ HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker,
+ HInstruction* receiver,
+ uint32_t dex_pc) const
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void FixUpReturnReferenceType(ArtMethod* resolved_method,
+ HInvoke* invoke_instruction,
+ HInstruction* return_replacement,
+ bool do_rtp)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
HGraph* const outermost_graph_;
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 49fc8c7..7d3a723 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -76,6 +76,8 @@
void VisitSub(HSub* instruction) OVERRIDE;
void VisitUShr(HUShr* instruction) OVERRIDE;
void VisitXor(HXor* instruction) OVERRIDE;
+ void VisitSelect(HSelect* select) OVERRIDE;
+ void VisitIf(HIf* instruction) OVERRIDE;
void VisitInstanceOf(HInstanceOf* instruction) OVERRIDE;
void VisitInvoke(HInvoke* invoke) OVERRIDE;
void VisitDeoptimize(HDeoptimize* deoptimize) OVERRIDE;
@@ -559,14 +561,86 @@
}
void InstructionSimplifierVisitor::VisitBooleanNot(HBooleanNot* bool_not) {
- HInstruction* parent = bool_not->InputAt(0);
- if (parent->IsBooleanNot()) {
- HInstruction* value = parent->InputAt(0);
- // Replace (!(!bool_value)) with bool_value
- bool_not->ReplaceWith(value);
+ HInstruction* input = bool_not->InputAt(0);
+ HInstruction* replace_with = nullptr;
+
+ if (input->IsIntConstant()) {
+ // Replace !(true/false) with false/true.
+ if (input->AsIntConstant()->IsOne()) {
+ replace_with = GetGraph()->GetIntConstant(0);
+ } else {
+ DCHECK(input->AsIntConstant()->IsZero());
+ replace_with = GetGraph()->GetIntConstant(1);
+ }
+ } else if (input->IsBooleanNot()) {
+ // Replace (!(!bool_value)) with bool_value.
+ replace_with = input->InputAt(0);
+ } else if (input->IsCondition() &&
+ // Don't change FP compares. The definition of compares involving
+ // NaNs forces the compares to be done as written by the user.
+ !Primitive::IsFloatingPointType(input->InputAt(0)->GetType())) {
+ // Replace condition with its opposite.
+ replace_with = GetGraph()->InsertOppositeCondition(input->AsCondition(), bool_not);
+ }
+
+ if (replace_with != nullptr) {
+ bool_not->ReplaceWith(replace_with);
bool_not->GetBlock()->RemoveInstruction(bool_not);
- // It is possible that `parent` is dead at this point but we leave
- // its removal to DCE for simplicity.
+ RecordSimplification();
+ }
+}
+
+void InstructionSimplifierVisitor::VisitSelect(HSelect* select) {
+ HInstruction* replace_with = nullptr;
+ HInstruction* condition = select->GetCondition();
+ HInstruction* true_value = select->GetTrueValue();
+ HInstruction* false_value = select->GetFalseValue();
+
+ if (condition->IsBooleanNot()) {
+ // Change ((!cond) ? x : y) to (cond ? y : x).
+ condition = condition->InputAt(0);
+ std::swap(true_value, false_value);
+ select->ReplaceInput(false_value, 0);
+ select->ReplaceInput(true_value, 1);
+ select->ReplaceInput(condition, 2);
+ RecordSimplification();
+ }
+
+ if (true_value == false_value) {
+ // Replace (cond ? x : x) with (x).
+ replace_with = true_value;
+ } else if (condition->IsIntConstant()) {
+ if (condition->AsIntConstant()->IsOne()) {
+ // Replace (true ? x : y) with (x).
+ replace_with = true_value;
+ } else {
+ // Replace (false ? x : y) with (y).
+ DCHECK(condition->AsIntConstant()->IsZero());
+ replace_with = false_value;
+ }
+ } else if (true_value->IsIntConstant() && false_value->IsIntConstant()) {
+ if (true_value->AsIntConstant()->IsOne() && false_value->AsIntConstant()->IsZero()) {
+ // Replace (cond ? true : false) with (cond).
+ replace_with = condition;
+ } else if (true_value->AsIntConstant()->IsZero() && false_value->AsIntConstant()->IsOne()) {
+ // Replace (cond ? false : true) with (!cond).
+ replace_with = GetGraph()->InsertOppositeCondition(condition, select);
+ }
+ }
+
+ if (replace_with != nullptr) {
+ select->ReplaceWith(replace_with);
+ select->GetBlock()->RemoveInstruction(select);
+ RecordSimplification();
+ }
+}
+
+void InstructionSimplifierVisitor::VisitIf(HIf* instruction) {
+ HInstruction* condition = instruction->InputAt(0);
+ if (condition->IsBooleanNot()) {
+ // Swap successors if input is negated.
+ instruction->ReplaceInput(condition->InputAt(0), 0);
+ instruction->GetBlock()->SwapSuccessors();
RecordSimplification();
}
}
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 97fe587..e8912b3 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -807,7 +807,8 @@
}
static void CreateIntIntIntIntIntToIntPlusTemps(ArenaAllocator* arena,
- HInvoke* invoke) {
+ HInvoke* invoke,
+ Primitive::Type type) {
LocationSummary* locations = new (arena) LocationSummary(invoke,
LocationSummary::kNoCall,
kIntrinsified);
@@ -817,11 +818,15 @@
locations->SetInAt(3, Location::RequiresRegister());
locations->SetInAt(4, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ // If heap poisoning is enabled, we don't want the unpoisoning
+ // operations to potentially clobber the output.
+ Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
+ ? Location::kOutputOverlap
+ : Location::kNoOutputOverlap;
+ locations->SetOut(Location::RequiresRegister(), overlaps);
locations->AddTemp(Location::RequiresRegister()); // Pointer.
locations->AddTemp(Location::RequiresRegister()); // Temp 1.
- locations->AddTemp(Location::RequiresRegister()); // Temp 2.
}
static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM* codegen) {
@@ -856,7 +861,12 @@
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
codegen->GetAssembler()->PoisonHeapReference(expected_lo);
- codegen->GetAssembler()->PoisonHeapReference(value_lo);
+ if (value_lo == expected_lo) {
+ // Do not poison `value_lo`, as it is the same register as
+ // `expected_lo`, which has just been poisoned.
+ } else {
+ codegen->GetAssembler()->PoisonHeapReference(value_lo);
+ }
}
// do {
@@ -892,13 +902,18 @@
__ mov(out, ShifterOperand(0), CC);
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
- codegen->GetAssembler()->UnpoisonHeapReference(value_lo);
codegen->GetAssembler()->UnpoisonHeapReference(expected_lo);
+ if (value_lo == expected_lo) {
+ // Do not unpoison `value_lo`, as it is the same register as
+ // `expected_lo`, which has just been unpoisoned.
+ } else {
+ codegen->GetAssembler()->UnpoisonHeapReference(value_lo);
+ }
}
}
void IntrinsicLocationsBuilderARM::VisitUnsafeCASInt(HInvoke* invoke) {
- CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+ CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) {
// The UnsafeCASObject intrinsic is missing a read barrier, and
@@ -906,16 +921,12 @@
// Turn it off temporarily as a quick fix, until the read barrier is
// implemented (see TODO in GenCAS below).
//
- // Also, the UnsafeCASObject intrinsic does not always work when heap
- // poisoning is enabled (it breaks run-test 004-UnsafeTest); turn it
- // off temporarily as a quick fix (b/26204023).
- //
- // TODO(rpl): Fix these two issues and re-enable this intrinsic.
- if (kEmitCompilerReadBarrier || kPoisonHeapReferences) {
+ // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ if (kEmitCompilerReadBarrier) {
return;
}
- CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke);
+ CreateIntIntIntIntIntToIntPlusTemps(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) {
GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index dbd7688..5dce83a 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -275,6 +275,45 @@
GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetVIXLAssembler());
}
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenCompare(LocationSummary* locations, bool is_long, vixl::MacroAssembler* masm) {
+ Location op1 = locations->InAt(0);
+ Location op2 = locations->InAt(1);
+ Location out = locations->Out();
+
+ Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
+ Register op2_reg = is_long ? XRegisterFrom(op2) : WRegisterFrom(op2);
+ Register out_reg = WRegisterFrom(out);
+
+ __ Cmp(op1_reg, op2_reg);
+ __ Cset(out_reg, gt); // out == +1 if GT or 0 otherwise
+ __ Cinv(out_reg, out_reg, lt); // out == -1 if LT or unchanged otherwise
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerCompare(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerCompare(HInvoke* invoke) {
+ GenCompare(invoke->GetLocations(), /* is_long */ false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongCompare(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongCompare(HInvoke* invoke) {
+ GenCompare(invoke->GetLocations(), /* is_long */ true, GetVIXLAssembler());
+}
+
static void GenNumberOfLeadingZeros(LocationSummary* locations,
Primitive::Type type,
vixl::MacroAssembler* masm) {
@@ -504,15 +543,6 @@
__ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
}
-static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
- LocationSummary* locations = new (arena) LocationSummary(invoke,
- LocationSummary::kNoCall,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
void IntrinsicLocationsBuilderARM64::VisitMathMinIntInt(HInvoke* invoke) {
CreateIntIntToIntLocations(arena_, invoke);
}
@@ -967,7 +997,9 @@
codegen_);
}
-static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena,
+ HInvoke* invoke,
+ Primitive::Type type) {
LocationSummary* locations = new (arena) LocationSummary(invoke,
LocationSummary::kNoCall,
kIntrinsified);
@@ -977,7 +1009,12 @@
locations->SetInAt(3, Location::RequiresRegister());
locations->SetInAt(4, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ // If heap poisoning is enabled, we don't want the unpoisoning
+ // operations to potentially clobber the output.
+ Location::OutputOverlap overlaps = (kPoisonHeapReferences && type == Primitive::kPrimNot)
+ ? Location::kOutputOverlap
+ : Location::kNoOutputOverlap;
+ locations->SetOut(Location::RequiresRegister(), overlaps);
}
static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorARM64* codegen) {
@@ -1007,7 +1044,12 @@
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
codegen->GetAssembler()->PoisonHeapReference(expected);
- codegen->GetAssembler()->PoisonHeapReference(value);
+ if (value.Is(expected)) {
+ // Do not poison `value`, as it is the same register as
+ // `expected`, which has just been poisoned.
+ } else {
+ codegen->GetAssembler()->PoisonHeapReference(value);
+ }
}
// do {
@@ -1034,16 +1076,21 @@
__ Cset(out, eq);
if (kPoisonHeapReferences && type == Primitive::kPrimNot) {
- codegen->GetAssembler()->UnpoisonHeapReference(value);
codegen->GetAssembler()->UnpoisonHeapReference(expected);
+ if (value.Is(expected)) {
+ // Do not unpoison `value`, as it is the same register as
+ // `expected`, which has just been unpoisoned.
+ } else {
+ codegen->GetAssembler()->UnpoisonHeapReference(value);
+ }
}
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASInt(HInvoke* invoke) {
- CreateIntIntIntIntIntToInt(arena_, invoke);
+ CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimInt);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASLong(HInvoke* invoke) {
- CreateIntIntIntIntIntToInt(arena_, invoke);
+ CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimLong);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) {
// The UnsafeCASObject intrinsic is missing a read barrier, and
@@ -1051,16 +1098,12 @@
// Turn it off temporarily as a quick fix, until the read barrier is
// implemented (see TODO in GenCAS below).
//
- // Also, the UnsafeCASObject intrinsic does not always work when heap
- // poisoning is enabled (it breaks run-test 004-UnsafeTest); turn it
- // off temporarily as a quick fix (b/26204023).
- //
- // TODO(rpl): Fix these two issues and re-enable this intrinsic.
- if (kEmitCompilerReadBarrier || kPoisonHeapReferences) {
+ // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ if (kEmitCompilerReadBarrier) {
return;
}
- CreateIntIntIntIntIntToInt(arena_, invoke);
+ CreateIntIntIntIntIntToInt(arena_, invoke, Primitive::kPrimNot);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafeCASInt(HInvoke* invoke) {
@@ -1413,6 +1456,209 @@
__ Bind(slow_path->GetExitLabel());
}
+static void GenSignum(LocationSummary* locations, bool is_long, vixl::MacroAssembler* masm) {
+ Location op1 = locations->InAt(0);
+ Location out = locations->Out();
+
+ Register op1_reg = is_long ? XRegisterFrom(op1) : WRegisterFrom(op1);
+ Register out_reg = WRegisterFrom(out);
+
+ __ Cmp(op1_reg, 0);
+ __ Cset(out_reg, gt); // out == +1 if GT or 0 otherwise
+ __ Cinv(out_reg, out_reg, lt); // out == -1 if LT or unchanged otherwise
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerSignum(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerSignum(HInvoke* invoke) {
+ GenSignum(invoke->GetLocations(), /* is_long */ false, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongSignum(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongSignum(HInvoke* invoke) {
+ GenSignum(invoke->GetLocations(), /* is_long */ true, GetVIXLAssembler());
+}
+
+static void CreateFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ DCHECK_EQ(invoke->GetNumberOfArguments(), 1U);
+ DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
+ DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
+
+ LocationSummary* const locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ InvokeRuntimeCallingConvention calling_convention;
+
+ locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
+}
+
+static void CreateFPFPToFPCallLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ DCHECK_EQ(invoke->GetNumberOfArguments(), 2U);
+ DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(0)->GetType()));
+ DCHECK(Primitive::IsFloatingPointType(invoke->InputAt(1)->GetType()));
+ DCHECK(Primitive::IsFloatingPointType(invoke->GetType()));
+
+ LocationSummary* const locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kCall,
+ kIntrinsified);
+ InvokeRuntimeCallingConvention calling_convention;
+
+ locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
+ locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
+ locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
+}
+
+static void GenFPToFPCall(HInvoke* invoke,
+ vixl::MacroAssembler* masm,
+ CodeGeneratorARM64* codegen,
+ QuickEntrypointEnum entry) {
+ __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64WordSize>(entry).Int32Value()));
+ __ Blr(lr);
+ codegen->RecordPcInfo(invoke, invoke->GetDexPc());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathCos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathCos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCos);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathSin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathSin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSin);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAcos(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAcos(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAcos);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAsin(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAsin(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAsin);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAtan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAtan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathCbrt(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathCbrt(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCbrt);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathCosh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathCosh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickCosh);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathExp(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathExp(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExp);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathExpm1(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathExpm1(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickExpm1);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathLog(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathLog(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathLog10(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathLog10(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickLog10);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathSinh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathSinh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickSinh);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathTan(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathTan(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTan);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathTanh(HInvoke* invoke) {
+ CreateFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathTanh(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickTanh);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathAtan2(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathAtan2(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickAtan2);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathHypot(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathHypot(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickHypot);
+}
+
+void IntrinsicLocationsBuilderARM64::VisitMathNextAfter(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
+ GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1428,37 +1674,15 @@
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
-UNIMPLEMENTED_INTRINSIC(MathCos)
-UNIMPLEMENTED_INTRINSIC(MathSin)
-UNIMPLEMENTED_INTRINSIC(MathAcos)
-UNIMPLEMENTED_INTRINSIC(MathAsin)
-UNIMPLEMENTED_INTRINSIC(MathAtan)
-UNIMPLEMENTED_INTRINSIC(MathAtan2)
-UNIMPLEMENTED_INTRINSIC(MathCbrt)
-UNIMPLEMENTED_INTRINSIC(MathCosh)
-UNIMPLEMENTED_INTRINSIC(MathExp)
-UNIMPLEMENTED_INTRINSIC(MathExpm1)
-UNIMPLEMENTED_INTRINSIC(MathHypot)
-UNIMPLEMENTED_INTRINSIC(MathLog)
-UNIMPLEMENTED_INTRINSIC(MathLog10)
-UNIMPLEMENTED_INTRINSIC(MathNextAfter)
-UNIMPLEMENTED_INTRINSIC(MathSinh)
-UNIMPLEMENTED_INTRINSIC(MathTan)
-UNIMPLEMENTED_INTRINSIC(MathTanh)
-
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
-UNIMPLEMENTED_INTRINSIC(IntegerCompare)
-UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(IntegerSignum)
-UNIMPLEMENTED_INTRINSIC(LongSignum)
// Rotate operations are handled as HRor instructions.
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index cba84fa..f681d1f 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1429,8 +1429,7 @@
__ LoadFromOffset(kLoadDoubleword,
TMP,
TR,
- QUICK_ENTRYPOINT_OFFSET(kMips64WordSize,
- pStringCompareTo).Int32Value());
+ QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pStringCompareTo).Int32Value());
__ Jalr(TMP);
__ Nop();
__ Bind(slow_path->GetExitLabel());
@@ -1583,7 +1582,7 @@
__ LoadFromOffset(kLoadDoubleword,
TMP,
TR,
- QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pIndexOf).Int32Value());
+ QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pIndexOf).Int32Value());
__ Jalr(TMP);
__ Nop();
@@ -1659,7 +1658,8 @@
__ LoadFromOffset(kLoadDoubleword,
TMP,
TR,
- QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pAllocStringFromBytes).Int32Value());
+ QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
+ pAllocStringFromBytes).Int32Value());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
__ Jalr(TMP);
__ Nop();
@@ -1685,7 +1685,8 @@
__ LoadFromOffset(kLoadDoubleword,
TMP,
TR,
- QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pAllocStringFromChars).Int32Value());
+ QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
+ pAllocStringFromChars).Int32Value());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
__ Jalr(TMP);
__ Nop();
@@ -1716,7 +1717,8 @@
__ LoadFromOffset(kLoadDoubleword,
TMP,
TR,
- QUICK_ENTRYPOINT_OFFSET(kMips64WordSize, pAllocStringFromString).Int32Value());
+ QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize,
+ pAllocStringFromString).Int32Value());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
__ Jalr(TMP);
__ Nop();
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 6ccc5d1..51fa514 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2302,7 +2302,7 @@
}
void IntrinsicCodeGeneratorX86_64::VisitIntegerReverse(HInvoke* invoke) {
- X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler());
+ X86_64Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>();
@@ -2346,7 +2346,7 @@
}
void IntrinsicCodeGeneratorX86_64::VisitLongReverse(HInvoke* invoke) {
- X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler());
+ X86_64Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
CpuRegister reg = locations->InAt(0).AsRegister<CpuRegister>();
@@ -2382,7 +2382,10 @@
locations->SetOut(Location::RequiresRegister());
}
-static void GenBitCount(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) {
+static void GenBitCount(X86_64Assembler* assembler,
+ CodeGeneratorX86_64* codegen,
+ HInvoke* invoke,
+ bool is_long) {
LocationSummary* locations = invoke->GetLocations();
Location src = locations->InAt(0);
CpuRegister out = locations->Out().AsRegister<CpuRegister>();
@@ -2393,11 +2396,7 @@
value = is_long
? POPCOUNT(static_cast<uint64_t>(value))
: POPCOUNT(static_cast<uint32_t>(value));
- if (value == 0) {
- __ xorl(out, out);
- } else {
- __ movl(out, Immediate(value));
- }
+ codegen->Load32BitValue(out, value);
return;
}
@@ -2421,7 +2420,7 @@
}
void IntrinsicCodeGeneratorX86_64::VisitIntegerBitCount(HInvoke* invoke) {
- GenBitCount(GetAssembler(), invoke, /* is_long */ false);
+ GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ false);
}
void IntrinsicLocationsBuilderX86_64::VisitLongBitCount(HInvoke* invoke) {
@@ -2429,7 +2428,190 @@
}
void IntrinsicCodeGeneratorX86_64::VisitLongBitCount(HInvoke* invoke) {
- GenBitCount(GetAssembler(), invoke, /* is_long */ true);
+ GenBitCount(GetAssembler(), codegen_, invoke, /* is_long */ true);
+}
+
+static void CreateCompareLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+static void GenCompare(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) {
+ LocationSummary* locations = invoke->GetLocations();
+ CpuRegister src1 = locations->InAt(0).AsRegister<CpuRegister>();
+ CpuRegister src2 = locations->InAt(1).AsRegister<CpuRegister>();
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+
+ NearLabel is_lt, done;
+
+ __ xorl(out, out);
+
+ if (is_long) {
+ __ cmpq(src1, src2);
+ } else {
+ __ cmpl(src1, src2);
+ }
+ __ j(kEqual, &done);
+ __ j(kLess, &is_lt);
+
+ __ movl(out, Immediate(1));
+ __ jmp(&done);
+
+ __ Bind(&is_lt);
+ __ movl(out, Immediate(-1));
+
+ __ Bind(&done);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitIntegerCompare(HInvoke* invoke) {
+ CreateCompareLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitIntegerCompare(HInvoke* invoke) {
+ GenCompare(GetAssembler(), invoke, /* is_long */ false);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitLongCompare(HInvoke* invoke) {
+ CreateCompareLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitLongCompare(HInvoke* invoke) {
+ GenCompare(GetAssembler(), invoke, /* is_long */ true);
+}
+
+static void CreateOneBitLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_high) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+ locations->SetOut(Location::RequiresRegister());
+ locations->AddTemp(is_high ? Location::RegisterLocation(RCX) // needs CL
+ : Location::RequiresRegister()); // any will do
+}
+
+static void GenOneBit(X86_64Assembler* assembler,
+ CodeGeneratorX86_64* codegen,
+ HInvoke* invoke,
+ bool is_high, bool is_long) {
+ LocationSummary* locations = invoke->GetLocations();
+ Location src = locations->InAt(0);
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+
+ if (invoke->InputAt(0)->IsConstant()) {
+ // Evaluate this at compile time.
+ int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
+ if (value == 0) {
+ __ xorl(out, out); // Clears upper bits too.
+ return;
+ }
+ // Nonzero value.
+ if (is_high) {
+ value = is_long ? 63 - CLZ(static_cast<uint64_t>(value))
+ : 31 - CLZ(static_cast<uint32_t>(value));
+ } else {
+ value = is_long ? CTZ(static_cast<uint64_t>(value))
+ : CTZ(static_cast<uint32_t>(value));
+ }
+ if (is_long) {
+ codegen->Load64BitValue(out, 1L << value);
+ } else {
+ codegen->Load32BitValue(out, 1 << value);
+ }
+ return;
+ }
+
+ // Handle the non-constant cases.
+ CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
+ if (is_high) {
+ // Use architectural support: basically 1 << bsr.
+ if (src.IsRegister()) {
+ if (is_long) {
+ __ bsrq(tmp, src.AsRegister<CpuRegister>());
+ } else {
+ __ bsrl(tmp, src.AsRegister<CpuRegister>());
+ }
+ } else if (is_long) {
+ DCHECK(src.IsDoubleStackSlot());
+ __ bsrq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+ } else {
+ DCHECK(src.IsStackSlot());
+ __ bsrl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+ }
+ // BSR sets ZF if the input was zero.
+ NearLabel is_zero, done;
+ __ j(kEqual, &is_zero);
+ __ movl(out, Immediate(1)); // Clears upper bits too.
+ if (is_long) {
+ __ shlq(out, tmp);
+ } else {
+ __ shll(out, tmp);
+ }
+ __ jmp(&done);
+ __ Bind(&is_zero);
+ __ xorl(out, out); // Clears upper bits too.
+ __ Bind(&done);
+ } else {
+ // Copy input into temporary.
+ if (src.IsRegister()) {
+ if (is_long) {
+ __ movq(tmp, src.AsRegister<CpuRegister>());
+ } else {
+ __ movl(tmp, src.AsRegister<CpuRegister>());
+ }
+ } else if (is_long) {
+ DCHECK(src.IsDoubleStackSlot());
+ __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+ } else {
+ DCHECK(src.IsStackSlot());
+ __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+ }
+ // Do the bit twiddling: basically tmp & -tmp;
+ if (is_long) {
+ __ movq(out, tmp);
+ __ negq(tmp);
+ __ andq(out, tmp);
+ } else {
+ __ movl(out, tmp);
+ __ negl(tmp);
+ __ andl(out, tmp);
+ }
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitIntegerHighestOneBit(HInvoke* invoke) {
+ CreateOneBitLocations(arena_, invoke, /* is_high */ true);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitIntegerHighestOneBit(HInvoke* invoke) {
+ GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ true, /* is_long */ false);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitLongHighestOneBit(HInvoke* invoke) {
+ CreateOneBitLocations(arena_, invoke, /* is_high */ true);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitLongHighestOneBit(HInvoke* invoke) {
+ GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ true, /* is_long */ true);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitIntegerLowestOneBit(HInvoke* invoke) {
+ CreateOneBitLocations(arena_, invoke, /* is_high */ false);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitIntegerLowestOneBit(HInvoke* invoke) {
+ GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ false, /* is_long */ false);
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitLongLowestOneBit(HInvoke* invoke) {
+ CreateOneBitLocations(arena_, invoke, /* is_high */ false);
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitLongLowestOneBit(HInvoke* invoke) {
+ GenOneBit(GetAssembler(), codegen_, invoke, /* is_high */ false, /* is_long */ true);
}
static void CreateLeadingZeroLocations(ArenaAllocator* arena, HInvoke* invoke) {
@@ -2440,7 +2622,9 @@
locations->SetOut(Location::RequiresRegister());
}
-static void GenLeadingZeros(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) {
+static void GenLeadingZeros(X86_64Assembler* assembler,
+ CodeGeneratorX86_64* codegen,
+ HInvoke* invoke, bool is_long) {
LocationSummary* locations = invoke->GetLocations();
Location src = locations->InAt(0);
CpuRegister out = locations->Out().AsRegister<CpuRegister>();
@@ -2454,11 +2638,7 @@
} else {
value = is_long ? CLZ(static_cast<uint64_t>(value)) : CLZ(static_cast<uint32_t>(value));
}
- if (value == 0) {
- __ xorl(out, out);
- } else {
- __ movl(out, Immediate(value));
- }
+ codegen->Load32BitValue(out, value);
return;
}
@@ -2497,8 +2677,7 @@
}
void IntrinsicCodeGeneratorX86_64::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
- X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler());
- GenLeadingZeros(assembler, invoke, /* is_long */ false);
+ GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false);
}
void IntrinsicLocationsBuilderX86_64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
@@ -2506,8 +2685,7 @@
}
void IntrinsicCodeGeneratorX86_64::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
- X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler());
- GenLeadingZeros(assembler, invoke, /* is_long */ true);
+ GenLeadingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true);
}
static void CreateTrailingZeroLocations(ArenaAllocator* arena, HInvoke* invoke) {
@@ -2518,7 +2696,9 @@
locations->SetOut(Location::RequiresRegister());
}
-static void GenTrailingZeros(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) {
+static void GenTrailingZeros(X86_64Assembler* assembler,
+ CodeGeneratorX86_64* codegen,
+ HInvoke* invoke, bool is_long) {
LocationSummary* locations = invoke->GetLocations();
Location src = locations->InAt(0);
CpuRegister out = locations->Out().AsRegister<CpuRegister>();
@@ -2532,11 +2712,7 @@
} else {
value = is_long ? CTZ(static_cast<uint64_t>(value)) : CTZ(static_cast<uint32_t>(value));
}
- if (value == 0) {
- __ xorl(out, out);
- } else {
- __ movl(out, Immediate(value));
- }
+ codegen->Load32BitValue(out, value);
return;
}
@@ -2570,8 +2746,7 @@
}
void IntrinsicCodeGeneratorX86_64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
- X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler());
- GenTrailingZeros(assembler, invoke, /* is_long */ false);
+ GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ false);
}
void IntrinsicLocationsBuilderX86_64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
@@ -2579,8 +2754,75 @@
}
void IntrinsicCodeGeneratorX86_64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
- X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen_->GetAssembler());
- GenTrailingZeros(assembler, invoke, /* is_long */ true);
+ GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true);
+}
+
+static void CreateSignLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::Any());
+ locations->SetOut(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister()); // Need a writeable register.
+}
+
+static void GenSign(X86_64Assembler* assembler,
+ CodeGeneratorX86_64* codegen,
+ HInvoke* invoke, bool is_long) {
+ LocationSummary* locations = invoke->GetLocations();
+ Location src = locations->InAt(0);
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+
+ if (invoke->InputAt(0)->IsConstant()) {
+ // Evaluate this at compile time.
+ int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
+ codegen->Load32BitValue(out, value == 0 ? 0 : (value > 0 ? 1 : -1));
+ return;
+ }
+
+ // Copy input into temporary.
+ CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
+ if (src.IsRegister()) {
+ if (is_long) {
+ __ movq(tmp, src.AsRegister<CpuRegister>());
+ } else {
+ __ movl(tmp, src.AsRegister<CpuRegister>());
+ }
+ } else if (is_long) {
+ DCHECK(src.IsDoubleStackSlot());
+ __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+ } else {
+ DCHECK(src.IsStackSlot());
+ __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+ }
+
+ // Do the bit twiddling: basically tmp >> 63/31 | -tmp >>> 63/31 for long/int.
+ if (is_long) {
+ __ movq(out, tmp);
+ __ sarq(out, Immediate(63));
+ __ negq(tmp);
+ __ shrq(tmp, Immediate(63));
+ __ orq(out, tmp);
+ } else {
+ __ movl(out, tmp);
+ __ sarl(out, Immediate(31));
+ __ negl(tmp);
+ __ shrl(tmp, Immediate(31));
+ __ orl(out, tmp);
+ }
+}
+
+void IntrinsicLocationsBuilderX86_64::VisitIntegerSignum(HInvoke* invoke) {
+ CreateSignLocations(arena_, invoke);
+}
+void IntrinsicCodeGeneratorX86_64::VisitIntegerSignum(HInvoke* invoke) {
+ GenSign(GetAssembler(), codegen_, invoke, /* is_long */ false);
+}
+void IntrinsicLocationsBuilderX86_64::VisitLongSignum(HInvoke* invoke) {
+ CreateSignLocations(arena_, invoke);
+}
+void IntrinsicCodeGeneratorX86_64::VisitLongSignum(HInvoke* invoke) {
+ GenSign(GetAssembler(), codegen_, invoke, /* is_long */ true);
}
// Unimplemented intrinsics.
@@ -2598,15 +2840,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
-UNIMPLEMENTED_INTRINSIC(IntegerCompare)
-UNIMPLEMENTED_INTRINSIC(LongCompare)
-UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(IntegerSignum)
-UNIMPLEMENTED_INTRINSIC(LongSignum)
-
// Rotate operations are handled as HRor instructions.
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 926f939..991f8f7 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -278,9 +278,9 @@
// Test for the phi.
interval = liveness.GetInstructionFromSsaIndex(3)->GetLiveInterval();
range = interval->GetFirstRange();
- // Instruction is consumed by the if.
+ // Instruction is input of non-materialized Equal and hence live until If.
ASSERT_EQ(14u, range->GetStart());
- ASSERT_EQ(17u, range->GetEnd());
+ ASSERT_EQ(19u, range->GetEnd());
ASSERT_TRUE(range->GetNext() == nullptr);
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 92f758d..c057eca 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -722,6 +722,22 @@
RemoveInstruction(initial);
}
+void HBasicBlock::MoveInstructionBefore(HInstruction* insn, HInstruction* cursor) {
+ DCHECK(!cursor->IsPhi());
+ DCHECK(!insn->IsPhi());
+ DCHECK(!insn->IsControlFlow());
+ DCHECK(insn->CanBeMoved());
+ DCHECK(!insn->HasSideEffects());
+
+ HBasicBlock* from_block = insn->GetBlock();
+ HBasicBlock* to_block = cursor->GetBlock();
+ DCHECK(from_block != to_block);
+
+ from_block->RemoveInstruction(insn, /* ensure_safety */ false);
+ insn->SetBlock(to_block);
+ to_block->instructions_.InsertInstructionBefore(insn, cursor);
+}
+
static void Add(HInstructionList* instruction_list,
HBasicBlock* block,
HInstruction* instruction) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 41c2f17..b808347 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1011,6 +1011,7 @@
// Replace instruction `initial` with `replacement` within this block.
void ReplaceAndRemoveInstructionWith(HInstruction* initial,
HInstruction* replacement);
+ void MoveInstructionBefore(HInstruction* insn, HInstruction* cursor);
void AddPhi(HPhi* phi);
void InsertPhiAfter(HPhi* instruction, HPhi* cursor);
// RemoveInstruction and RemovePhi delete a given instruction from the respective
@@ -1161,6 +1162,7 @@
M(BoundsCheck, Instruction) \
M(BoundType, Instruction) \
M(CheckCast, Instruction) \
+ M(ClassTableGet, Instruction) \
M(ClearException, Instruction) \
M(ClinitCheck, Instruction) \
M(Compare, BinaryOperation) \
@@ -1220,6 +1222,7 @@
M(UnresolvedInstanceFieldSet, Instruction) \
M(UnresolvedStaticFieldGet, Instruction) \
M(UnresolvedStaticFieldSet, Instruction) \
+ M(Select, Instruction) \
M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
@@ -1819,6 +1822,7 @@
dex_pc_(dex_pc),
id_(-1),
ssa_index_(-1),
+ emitted_at_use_site_(false),
environment_(nullptr),
locations_(nullptr),
live_interval_(nullptr),
@@ -2081,6 +2085,9 @@
// The caller must ensure that this is safe to do.
void RemoveEnvironmentUsers();
+ bool IsEmittedAtUseSite() const { return emitted_at_use_site_; }
+ void MarkEmittedAtUseSite() { emitted_at_use_site_ = true; }
+
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
@@ -2102,6 +2109,10 @@
// When doing liveness analysis, instructions that have uses get an SSA index.
int ssa_index_;
+ // If set, the machine code for this instruction is assumed to be generated by
+ // its users. Used by liveness analysis to compute use positions accordingly.
+ bool emitted_at_use_site_;
+
// List of instructions that have this instruction as input.
HUseList<HInstruction*> uses_;
@@ -2542,6 +2553,44 @@
DISALLOW_COPY_AND_ASSIGN(HCurrentMethod);
};
+// Fetches an ArtMethod from the virtual table or the interface method table
+// of a class.
+class HClassTableGet : public HExpression<1> {
+ public:
+ enum TableKind {
+ kVTable,
+ kIMTable,
+ };
+ HClassTableGet(HInstruction* cls,
+ Primitive::Type type,
+ TableKind kind,
+ size_t index,
+ uint32_t dex_pc)
+ : HExpression(type, SideEffects::None(), dex_pc),
+ index_(index),
+ table_kind_(kind) {
+ SetRawInputAt(0, cls);
+ }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ return other->AsClassTableGet()->GetIndex() == index_ &&
+ other->AsClassTableGet()->GetTableKind() == table_kind_;
+ }
+
+ TableKind GetTableKind() const { return table_kind_; }
+ size_t GetIndex() const { return index_; }
+
+ DECLARE_INSTRUCTION(ClassTableGet);
+
+ private:
+ // The index of the ArtMethod in the table.
+ const size_t index_;
+ const TableKind table_kind_;
+
+ DISALLOW_COPY_AND_ASSIGN(HClassTableGet);
+};
+
// PackedSwitch (jump table). A block ending with a PackedSwitch instruction will
// have one successor for each entry in the switch table, and the final successor
// will be the block containing the next Dex opcode.
@@ -2711,12 +2760,8 @@
public:
HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
: HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc),
- needs_materialization_(true),
bias_(ComparisonBias::kNoBias) {}
- bool NeedsMaterialization() const { return needs_materialization_; }
- void ClearNeedsMaterialization() { needs_materialization_ = false; }
-
// For code generation purposes, returns whether this instruction is just before
// `instruction`, and disregard moves in between.
bool IsBeforeWhenDisregardMoves(HInstruction* instruction) const;
@@ -2748,10 +2793,6 @@
}
private:
- // For register allocation purposes, returns whether this instruction needs to be
- // materialized (that is, not just be in the processor flags).
- bool needs_materialization_;
-
// Needed if we merge a HCompare into a HCondition.
ComparisonBias bias_;
@@ -4409,9 +4450,6 @@
Primitive::Type GetInputType() const { return GetInput()->GetType(); }
Primitive::Type GetResultType() const { return GetType(); }
- // Required by the x86, ARM, MIPS and MIPS64 code generators when producing calls
- // to the runtime.
-
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
@@ -4778,8 +4816,7 @@
}
bool NeedsEnvironment() const OVERRIDE {
- // We currently always call a runtime method to catch array store
- // exceptions.
+ // We call a runtime method to throw ArrayStoreException.
return needs_type_check_;
}
@@ -5090,11 +5127,13 @@
uint32_t GetStringIndex() const { return string_index_; }
- // TODO: Can we deopt or debug when we resolve a string?
- bool NeedsEnvironment() const OVERRIDE { return false; }
+ // Will call the runtime if the string is not already in the dex cache.
+ bool NeedsEnvironment() const OVERRIDE { return !IsInDexCache(); }
+
bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
bool CanBeNull() const OVERRIDE { return false; }
bool IsInDexCache() const { return is_in_dex_cache_; }
+ bool CanThrow() const OVERRIDE { return !IsInDexCache(); }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
@@ -5424,7 +5463,7 @@
}
bool NeedsEnvironment() const OVERRIDE {
- return false;
+ return CanCallRuntime(check_kind_);
}
bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; }
@@ -5435,11 +5474,13 @@
bool MustDoNullCheck() const { return must_do_null_check_; }
void ClearMustDoNullCheck() { must_do_null_check_ = false; }
+ static bool CanCallRuntime(TypeCheckKind check_kind) {
+ // Mips currently does runtime calls for any other checks.
+ return check_kind != TypeCheckKind::kExactCheck;
+ }
+
static SideEffects SideEffectsForArchRuntimeCalls(TypeCheckKind check_kind) {
- return (check_kind == TypeCheckKind::kExactCheck)
- ? SideEffects::None()
- // Mips currently does runtime calls for any other checks.
- : SideEffects::CanTriggerGC();
+ return CanCallRuntime(check_kind) ? SideEffects::CanTriggerGC() : SideEffects::None();
}
DECLARE_INSTRUCTION(InstanceOf);
@@ -5564,8 +5605,8 @@
SetRawInputAt(0, object);
}
- // Instruction may throw a Java exception, so we need an environment.
- bool NeedsEnvironment() const OVERRIDE { return CanThrow(); }
+ // Instruction may go into runtime, so we need an environment.
+ bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE {
// Verifier guarantees that monitor-exit cannot throw.
@@ -5586,6 +5627,41 @@
DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
};
+class HSelect : public HExpression<3> {
+ public:
+ HSelect(HInstruction* condition,
+ HInstruction* true_value,
+ HInstruction* false_value,
+ uint32_t dex_pc)
+ : HExpression(HPhi::ToPhiType(true_value->GetType()), SideEffects::None(), dex_pc) {
+ DCHECK_EQ(HPhi::ToPhiType(true_value->GetType()), HPhi::ToPhiType(false_value->GetType()));
+
+ // First input must be `true_value` or `false_value` to allow codegens to
+ // use the SameAsFirstInput allocation policy. We make it `false_value`, so
+ // that architectures which implement HSelect as a conditional move also
+ // will not need to invert the condition.
+ SetRawInputAt(0, false_value);
+ SetRawInputAt(1, true_value);
+ SetRawInputAt(2, condition);
+ }
+
+ HInstruction* GetFalseValue() const { return InputAt(0); }
+ HInstruction* GetTrueValue() const { return InputAt(1); }
+ HInstruction* GetCondition() const { return InputAt(2); }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; }
+
+ bool CanBeNull() const OVERRIDE {
+ return GetTrueValue()->CanBeNull() || GetFalseValue()->CanBeNull();
+ }
+
+ DECLARE_INSTRUCTION(Select);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HSelect);
+};
+
class MoveOperands : public ArenaObject<kArenaAllocMoveOperands> {
public:
MoveOperands(Location source,
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 556217b..b1bf939 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -36,16 +36,12 @@
class HX86LoadFromConstantTable : public HExpression<2> {
public:
HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
- HConstant* constant,
- bool needs_materialization = true)
- : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc),
- needs_materialization_(needs_materialization) {
+ HConstant* constant)
+ : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc) {
SetRawInputAt(0, method_base);
SetRawInputAt(1, constant);
}
- bool NeedsMaterialization() const { return needs_materialization_; }
-
HX86ComputeBaseMethodAddress* GetBaseMethodAddress() const {
return InputAt(0)->AsX86ComputeBaseMethodAddress();
}
@@ -57,8 +53,6 @@
DECLARE_INSTRUCTION(X86LoadFromConstantTable);
private:
- const bool needs_materialization_;
-
DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
};
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 3fac914..bdc664b 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -38,7 +38,6 @@
#include "base/dumpable.h"
#include "base/macros.h"
#include "base/timing_logger.h"
-#include "boolean_simplifier.h"
#include "bounds_check_elimination.h"
#include "builder.h"
#include "code_generator.h"
@@ -73,6 +72,7 @@
#include "reference_type_propagation.h"
#include "register_allocator.h"
#include "oat_quick_method_header.h"
+#include "select_generator.h"
#include "sharpening.h"
#include "side_effects_analysis.h"
#include "ssa_builder.h"
@@ -512,7 +512,7 @@
graph, stats, HDeadCodeElimination::kFinalDeadCodeEliminationPassName);
HConstantFolding* fold1 = new (arena) HConstantFolding(graph);
InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
- HBooleanSimplifier* boolean_simplify = new (arena) HBooleanSimplifier(graph);
+ HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph);
HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining");
HConstantFolding* fold3 = new (arena) HConstantFolding(graph, "constant_folding_after_bce");
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
@@ -540,9 +540,9 @@
MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
HOptimization* optimizations2[] = {
- // BooleanSimplifier depends on the InstructionSimplifier removing
+ // SelectGenerator depends on the InstructionSimplifier removing
// redundant suspend checks to recognize empty blocks.
- boolean_simplify,
+ select_generator,
fold2, // TODO: if we don't inline we can also skip fold2.
side_effects,
gvn,
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index f8035aa..52a7b10 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -29,6 +29,7 @@
kAttemptCompilation = 0,
kCompiled,
kInlinedInvoke,
+ kReplacedInvokeWithSimplePattern,
kInstructionSimplifications,
kInstructionSimplificationsArch,
kUnresolvedMethod,
@@ -51,6 +52,7 @@
kNotCompiledVerificationError,
kNotCompiledVerifyAtRuntime,
kInlinedMonomorphicCall,
+ kInlinedPolymorphicCall,
kMonomorphicCall,
kPolymorphicCall,
kMegamorphicCall,
@@ -96,6 +98,7 @@
case kAttemptCompilation : name = "AttemptCompilation"; break;
case kCompiled : name = "Compiled"; break;
case kInlinedInvoke : name = "InlinedInvoke"; break;
+ case kReplacedInvokeWithSimplePattern: name = "ReplacedInvokeWithSimplePattern"; break;
case kInstructionSimplifications: name = "InstructionSimplifications"; break;
case kInstructionSimplificationsArch: name = "InstructionSimplificationsArch"; break;
case kUnresolvedMethod : name = "UnresolvedMethod"; break;
@@ -118,6 +121,7 @@
case kNotCompiledVerificationError : name = "NotCompiledVerificationError"; break;
case kNotCompiledVerifyAtRuntime : name = "NotCompiledVerifyAtRuntime"; break;
case kInlinedMonomorphicCall: name = "InlinedMonomorphicCall"; break;
+ case kInlinedPolymorphicCall: name = "InlinedPolymorphicCall"; break;
case kMonomorphicCall: name = "MonomorphicCall"; break;
case kPolymorphicCall: name = "PolymorphicCall"; break;
case kMegamorphicCall: name = "kMegamorphicCall"; break;
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index 1394dfa..a2180bc 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -115,7 +115,10 @@
void ReplaceInput(HInstruction* insn, HConstant* value, int input_index, bool materialize) {
InitializePCRelativeBasePointer();
HX86LoadFromConstantTable* load_constant =
- new (GetGraph()->GetArena()) HX86LoadFromConstantTable(base_, value, materialize);
+ new (GetGraph()->GetArena()) HX86LoadFromConstantTable(base_, value);
+ if (!materialize) {
+ load_constant->MarkEmittedAtUseSite();
+ }
insn->GetBlock()->InsertInstructionBefore(load_constant, insn);
insn->ReplaceInput(load_constant, input_index);
}
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 63ef600..324d84f 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -127,24 +127,37 @@
}
}
-void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
- bool needs_materialization = false;
- if (!condition->GetUses().HasOnlyOneUse() || !condition->GetEnvUses().IsEmpty()) {
- needs_materialization = true;
- } else {
- HInstruction* user = condition->GetUses().GetFirst()->GetUser();
- if (!user->IsIf() && !user->IsDeoptimize()) {
- needs_materialization = true;
+bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition,
+ HInstruction* user) const {
+ if (condition->GetNext() != user) {
+ return false;
+ }
+
+ if (user->IsIf() || user->IsDeoptimize()) {
+ return true;
+ }
+
+ if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) {
+ if (GetGraph()->GetInstructionSet() == kX86) {
+ // Long values and long condition inputs result in 8 required core registers.
+ // We don't have that many on x86. Materialize the condition in such case.
+ return user->GetType() != Primitive::kPrimLong ||
+ condition->InputAt(1)->GetType() != Primitive::kPrimLong ||
+ condition->InputAt(1)->IsConstant();
} else {
- // TODO: if there is no intervening instructions with side-effect between this condition
- // and the If instruction, we should move the condition just before the If.
- if (condition->GetNext() != user) {
- needs_materialization = true;
- }
+ return true;
}
}
- if (!needs_materialization) {
- condition->ClearNeedsMaterialization();
+
+ return false;
+}
+
+void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
+ if (condition->HasOnlyOneNonEnvironmentUse()) {
+ HInstruction* user = condition->GetUses().GetFirst()->GetUser();
+ if (CanEmitConditionAt(condition, user)) {
+ condition->MarkEmittedAtUseSite();
+ }
}
}
@@ -165,7 +178,8 @@
}
}
-bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input, HInstruction* user) {
+bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input,
+ HInstruction* user) const {
// Determine if input and user come from the same dex instruction, so that we can move
// the clinit check responsibility from one to the other, i.e. from HClinitCheck (user)
// to HLoadClass (input), or from HClinitCheck (input) to HInvokeStaticOrDirect (user).
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index 9b24342..c8b8b0d 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -42,7 +42,8 @@
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
void VisitNewInstance(HNewInstance* instruction) OVERRIDE;
- bool CanMoveClinitCheck(HInstruction* input, HInstruction* user);
+ bool CanMoveClinitCheck(HInstruction* input, HInstruction* user) const;
+ bool CanEmitConditionAt(HCondition* condition, HInstruction* user) const;
DISALLOW_COPY_AND_ASSIGN(PrepareForRegisterAllocation);
};
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 779f319..1224a48 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -94,7 +94,6 @@
: HOptimization(graph, name),
handle_cache_(handles),
worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)) {
- worklist_.reserve(kDefaultWorklistSize);
}
void ReferenceTypePropagation::ValidateTypes() {
@@ -125,7 +124,14 @@
}
}
+void ReferenceTypePropagation::Visit(HInstruction* instruction) {
+ RTPVisitor visitor(graph_, &handle_cache_, &worklist_);
+ instruction->Accept(&visitor);
+}
+
void ReferenceTypePropagation::Run() {
+ worklist_.reserve(kDefaultWorklistSize);
+
// To properly propagate type info we need to visit in the dominator-based order.
// Reverse post order guarantees a node's dominators are visited first.
// We take advantage of this order in `VisitBasicBlock`.
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 47ba027..a7f10a6 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -35,6 +35,9 @@
StackHandleScopeCollection* handles,
const char* name = kReferenceTypePropagationPassName);
+ // Visit a single instruction.
+ void Visit(HInstruction* instruction);
+
void Run() OVERRIDE;
static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation";
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index d77639d..5cd30ad 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -179,7 +179,7 @@
}
if (block->IsCatchBlock() ||
- (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+ (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
// By blocking all registers at the top of each catch block or irreducible loop, we force
// intervals belonging to the live-in set of the catch/header block to be spilled.
// TODO(ngeoffray): Phis in this block could be allocated in register.
@@ -1749,8 +1749,10 @@
}
// Find the intervals that cover `from` and `to`.
- LiveInterval* destination = interval->GetSiblingAt(to->GetLifetimeStart());
- LiveInterval* source = interval->GetSiblingAt(from->GetLifetimeEnd() - 1);
+ size_t destination_position = to->GetLifetimeStart();
+ size_t source_position = from->GetLifetimeEnd() - 1;
+ LiveInterval* destination = interval->GetSiblingAt(destination_position);
+ LiveInterval* source = interval->GetSiblingAt(source_position);
if (destination == source) {
// Interval was not split.
@@ -1759,7 +1761,8 @@
LiveInterval* parent = interval->GetParent();
HInstruction* defined_by = parent->GetDefinedBy();
- if (destination == nullptr) {
+ if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+ (destination == nullptr || !destination->CoversSlow(destination_position))) {
// Our live_in fixed point calculation has found that the instruction is live
// in the `to` block because it will eventually enter an irreducible loop. Our
// live interval computation however does not compute a fixed point, and
@@ -1775,18 +1778,41 @@
return;
}
+ Location location_source;
+ // `GetSiblingAt` returns the interval whose start and end cover `position`,
+ // but does not check whether the interval is inactive at that position.
+ // The only situation where the interval is inactive at that position is in the
+ // presence of irreducible loops for constants and ArtMethod.
+ if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+ (source == nullptr || !source->CoversSlow(source_position))) {
+ DCHECK(IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(defined_by));
+ if (defined_by->IsConstant()) {
+ location_source = defined_by->GetLocations()->Out();
+ } else {
+ DCHECK(defined_by->IsCurrentMethod());
+ location_source = parent->NeedsTwoSpillSlots()
+ ? Location::DoubleStackSlot(parent->GetSpillSlot())
+ : Location::StackSlot(parent->GetSpillSlot());
+ }
+ } else {
+ DCHECK(source != nullptr);
+ DCHECK(source->CoversSlow(source_position));
+ DCHECK(destination->CoversSlow(destination_position));
+ location_source = source->ToLocation();
+ }
+
// If `from` has only one successor, we can put the moves at the exit of it. Otherwise
// we need to put the moves at the entry of `to`.
if (from->GetNormalSuccessors().size() == 1) {
InsertParallelMoveAtExitOf(from,
defined_by,
- source->ToLocation(),
+ location_source,
destination->ToLocation());
} else {
DCHECK_EQ(to->GetPredecessors().size(), 1u);
InsertParallelMoveAtEntryOf(to,
defined_by,
- source->ToLocation(),
+ location_source,
destination->ToLocation());
}
}
@@ -1890,7 +1916,7 @@
for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (block->IsCatchBlock() ||
- (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+ (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
// Instructions live at the top of catch blocks or irreducible loop header
// were forced to spill.
if (kIsDebugBuild) {
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
new file mode 100644
index 0000000..105b30a
--- /dev/null
+++ b/compiler/optimizing/select_generator.cc
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "select_generator.h"
+
+namespace art {
+
+static constexpr size_t kMaxInstructionsInBranch = 1u;
+
+// Returns true if `block` has only one predecessor, ends with a Goto and
+// contains at most `kMaxInstructionsInBranch` other movable instruction with
+// no side-effects.
+static bool IsSimpleBlock(HBasicBlock* block) {
+ if (block->GetPredecessors().size() != 1u) {
+ return false;
+ }
+ DCHECK(block->GetPhis().IsEmpty());
+
+ size_t num_instructions = 0u;
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsControlFlow()) {
+ return instruction->IsGoto() && num_instructions <= kMaxInstructionsInBranch;
+ } else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) {
+ num_instructions++;
+ } else {
+ return false;
+ }
+ }
+
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+}
+
+// Returns true if 'block1' and 'block2' are empty, merge into the same single
+// successor and the successor can only be reached from them.
+static bool BlocksMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
+ return block1->GetSingleSuccessor() == block2->GetSingleSuccessor();
+}
+
+// Returns nullptr if `block` has either no phis or there is more than one phi
+// with different inputs at `index1` and `index2`. Otherwise returns that phi.
+static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index2) {
+ DCHECK_NE(index1, index2);
+
+ HPhi* select_phi = nullptr;
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ if (phi->InputAt(index1) != phi->InputAt(index2)) {
+ if (select_phi == nullptr) {
+ // First phi with different inputs for the two indices found.
+ select_phi = phi;
+ } else {
+ // More than one phis has different inputs for the two indices.
+ return nullptr;
+ }
+ }
+ }
+ return select_phi;
+}
+
+void HSelectGenerator::Run() {
+ // Iterate in post order in the unlikely case that removing one occurrence of
+ // the selection pattern empties a branch block of another occurrence.
+ // Otherwise the order does not matter.
+ for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+ HBasicBlock* block = it.Current();
+ if (!block->EndsWithIf()) continue;
+
+ // Find elements of the diamond pattern.
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
+ HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
+ DCHECK_NE(true_block, false_block);
+ if (!IsSimpleBlock(true_block) ||
+ !IsSimpleBlock(false_block) ||
+ !BlocksMergeTogether(true_block, false_block)) {
+ continue;
+ }
+ HBasicBlock* merge_block = true_block->GetSingleSuccessor();
+
+ // If the branches are not empty, move instructions in front of the If.
+ // TODO(dbrazdil): This puts an instruction between If and its condition.
+ // Implement moving of conditions to first users if possible.
+ if (!true_block->IsSingleGoto()) {
+ true_block->MoveInstructionBefore(true_block->GetFirstInstruction(), if_instruction);
+ }
+ if (!false_block->IsSingleGoto()) {
+ false_block->MoveInstructionBefore(false_block->GetFirstInstruction(), if_instruction);
+ }
+ DCHECK(true_block->IsSingleGoto());
+ DCHECK(false_block->IsSingleGoto());
+
+ // Find the resulting true/false values.
+ size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block);
+ size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block);
+ DCHECK_NE(predecessor_index_true, predecessor_index_false);
+
+ HPhi* phi = GetSingleChangedPhi(merge_block, predecessor_index_true, predecessor_index_false);
+ if (phi == nullptr) {
+ continue;
+ }
+ HInstruction* true_value = phi->InputAt(predecessor_index_true);
+ HInstruction* false_value = phi->InputAt(predecessor_index_false);
+
+ // Create the Select instruction and insert it in front of the If.
+ HSelect* select = new (graph_->GetArena()) HSelect(if_instruction->InputAt(0),
+ true_value,
+ false_value,
+ if_instruction->GetDexPc());
+ if (phi->GetType() == Primitive::kPrimNot) {
+ select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo());
+ }
+ block->InsertInstructionBefore(select, if_instruction);
+
+ // Remove the true branch which removes the corresponding Phi input.
+ // If left only with the false branch, the Phi is automatically removed.
+ phi->ReplaceInput(select, predecessor_index_false);
+ bool only_two_predecessors = (merge_block->GetPredecessors().size() == 2u);
+ true_block->DisconnectAndDelete();
+ DCHECK_EQ(only_two_predecessors, phi->GetBlock() == nullptr);
+
+ // Merge remaining blocks which are now connected with Goto.
+ DCHECK_EQ(block->GetSingleSuccessor(), false_block);
+ block->MergeWith(false_block);
+ if (only_two_predecessors) {
+ DCHECK_EQ(block->GetSingleSuccessor(), merge_block);
+ block->MergeWith(merge_block);
+ }
+
+ // No need to update dominance information, as we are simplifying
+ // a simple diamond shape, where the join block is merged with the
+ // entry block. Any following blocks would have had the join block
+ // as a dominator, and `MergeWith` handles changing that to the
+ // entry block.
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
new file mode 100644
index 0000000..f9d6d4d
--- /dev/null
+++ b/compiler/optimizing/select_generator.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This optimization recognizes the common diamond selection pattern and
+ * replaces it with an instance of the HSelect instruction.
+ *
+ * Recognized pattern:
+ *
+ * If [ Condition ]
+ * / \
+ * false branch true branch
+ * \ /
+ * Phi [FalseValue, TrueValue]
+ *
+ * The pattern will be simplified if `true_branch` and `false_branch` each
+ * contain at most one instruction without any side effects.
+ *
+ * Blocks are merged into one and Select replaces the If and the Phi:
+ * true branch
+ * false branch
+ * Select [FalseValue, TrueValue, Condition]
+ *
+ * Note: In order to recognize no side-effect blocks, this optimization must be
+ * run after the instruction simplifier has removed redundant suspend checks.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
+#define ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
+
+#include "optimization.h"
+
+namespace art {
+
+class HSelectGenerator : public HOptimization {
+ public:
+ explicit HSelectGenerator(HGraph* graph)
+ : HOptimization(graph, kSelectGeneratorPassName) {}
+
+ void Run() OVERRIDE;
+
+ static constexpr const char* kSelectGeneratorPassName = "select_generator";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HSelectGenerator);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index a5609fc..7ed3c84 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -242,19 +242,53 @@
}
if (instruction != nullptr) {
instruction->GetLiveInterval()->AddUse(
- current, environment, i, should_be_live);
+ current, environment, i, /* actual_user */ nullptr, should_be_live);
}
}
}
- // All inputs of an instruction must be live.
- for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
- HInstruction* input = current->InputAt(i);
- // Some instructions 'inline' their inputs, that is they do not need
- // to be materialized.
- if (input->HasSsaIndex() && current->GetLocations()->InAt(i).IsValid()) {
- live_in->SetBit(input->GetSsaIndex());
- input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);
+ // Process inputs of instructions.
+ if (current->IsEmittedAtUseSite()) {
+ if (kIsDebugBuild) {
+ DCHECK(!current->GetLocations()->Out().IsValid());
+ for (HUseIterator<HInstruction*> use_it(current->GetUses());
+ !use_it.Done();
+ use_it.Advance()) {
+ HInstruction* user = use_it.Current()->GetUser();
+ size_t index = use_it.Current()->GetIndex();
+ DCHECK(!user->GetLocations()->InAt(index).IsValid());
+ }
+ DCHECK(!current->HasEnvironmentUses());
+ }
+ } else {
+ for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
+ HInstruction* input = current->InputAt(i);
+ bool has_in_location = current->GetLocations()->InAt(i).IsValid();
+ bool has_out_location = input->GetLocations()->Out().IsValid();
+
+ if (has_in_location) {
+ DCHECK(has_out_location);
+ DCHECK(input->HasSsaIndex());
+ // `Input` generates a result used by `current`. Add use and update
+ // the live-in set.
+ input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i);
+ live_in->SetBit(input->GetSsaIndex());
+ } else if (has_out_location) {
+ // `Input` generates a result but it is not used by `current`.
+ } else {
+ // `Input` is inlined into `current`. Walk over its inputs and record
+ // uses at `current`.
+ DCHECK(input->IsEmittedAtUseSite());
+ for (size_t i2 = 0, e2 = input->InputCount(); i2 < e2; ++i2) {
+ HInstruction* inlined_input = input->InputAt(i2);
+ DCHECK(inlined_input->HasSsaIndex()) << "Recursive inlining not allowed.";
+ if (input->GetLocations()->InAt(i2).IsValid()) {
+ live_in->SetBit(inlined_input->GetSsaIndex());
+ inlined_input->GetLiveInterval()->AddUse(
+ /* owner */ input, /* environment */ nullptr, i2, /* actual_user */ current);
+ }
+ }
+ }
}
}
}
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 572a7b6..a78aedc 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -113,10 +113,6 @@
input_index_(input_index),
position_(position),
next_(next) {
- DCHECK((user == nullptr)
- || user->IsPhi()
- || (GetPosition() == user->GetLifetimePosition() + 1)
- || (GetPosition() == user->GetLifetimePosition()));
DCHECK(environment == nullptr || user == nullptr);
DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition());
}
@@ -243,21 +239,30 @@
AddRange(position, position + 1);
}
+ // Record use of an input. The use will be recorded as an environment use if
+ // `environment` is not null and as register use otherwise. If `actual_user`
+ // is specified, the use will be recorded at `actual_user`'s lifetime position.
void AddUse(HInstruction* instruction,
HEnvironment* environment,
size_t input_index,
+ HInstruction* actual_user = nullptr,
bool keep_alive = false) {
- // Set the use within the instruction.
bool is_environment = (environment != nullptr);
- size_t position = instruction->GetLifetimePosition() + 1;
LocationSummary* locations = instruction->GetLocations();
+ if (actual_user == nullptr) {
+ actual_user = instruction;
+ }
+
+ // Set the use within the instruction.
+ size_t position = actual_user->GetLifetimePosition() + 1;
if (!is_environment) {
if (locations->IsFixedInput(input_index) || locations->OutputUsesSameAs(input_index)) {
// For fixed inputs and output same as input, the register allocator
// requires to have inputs die at the instruction, so that input moves use the
// location of the input just before that instruction (and not potential moves due
// to splitting).
- position = instruction->GetLifetimePosition();
+ DCHECK_EQ(instruction, actual_user);
+ position = actual_user->GetLifetimePosition();
} else if (!locations->InAt(input_index).IsValid()) {
return;
}
@@ -267,11 +272,8 @@
AddBackEdgeUses(*instruction->GetBlock());
}
- DCHECK(position == instruction->GetLifetimePosition()
- || position == instruction->GetLifetimePosition() + 1);
-
if ((first_use_ != nullptr)
- && (first_use_->GetUser() == instruction)
+ && (first_use_->GetUser() == actual_user)
&& (first_use_->GetPosition() < position)) {
// The user uses the instruction multiple times, and one use dies before the other.
// We update the use list so that the latter is first.
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index f9ff2df..ab480ca 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -300,10 +300,17 @@
EmitRtd(0x1f, rt, rd, 0x5, 0x24);
}
-void Mips64Assembler::Dext(GpuRegister rt, GpuRegister rs, int pos, int size_less_one) {
- DCHECK(0 <= pos && pos < 32) << pos;
- DCHECK(0 <= size_less_one && size_less_one < 32) << size_less_one;
- EmitR(0x1f, rs, rt, static_cast<GpuRegister>(size_less_one), pos, 3);
+void Mips64Assembler::Dext(GpuRegister rt, GpuRegister rs, int pos, int size) {
+ CHECK(IsUint<5>(pos)) << pos;
+ CHECK(IsUint<5>(size - 1)) << size;
+ EmitR(0x1f, rs, rt, static_cast<GpuRegister>(size - 1), pos, 0x3);
+}
+
+void Mips64Assembler::Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size) {
+ CHECK(IsUint<5>(pos - 32)) << pos;
+ CHECK(IsUint<5>(size - 1)) << size;
+ CHECK(IsUint<5>(pos + size - 33)) << pos << " + " << size;
+ EmitR(0x1f, rs, rt, static_cast<GpuRegister>(pos + size - 33), pos - 32, 0x6);
}
void Mips64Assembler::Wsbh(GpuRegister rd, GpuRegister rt) {
@@ -311,22 +318,22 @@
}
void Mips64Assembler::Sc(GpuRegister rt, GpuRegister base, int16_t imm9) {
- DCHECK((-256 <= imm9) && (imm9 < 256));
+ CHECK(IsInt<9>(imm9));
EmitI(0x1f, base, rt, ((imm9 & 0x1FF) << 7) | 0x26);
}
void Mips64Assembler::Scd(GpuRegister rt, GpuRegister base, int16_t imm9) {
- DCHECK((-256 <= imm9) && (imm9 < 256));
+ CHECK(IsInt<9>(imm9));
EmitI(0x1f, base, rt, ((imm9 & 0x1FF) << 7) | 0x27);
}
void Mips64Assembler::Ll(GpuRegister rt, GpuRegister base, int16_t imm9) {
- DCHECK((-256 <= imm9) && (imm9 < 256));
+ CHECK(IsInt<9>(imm9));
EmitI(0x1f, base, rt, ((imm9 & 0x1FF) << 7) | 0x36);
}
void Mips64Assembler::Lld(GpuRegister rt, GpuRegister base, int16_t imm9) {
- DCHECK((-256 <= imm9) && (imm9 < 256));
+ CHECK(IsInt<9>(imm9));
EmitI(0x1f, base, rt, ((imm9 & 0x1FF) << 7) | 0x37);
}
@@ -967,10 +974,18 @@
EmitFR(0x11, 0x00, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);
}
+void Mips64Assembler::Mfhc1(GpuRegister rt, FpuRegister fs) {
+ EmitFR(0x11, 0x03, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);
+}
+
void Mips64Assembler::Mtc1(GpuRegister rt, FpuRegister fs) {
EmitFR(0x11, 0x04, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);
}
+void Mips64Assembler::Mthc1(GpuRegister rt, FpuRegister fs) {
+ EmitFR(0x11, 0x07, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);
+}
+
void Mips64Assembler::Dmfc1(GpuRegister rt, FpuRegister fs) {
EmitFR(0x11, 0x01, static_cast<FpuRegister>(rt), fs, static_cast<FpuRegister>(0), 0x0);
}
@@ -1787,11 +1802,13 @@
void Mips64Assembler::LoadFromOffset(LoadOperandType type, GpuRegister reg, GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset)) {
- LoadConst32(AT, offset);
+ if (!IsInt<16>(offset) ||
+ (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
Daddu(AT, AT, base);
base = AT;
- offset = 0;
+ offset &= (kMips64DoublewordSize - 1);
}
switch (type) {
@@ -1808,32 +1825,51 @@
Lhu(reg, base, offset);
break;
case kLoadWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
Lw(reg, base, offset);
break;
case kLoadUnsignedWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
Lwu(reg, base, offset);
break;
case kLoadDoubleword:
- Ld(reg, base, offset);
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Lwu(reg, base, offset);
+ Lwu(TMP2, base, offset + kMips64WordSize);
+ Dinsu(reg, TMP2, 32, 32);
+ } else {
+ Ld(reg, base, offset);
+ }
break;
}
}
void Mips64Assembler::LoadFpuFromOffset(LoadOperandType type, FpuRegister reg, GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset)) {
- LoadConst32(AT, offset);
+ if (!IsInt<16>(offset) ||
+ (type == kLoadDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
Daddu(AT, AT, base);
base = AT;
- offset = 0;
+ offset &= (kMips64DoublewordSize - 1);
}
switch (type) {
case kLoadWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
Lwc1(reg, base, offset);
break;
case kLoadDoubleword:
- Ldc1(reg, base, offset);
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Lwc1(reg, base, offset);
+ Lw(TMP2, base, offset + kMips64WordSize);
+ Mthc1(TMP2, reg);
+ } else {
+ Ldc1(reg, base, offset);
+ }
break;
default:
LOG(FATAL) << "UNREACHABLE";
@@ -1869,11 +1905,13 @@
void Mips64Assembler::StoreToOffset(StoreOperandType type, GpuRegister reg, GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset)) {
- LoadConst32(AT, offset);
+ if (!IsInt<16>(offset) ||
+ (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
Daddu(AT, AT, base);
base = AT;
- offset = 0;
+ offset &= (kMips64DoublewordSize - 1);
}
switch (type) {
@@ -1884,10 +1922,18 @@
Sh(reg, base, offset);
break;
case kStoreWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
Sw(reg, base, offset);
break;
case kStoreDoubleword:
- Sd(reg, base, offset);
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Sw(reg, base, offset);
+ Dsrl32(TMP2, reg, 0);
+ Sw(TMP2, base, offset + kMips64WordSize);
+ } else {
+ Sd(reg, base, offset);
+ }
break;
default:
LOG(FATAL) << "UNREACHABLE";
@@ -1896,19 +1942,29 @@
void Mips64Assembler::StoreFpuToOffset(StoreOperandType type, FpuRegister reg, GpuRegister base,
int32_t offset) {
- if (!IsInt<16>(offset)) {
- LoadConst32(AT, offset);
+ if (!IsInt<16>(offset) ||
+ (type == kStoreDoubleword && !IsAligned<kMips64DoublewordSize>(offset) &&
+ !IsInt<16>(static_cast<int32_t>(offset + kMips64WordSize)))) {
+ LoadConst32(AT, offset & ~(kMips64DoublewordSize - 1));
Daddu(AT, AT, base);
base = AT;
- offset = 0;
+ offset &= (kMips64DoublewordSize - 1);
}
switch (type) {
case kStoreWord:
+ CHECK_ALIGNED(offset, kMips64WordSize);
Swc1(reg, base, offset);
break;
case kStoreDoubleword:
- Sdc1(reg, base, offset);
+ if (!IsAligned<kMips64DoublewordSize>(offset)) {
+ CHECK_ALIGNED(offset, kMips64WordSize);
+ Mfhc1(TMP2, reg);
+ Swc1(reg, base, offset);
+ Sw(TMP2, base, offset + kMips64WordSize);
+ } else {
+ Sdc1(reg, base, offset);
+ }
break;
default:
LOG(FATAL) << "UNREACHABLE";
@@ -2053,7 +2109,7 @@
StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
}
-void Mips64Assembler::StoreStackOffsetToThread64(ThreadOffset<kMipsDoublewordSize> thr_offs,
+void Mips64Assembler::StoreStackOffsetToThread64(ThreadOffset<kMips64DoublewordSize> thr_offs,
FrameOffset fr_offs,
ManagedRegister mscratch) {
Mips64ManagedRegister scratch = mscratch.AsMips64();
@@ -2062,7 +2118,7 @@
StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), S1, thr_offs.Int32Value());
}
-void Mips64Assembler::StoreStackPointerToThread64(ThreadOffset<kMipsDoublewordSize> thr_offs) {
+void Mips64Assembler::StoreStackPointerToThread64(ThreadOffset<kMips64DoublewordSize> thr_offs) {
StoreToOffset(kStoreDoubleword, SP, S1, thr_offs.Int32Value());
}
@@ -2080,7 +2136,7 @@
}
void Mips64Assembler::LoadFromThread64(ManagedRegister mdest,
- ThreadOffset<kMipsDoublewordSize> src,
+ ThreadOffset<kMips64DoublewordSize> src,
size_t size) {
return EmitLoad(mdest, S1, src.Int32Value(), size);
}
@@ -2102,7 +2158,7 @@
// Negate the 32-bit ref
Dsubu(dest.AsGpuRegister(), ZERO, dest.AsGpuRegister());
// And constrain it to 32 bits (zero-extend into bits 32 through 63) as on Arm64 and x86/64
- Dext(dest.AsGpuRegister(), dest.AsGpuRegister(), 0, 31);
+ Dext(dest.AsGpuRegister(), dest.AsGpuRegister(), 0, 32);
}
}
@@ -2115,7 +2171,7 @@
}
void Mips64Assembler::LoadRawPtrFromThread64(ManagedRegister mdest,
- ThreadOffset<kMipsDoublewordSize> offs) {
+ ThreadOffset<kMips64DoublewordSize> offs) {
Mips64ManagedRegister dest = mdest.AsMips64();
CHECK(dest.IsGpuRegister());
LoadFromOffset(kLoadDoubleword, dest.AsGpuRegister(), S1, offs.Int32Value());
@@ -2160,7 +2216,7 @@
}
void Mips64Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs,
- ThreadOffset<kMipsDoublewordSize> thr_offs,
+ ThreadOffset<kMips64DoublewordSize> thr_offs,
ManagedRegister mscratch) {
Mips64ManagedRegister scratch = mscratch.AsMips64();
CHECK(scratch.IsGpuRegister()) << scratch;
@@ -2168,7 +2224,7 @@
StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, fr_offs.Int32Value());
}
-void Mips64Assembler::CopyRawPtrToThread64(ThreadOffset<kMipsDoublewordSize> thr_offs,
+void Mips64Assembler::CopyRawPtrToThread64(ThreadOffset<kMips64DoublewordSize> thr_offs,
FrameOffset fr_offs,
ManagedRegister mscratch) {
Mips64ManagedRegister scratch = mscratch.AsMips64();
@@ -2372,7 +2428,7 @@
// TODO: place reference map on call
}
-void Mips64Assembler::CallFromThread64(ThreadOffset<kMipsDoublewordSize> offset ATTRIBUTE_UNUSED,
+void Mips64Assembler::CallFromThread64(ThreadOffset<kMips64DoublewordSize> offset ATTRIBUTE_UNUSED,
ManagedRegister mscratch ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL) << "No MIPS64 implementation";
}
@@ -2392,7 +2448,7 @@
LoadFromOffset(kLoadDoubleword,
scratch.AsGpuRegister(),
S1,
- Thread::ExceptionOffset<kMipsDoublewordSize>().Int32Value());
+ Thread::ExceptionOffset<kMips64DoublewordSize>().Int32Value());
Bnezc(scratch.AsGpuRegister(), exception_blocks_.back().Entry());
}
@@ -2409,7 +2465,7 @@
LoadFromOffset(kLoadDoubleword,
T9,
S1,
- QUICK_ENTRYPOINT_OFFSET(kMipsDoublewordSize, pDeliverException).Int32Value());
+ QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pDeliverException).Int32Value());
Jr(T9);
Nop();
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index 3262640..71f5e00 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -31,7 +31,8 @@
namespace art {
namespace mips64 {
-static constexpr size_t kMipsDoublewordSize = 8;
+static constexpr size_t kMips64WordSize = 4;
+static constexpr size_t kMips64DoublewordSize = 8;
enum LoadOperandType {
kLoadSignedByte,
@@ -151,7 +152,8 @@
void Seh(GpuRegister rd, GpuRegister rt);
void Dsbh(GpuRegister rd, GpuRegister rt);
void Dshd(GpuRegister rd, GpuRegister rt);
- void Dext(GpuRegister rs, GpuRegister rt, int pos, int size_less_one); // MIPS64
+ void Dext(GpuRegister rs, GpuRegister rt, int pos, int size); // MIPS64
+ void Dinsu(GpuRegister rt, GpuRegister rs, int pos, int size); // MIPS64
void Wsbh(GpuRegister rd, GpuRegister rt);
void Sc(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
void Scd(GpuRegister rt, GpuRegister base, int16_t imm9 = 0);
@@ -301,7 +303,9 @@
void Cvtdl(FpuRegister fd, FpuRegister fs);
void Mfc1(GpuRegister rt, FpuRegister fs);
+ void Mfhc1(GpuRegister rt, FpuRegister fs);
void Mtc1(GpuRegister rt, FpuRegister fs);
+ void Mthc1(GpuRegister rt, FpuRegister fs);
void Dmfc1(GpuRegister rt, FpuRegister fs); // MIPS64
void Dmtc1(GpuRegister rt, FpuRegister fs); // MIPS64
void Lwc1(FpuRegister ft, GpuRegister rs, uint16_t imm16);
@@ -378,10 +382,10 @@
void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, ManagedRegister mscratch) OVERRIDE;
- void StoreStackOffsetToThread64(ThreadOffset<kMipsDoublewordSize> thr_offs, FrameOffset fr_offs,
+ void StoreStackOffsetToThread64(ThreadOffset<kMips64DoublewordSize> thr_offs, FrameOffset fr_offs,
ManagedRegister mscratch) OVERRIDE;
- void StoreStackPointerToThread64(ThreadOffset<kMipsDoublewordSize> thr_offs) OVERRIDE;
+ void StoreStackPointerToThread64(ThreadOffset<kMips64DoublewordSize> thr_offs) OVERRIDE;
void StoreSpanning(FrameOffset dest, ManagedRegister msrc, FrameOffset in_off,
ManagedRegister mscratch) OVERRIDE;
@@ -390,7 +394,7 @@
void Load(ManagedRegister mdest, FrameOffset src, size_t size) OVERRIDE;
void LoadFromThread64(ManagedRegister mdest,
- ThreadOffset<kMipsDoublewordSize> src,
+ ThreadOffset<kMips64DoublewordSize> src,
size_t size) OVERRIDE;
void LoadRef(ManagedRegister dest, FrameOffset src) OVERRIDE;
@@ -401,15 +405,15 @@
void LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) OVERRIDE;
void LoadRawPtrFromThread64(ManagedRegister mdest,
- ThreadOffset<kMipsDoublewordSize> offs) OVERRIDE;
+ ThreadOffset<kMips64DoublewordSize> offs) OVERRIDE;
// Copying routines.
void Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) OVERRIDE;
- void CopyRawPtrFromThread64(FrameOffset fr_offs, ThreadOffset<kMipsDoublewordSize> thr_offs,
+ void CopyRawPtrFromThread64(FrameOffset fr_offs, ThreadOffset<kMips64DoublewordSize> thr_offs,
ManagedRegister mscratch) OVERRIDE;
- void CopyRawPtrToThread64(ThreadOffset<kMipsDoublewordSize> thr_offs, FrameOffset fr_offs,
+ void CopyRawPtrToThread64(ThreadOffset<kMips64DoublewordSize> thr_offs, FrameOffset fr_offs,
ManagedRegister mscratch) OVERRIDE;
void CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) OVERRIDE;
@@ -466,7 +470,7 @@
// Call to address held at [base+offset].
void Call(ManagedRegister base, Offset offset, ManagedRegister mscratch) OVERRIDE;
void Call(FrameOffset base, Offset offset, ManagedRegister mscratch) OVERRIDE;
- void CallFromThread64(ThreadOffset<kMipsDoublewordSize> offset,
+ void CallFromThread64(ThreadOffset<kMips64DoublewordSize> offset,
ManagedRegister mscratch) OVERRIDE;
// Generate code to check if Thread::Current()->exception_ is non-null
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index 7d79be2..b758d64 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -543,6 +543,30 @@
DriverStr(RepeatFF(&mips64::Mips64Assembler::TruncLD, "trunc.l.d ${reg1}, ${reg2}"), "trunc.l.d");
}
+TEST_F(AssemblerMIPS64Test, Mfc1) {
+ DriverStr(RepeatRF(&mips64::Mips64Assembler::Mfc1, "mfc1 ${reg1}, ${reg2}"), "Mfc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Mfhc1) {
+ DriverStr(RepeatRF(&mips64::Mips64Assembler::Mfhc1, "mfhc1 ${reg1}, ${reg2}"), "Mfhc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Mtc1) {
+ DriverStr(RepeatRF(&mips64::Mips64Assembler::Mtc1, "mtc1 ${reg1}, ${reg2}"), "Mtc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Mthc1) {
+ DriverStr(RepeatRF(&mips64::Mips64Assembler::Mthc1, "mthc1 ${reg1}, ${reg2}"), "Mthc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Dmfc1) {
+ DriverStr(RepeatRF(&mips64::Mips64Assembler::Dmfc1, "dmfc1 ${reg1}, ${reg2}"), "Dmfc1");
+}
+
+TEST_F(AssemblerMIPS64Test, Dmtc1) {
+ DriverStr(RepeatRF(&mips64::Mips64Assembler::Dmtc1, "dmtc1 ${reg1}, ${reg2}"), "Dmtc1");
+}
+
////////////////
// CALL / JMP //
////////////////
@@ -827,6 +851,44 @@
DriverStr(RepeatRR(&mips64::Mips64Assembler::Dshd, "dshd ${reg1}, ${reg2}"), "dshd");
}
+TEST_F(AssemblerMIPS64Test, Dext) {
+ std::vector<mips64::GpuRegister*> reg1_registers = GetRegisters();
+ std::vector<mips64::GpuRegister*> reg2_registers = GetRegisters();
+ WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * 33 * 16);
+ std::ostringstream expected;
+ for (mips64::GpuRegister* reg1 : reg1_registers) {
+ for (mips64::GpuRegister* reg2 : reg2_registers) {
+ for (int32_t pos = 0; pos < 32; pos++) {
+ for (int32_t size = 1; size <= 32; size++) {
+ __ Dext(*reg1, *reg2, pos, size);
+ expected << "dext $" << *reg1 << ", $" << *reg2 << ", " << pos << ", " << size << "\n";
+ }
+ }
+ }
+ }
+
+ DriverStr(expected.str(), "Dext");
+}
+
+TEST_F(AssemblerMIPS64Test, Dinsu) {
+ std::vector<mips64::GpuRegister*> reg1_registers = GetRegisters();
+ std::vector<mips64::GpuRegister*> reg2_registers = GetRegisters();
+ WarnOnCombinations(reg1_registers.size() * reg2_registers.size() * 33 * 16);
+ std::ostringstream expected;
+ for (mips64::GpuRegister* reg1 : reg1_registers) {
+ for (mips64::GpuRegister* reg2 : reg2_registers) {
+ for (int32_t pos = 32; pos < 64; pos++) {
+ for (int32_t size = 1; pos + size <= 64; size++) {
+ __ Dinsu(*reg1, *reg2, pos, size);
+ expected << "dinsu $" << *reg1 << ", $" << *reg2 << ", " << pos << ", " << size << "\n";
+ }
+ }
+ }
+ }
+
+ DriverStr(expected.str(), "Dinsu");
+}
+
TEST_F(AssemblerMIPS64Test, Wsbh) {
DriverStr(RepeatRR(&mips64::Mips64Assembler::Wsbh, "wsbh ${reg1}, ${reg2}"), "wsbh");
}
@@ -942,4 +1004,638 @@
DriverStr(RepeatRR(&mips64::Mips64Assembler::Dclo, "dclo ${reg1}, ${reg2}"), "dclo");
}
+TEST_F(AssemblerMIPS64Test, LoadFromOffset) {
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A0, 0);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 0);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 1);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 256);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 1000);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 0x7FFF);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 0x8000);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 0x8001);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 0x10000);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 0x12345678);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, -256);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, -32768);
+ __ LoadFromOffset(mips64::kLoadSignedByte, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A0, 0);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 0);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 1);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 256);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 1000);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 0x7FFF);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 0x8000);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 0x8001);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 0x10000);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 0x12345678);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, -256);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, -32768);
+ __ LoadFromOffset(mips64::kLoadUnsignedByte, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A0, 0);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 0);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 2);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 256);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 1000);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 0x7FFE);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 0x8000);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 0x8002);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 0x10000);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 0x12345678);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, -256);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, -32768);
+ __ LoadFromOffset(mips64::kLoadSignedHalfword, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A0, 0);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 0);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 2);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 256);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 1000);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 0x7FFE);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 0x8000);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 0x8002);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 0x10000);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 0x12345678);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, -256);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, -32768);
+ __ LoadFromOffset(mips64::kLoadUnsignedHalfword, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A0, 0);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 0);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 4);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 256);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 1000);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 0x7FFC);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 0x8000);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 0x8004);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 0x10000);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 0x12345678);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, -256);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, -32768);
+ __ LoadFromOffset(mips64::kLoadWord, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A0, 0);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 0);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 4);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 256);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 1000);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 0x7FFC);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 0x8000);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 0x8004);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 0x10000);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 0x12345678);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, -256);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, -32768);
+ __ LoadFromOffset(mips64::kLoadUnsignedWord, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A0, 0);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 0);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 4);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 256);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 1000);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 0x7FFC);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 0x8000);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 0x8004);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 0x10000);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 0x12345678);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, -256);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, -32768);
+ __ LoadFromOffset(mips64::kLoadDoubleword, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ const char* expected =
+ "lb $a0, 0($a0)\n"
+ "lb $a0, 0($a1)\n"
+ "lb $a0, 1($a1)\n"
+ "lb $a0, 256($a1)\n"
+ "lb $a0, 1000($a1)\n"
+ "lb $a0, 0x7FFF($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lb $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lb $a0, 1($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "lb $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "lb $a0, 0($at)\n"
+ "lb $a0, -256($a1)\n"
+ "lb $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "lb $a0, 0($at)\n"
+
+ "lbu $a0, 0($a0)\n"
+ "lbu $a0, 0($a1)\n"
+ "lbu $a0, 1($a1)\n"
+ "lbu $a0, 256($a1)\n"
+ "lbu $a0, 1000($a1)\n"
+ "lbu $a0, 0x7FFF($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lbu $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lbu $a0, 1($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "lbu $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "lbu $a0, 0($at)\n"
+ "lbu $a0, -256($a1)\n"
+ "lbu $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "lbu $a0, 0($at)\n"
+
+ "lh $a0, 0($a0)\n"
+ "lh $a0, 0($a1)\n"
+ "lh $a0, 2($a1)\n"
+ "lh $a0, 256($a1)\n"
+ "lh $a0, 1000($a1)\n"
+ "lh $a0, 0x7FFE($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lh $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lh $a0, 2($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "lh $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "lh $a0, 0($at)\n"
+ "lh $a0, -256($a1)\n"
+ "lh $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "lh $a0, 0($at)\n"
+
+ "lhu $a0, 0($a0)\n"
+ "lhu $a0, 0($a1)\n"
+ "lhu $a0, 2($a1)\n"
+ "lhu $a0, 256($a1)\n"
+ "lhu $a0, 1000($a1)\n"
+ "lhu $a0, 0x7FFE($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lhu $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lhu $a0, 2($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "lhu $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "lhu $a0, 0($at)\n"
+ "lhu $a0, -256($a1)\n"
+ "lhu $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "lhu $a0, 0($at)\n"
+
+ "lw $a0, 0($a0)\n"
+ "lw $a0, 0($a1)\n"
+ "lw $a0, 4($a1)\n"
+ "lw $a0, 256($a1)\n"
+ "lw $a0, 1000($a1)\n"
+ "lw $a0, 0x7FFC($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lw $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lw $a0, 4($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "lw $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "lw $a0, 0($at)\n"
+ "lw $a0, -256($a1)\n"
+ "lw $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "lw $a0, 0($at)\n"
+
+ "lwu $a0, 0($a0)\n"
+ "lwu $a0, 0($a1)\n"
+ "lwu $a0, 4($a1)\n"
+ "lwu $a0, 256($a1)\n"
+ "lwu $a0, 1000($a1)\n"
+ "lwu $a0, 0x7FFC($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lwu $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lwu $a0, 4($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "lwu $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "lwu $a0, 0($at)\n"
+ "lwu $a0, -256($a1)\n"
+ "lwu $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "lwu $a0, 0($at)\n"
+
+ "ld $a0, 0($a0)\n"
+ "ld $a0, 0($a1)\n"
+ "lwu $a0, 4($a1)\n"
+ "lwu $t3, 8($a1)\n"
+ "dins $a0, $t3, 32, 32\n"
+ "ld $a0, 256($a1)\n"
+ "ld $a0, 1000($a1)\n"
+ "ori $at, $zero, 0x7FF8\n"
+ "daddu $at, $at, $a1\n"
+ "lwu $a0, 4($at)\n"
+ "lwu $t3, 8($at)\n"
+ "dins $a0, $t3, 32, 32\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "ld $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "lwu $a0, 4($at)\n"
+ "lwu $t3, 8($at)\n"
+ "dins $a0, $t3, 32, 32\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "ld $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "ld $a0, 0($at)\n"
+ "ld $a0, -256($a1)\n"
+ "ld $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "ld $a0, 0($at)\n";
+ DriverStr(expected, "LoadFromOffset");
+}
+
+TEST_F(AssemblerMIPS64Test, LoadFpuFromOffset) {
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 0);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 4);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 256);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 0x7FFC);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 0x8000);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 0x8004);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 0x10000);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 0x12345678);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, -256);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, -32768);
+ __ LoadFpuFromOffset(mips64::kLoadWord, mips64::F0, mips64::A0, 0xABCDEF00);
+
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 0);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 4);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 256);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 0x7FFC);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 0x8000);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 0x8004);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 0x10000);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 0x12345678);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, -256);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, -32768);
+ __ LoadFpuFromOffset(mips64::kLoadDoubleword, mips64::F0, mips64::A0, 0xABCDEF00);
+
+ const char* expected =
+ "lwc1 $f0, 0($a0)\n"
+ "lwc1 $f0, 4($a0)\n"
+ "lwc1 $f0, 256($a0)\n"
+ "lwc1 $f0, 0x7FFC($a0)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "lwc1 $f0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "lwc1 $f0, 4($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a0\n"
+ "lwc1 $f0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a0\n"
+ "lwc1 $f0, 0($at)\n"
+ "lwc1 $f0, -256($a0)\n"
+ "lwc1 $f0, -32768($a0)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a0\n"
+ "lwc1 $f0, 0($at)\n"
+
+ "ldc1 $f0, 0($a0)\n"
+ "lwc1 $f0, 4($a0)\n"
+ "lw $t3, 8($a0)\n"
+ "mthc1 $t3, $f0\n"
+ "ldc1 $f0, 256($a0)\n"
+ "ori $at, $zero, 0x7FF8\n"
+ "daddu $at, $at, $a0\n"
+ "lwc1 $f0, 4($at)\n"
+ "lw $t3, 8($at)\n"
+ "mthc1 $t3, $f0\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "ldc1 $f0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "lwc1 $f0, 4($at)\n"
+ "lw $t3, 8($at)\n"
+ "mthc1 $t3, $f0\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a0\n"
+ "ldc1 $f0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a0\n"
+ "ldc1 $f0, 0($at)\n"
+ "ldc1 $f0, -256($a0)\n"
+ "ldc1 $f0, -32768($a0)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a0\n"
+ "ldc1 $f0, 0($at)\n";
+ DriverStr(expected, "LoadFpuFromOffset");
+}
+
+TEST_F(AssemblerMIPS64Test, StoreToOffset) {
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A0, 0);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 0);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 1);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 256);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 1000);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 0x7FFF);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 0x8000);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 0x8001);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 0x10000);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 0x12345678);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, -256);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, -32768);
+ __ StoreToOffset(mips64::kStoreByte, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A0, 0);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 0);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 2);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 256);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 1000);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 0x7FFE);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 0x8000);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 0x8002);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 0x10000);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 0x12345678);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, -256);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, -32768);
+ __ StoreToOffset(mips64::kStoreHalfword, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A0, 0);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 0);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 4);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 256);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 1000);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 0x7FFC);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 0x8000);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 0x8004);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 0x10000);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 0x12345678);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, -256);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, -32768);
+ __ StoreToOffset(mips64::kStoreWord, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A0, 0);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 0);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 4);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 256);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 1000);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 0x7FFC);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 0x8000);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 0x8004);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 0x10000);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 0x12345678);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, -256);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, -32768);
+ __ StoreToOffset(mips64::kStoreDoubleword, mips64::A0, mips64::A1, 0xABCDEF00);
+
+ const char* expected =
+ "sb $a0, 0($a0)\n"
+ "sb $a0, 0($a1)\n"
+ "sb $a0, 1($a1)\n"
+ "sb $a0, 256($a1)\n"
+ "sb $a0, 1000($a1)\n"
+ "sb $a0, 0x7FFF($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sb $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sb $a0, 1($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "sb $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "sb $a0, 0($at)\n"
+ "sb $a0, -256($a1)\n"
+ "sb $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "sb $a0, 0($at)\n"
+
+ "sh $a0, 0($a0)\n"
+ "sh $a0, 0($a1)\n"
+ "sh $a0, 2($a1)\n"
+ "sh $a0, 256($a1)\n"
+ "sh $a0, 1000($a1)\n"
+ "sh $a0, 0x7FFE($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sh $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sh $a0, 2($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "sh $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "sh $a0, 0($at)\n"
+ "sh $a0, -256($a1)\n"
+ "sh $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "sh $a0, 0($at)\n"
+
+ "sw $a0, 0($a0)\n"
+ "sw $a0, 0($a1)\n"
+ "sw $a0, 4($a1)\n"
+ "sw $a0, 256($a1)\n"
+ "sw $a0, 1000($a1)\n"
+ "sw $a0, 0x7FFC($a1)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sw $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sw $a0, 4($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "sw $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "sw $a0, 0($at)\n"
+ "sw $a0, -256($a1)\n"
+ "sw $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "sw $a0, 0($at)\n"
+
+ "sd $a0, 0($a0)\n"
+ "sd $a0, 0($a1)\n"
+ "sw $a0, 4($a1)\n"
+ "dsrl32 $t3, $a0, 0\n"
+ "sw $t3, 8($a1)\n"
+ "sd $a0, 256($a1)\n"
+ "sd $a0, 1000($a1)\n"
+ "ori $at, $zero, 0x7FF8\n"
+ "daddu $at, $at, $a1\n"
+ "sw $a0, 4($at)\n"
+ "dsrl32 $t3, $a0, 0\n"
+ "sw $t3, 8($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sd $a0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a1\n"
+ "sw $a0, 4($at)\n"
+ "dsrl32 $t3, $a0, 0\n"
+ "sw $t3, 8($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a1\n"
+ "sd $a0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a1\n"
+ "sd $a0, 0($at)\n"
+ "sd $a0, -256($a1)\n"
+ "sd $a0, -32768($a1)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a1\n"
+ "sd $a0, 0($at)\n";
+ DriverStr(expected, "StoreToOffset");
+}
+
+TEST_F(AssemblerMIPS64Test, StoreFpuToOffset) {
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 0);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 4);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 256);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 0x7FFC);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 0x8000);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 0x8004);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 0x10000);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 0x12345678);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, -256);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, -32768);
+ __ StoreFpuToOffset(mips64::kStoreWord, mips64::F0, mips64::A0, 0xABCDEF00);
+
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 0);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 4);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 256);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 0x7FFC);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 0x8000);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 0x8004);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 0x10000);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 0x12345678);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, -256);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, -32768);
+ __ StoreFpuToOffset(mips64::kStoreDoubleword, mips64::F0, mips64::A0, 0xABCDEF00);
+
+ const char* expected =
+ "swc1 $f0, 0($a0)\n"
+ "swc1 $f0, 4($a0)\n"
+ "swc1 $f0, 256($a0)\n"
+ "swc1 $f0, 0x7FFC($a0)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "swc1 $f0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "swc1 $f0, 4($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a0\n"
+ "swc1 $f0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a0\n"
+ "swc1 $f0, 0($at)\n"
+ "swc1 $f0, -256($a0)\n"
+ "swc1 $f0, -32768($a0)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a0\n"
+ "swc1 $f0, 0($at)\n"
+
+ "sdc1 $f0, 0($a0)\n"
+ "mfhc1 $t3, $f0\n"
+ "swc1 $f0, 4($a0)\n"
+ "sw $t3, 8($a0)\n"
+ "sdc1 $f0, 256($a0)\n"
+ "ori $at, $zero, 0x7FF8\n"
+ "daddu $at, $at, $a0\n"
+ "mfhc1 $t3, $f0\n"
+ "swc1 $f0, 4($at)\n"
+ "sw $t3, 8($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "sdc1 $f0, 0($at)\n"
+ "ori $at, $zero, 0x8000\n"
+ "daddu $at, $at, $a0\n"
+ "mfhc1 $t3, $f0\n"
+ "swc1 $f0, 4($at)\n"
+ "sw $t3, 8($at)\n"
+ "lui $at, 1\n"
+ "daddu $at, $at, $a0\n"
+ "sdc1 $f0, 0($at)\n"
+ "lui $at, 0x1234\n"
+ "ori $at, 0x5678\n"
+ "daddu $at, $at, $a0\n"
+ "sdc1 $f0, 0($at)\n"
+ "sdc1 $f0, -256($a0)\n"
+ "sdc1 $f0, -32768($a0)\n"
+ "lui $at, 0xABCD\n"
+ "ori $at, 0xEF00\n"
+ "daddu $at, $at, $a0\n"
+ "sdc1 $f0, 0($at)\n";
+ DriverStr(expected, "StoreFpuToOffset");
+}
+
+#undef __
+
} // namespace art
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index e57a540..2958dc6 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -89,11 +89,12 @@
DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
std::copy_n(DexFile::kDexMagic, 4u, header->magic_);
std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u);
- header->header_size_ = sizeof(header);
+ header->header_size_ = sizeof(DexFile::Header);
header->endian_tag_ = DexFile::kDexEndianConstant;
header->link_size_ = 0u; // Unused.
header->link_off_ = 0u; // Unused.
- header->map_off_ = 0u; // Unused.
+ header->map_off_ = 0u; // Unused. TODO: This is wrong. Dex files created by this builder
+ // cannot be verified. b/26808512
uint32_t data_section_size = 0u;
@@ -213,13 +214,22 @@
// Leave signature as zeros.
header->file_size_ = dex_file_data_.size();
+
+ // Write the complete header early, as part of it needs to be checksummed.
+ std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
+
+ // Checksum starts after the checksum field.
size_t skip = sizeof(header->magic_) + sizeof(header->checksum_);
- header->checksum_ = adler32(0u, dex_file_data_.data() + skip, dex_file_data_.size() - skip);
+ header->checksum_ = adler32(adler32(0L, Z_NULL, 0),
+ dex_file_data_.data() + skip,
+ dex_file_data_.size() - skip);
+
+ // Write the complete header again, just simpler that way.
std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
std::string error_msg;
std::unique_ptr<const DexFile> dex_file(DexFile::Open(
- &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg));
+ &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, false, &error_msg));
CHECK(dex_file != nullptr) << error_msg;
return dex_file;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6202384..8e80961 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -380,6 +380,10 @@
UsageError(" --multi-image: specify that separate oat and image files be generated for each "
"input dex file.");
UsageError("");
+ UsageError(" --force-determinism: force the compiler to emit a deterministic output.");
+ UsageError(" This option is incompatible with read barriers (e.g., if dex2oat has been");
+ UsageError(" built with the environment variable `ART_USE_READ_BARRIER` set to `true`).");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -560,7 +564,8 @@
dump_slow_timing_(kIsDebugBuild),
swap_fd_(-1),
app_image_fd_(kInvalidFd),
- timings_(timings) {}
+ timings_(timings),
+ force_determinism_(false) {}
~Dex2Oat() {
// Log completion time before deleting the runtime_, because this accesses
@@ -922,6 +927,15 @@
// Fill some values into the key-value store for the oat header.
key_value_store_.reset(new SafeMap<std::string, std::string>());
+
+ // Automatically force determinism for the boot image in a host
+ // build, except when read barriers are enabled, as the former
+ // switches the GC to a non-concurrent one by passing the
+ // option `-Xgc:nonconcurrent` (see below).
+ if (!kIsTargetBuild && IsBootImage() && !kEmitCompilerReadBarrier) {
+ force_determinism_ = true;
+ }
+ compiler_options_->force_determinism_ = force_determinism_;
}
void ExpandOatAndImageFilenames() {
@@ -1041,6 +1055,9 @@
key_value_store_->Put(
OatHeader::kDebuggableKey,
compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ key_value_store_->Put(
+ OatHeader::kExtractOnlyKey,
+ compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
}
// Parse the arguments from the command line. In case of an unrecognized option or impossible
@@ -1165,6 +1182,11 @@
multi_image_ = true;
} else if (option.starts_with("--no-inline-from=")) {
no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
+ } else if (option == "--force-determinism") {
+ if (kEmitCompilerReadBarrier) {
+ Usage("Cannot use --force-determinism with read barriers");
+ }
+ force_determinism_ = true;
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
@@ -1313,7 +1335,13 @@
return false;
}
- {
+ if (compiler_options_->IsExtractOnly()) {
+ // ExtractOnly oat files only contain non-quickened DEX code and are
+ // therefore independent of the image file.
+ image_file_location_oat_checksum_ = 0u;
+ image_file_location_oat_data_begin_ = 0u;
+ image_patch_delta_ = 0;
+ } else {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
std::vector<gc::space::ImageSpace*> image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
@@ -1357,6 +1385,7 @@
instruction_set_,
instruction_set_features_.get(),
key_value_store_.get(),
+ /* verify */ true,
&opened_dex_files_map,
&opened_dex_files)) {
return false;
@@ -1689,7 +1718,10 @@
if (IsImage()) {
// Update oat estimates.
- UpdateImageWriter(i);
+ DCHECK(image_writer_ != nullptr);
+ DCHECK_LT(i, oat_filenames_.size());
+
+ image_writer_->UpdateOatFile(oat_file.get(), oat_filenames_[i]);
}
VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i];
@@ -2111,6 +2143,21 @@
// foreground collector by default for dex2oat.
raw_options.push_back(std::make_pair("-XX:DisableHSpaceCompactForOOM", nullptr));
+ // If we're asked to be deterministic, ensure non-concurrent GC for determinism. Also
+ // force the free-list implementation for large objects.
+ if (compiler_options_->IsForceDeterminism()) {
+ raw_options.push_back(std::make_pair("-Xgc:nonconcurrent", nullptr));
+ raw_options.push_back(std::make_pair("-XX:LargeObjectSpace=freelist", nullptr));
+
+ // We also need to turn off the nonmoving space. For that, we need to disable HSpace
+ // compaction (done above) and ensure that neither foreground nor background collectors
+ // are concurrent.
+ raw_options.push_back(std::make_pair("-XX:BackgroundGC=nonconcurrent", nullptr));
+
+ // To make identity hashcode deterministic, set a known seed.
+ mirror::Object::SetHashCodeSeed(987654321U);
+ }
+
if (!Runtime::ParseOptions(raw_options, false, runtime_options)) {
LOG(ERROR) << "Failed to parse runtime options";
return false;
@@ -2316,14 +2363,6 @@
return res.substr(0, penultimate_slash) + res.substr(last_slash);
}
- // Update the estimate for the oat file with the given index.
- void UpdateImageWriter(size_t index) {
- DCHECK(image_writer_ != nullptr);
- DCHECK_LT(index, oat_filenames_.size());
-
- image_writer_->UpdateOatFile(oat_filenames_[index]);
- }
-
std::unique_ptr<CompilerOptions> compiler_options_;
Compiler::Kind compiler_kind_;
@@ -2414,6 +2453,9 @@
// Backing storage.
std::vector<std::string> char_backing_storage_;
+ // See CompilerOptions.force_determinism_.
+ bool force_determinism_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
};
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 7a0decb..af08fc4 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -153,6 +153,7 @@
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
+ AddTrampolineSymbols();
Walk(&art::OatSymbolizer::AddSymbol);
strtab->End();
@@ -165,6 +166,35 @@
return builder_->Good();
}
+ void AddTrampolineSymbol(const char* name, uint32_t code_offset) {
+ if (code_offset != 0) {
+ uint32_t name_offset = builder_->GetStrTab()->Write(name);
+ uint64_t symbol_value = code_offset - oat_file_->GetOatHeader().GetExecutableOffset();
+ // Specifying 0 as the symbol size means that the symbol lasts until the next symbol or until
+ // the end of the section in case of the last symbol.
+ builder_->GetSymTab()->Add(name_offset, builder_->GetText(), symbol_value,
+ /* is_relative */ true, /* size */ 0, STB_GLOBAL, STT_FUNC);
+ }
+ }
+
+ void AddTrampolineSymbols() {
+ const OatHeader& oat_header = oat_file_->GetOatHeader();
+ AddTrampolineSymbol("interpreterToInterpreterBridge",
+ oat_header.GetInterpreterToInterpreterBridgeOffset());
+ AddTrampolineSymbol("interpreterToCompiledCodeBridge",
+ oat_header.GetInterpreterToCompiledCodeBridgeOffset());
+ AddTrampolineSymbol("jniDlsymLookup",
+ oat_header.GetJniDlsymLookupOffset());
+ AddTrampolineSymbol("quickGenericJniTrampoline",
+ oat_header.GetQuickGenericJniTrampolineOffset());
+ AddTrampolineSymbol("quickImtConflictTrampoline",
+ oat_header.GetQuickImtConflictTrampolineOffset());
+ AddTrampolineSymbol("quickResolutionTrampoline",
+ oat_header.GetQuickResolutionTrampolineOffset());
+ AddTrampolineSymbol("quickToInterpreterBridge",
+ oat_header.GetQuickToInterpreterBridgeOffset());
+ }
+
void Walk(Callback callback) {
std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file_->GetOatDexFiles();
for (size_t i = 0; i < oat_dex_files.size(); i++) {
@@ -1654,7 +1684,7 @@
stats_.file_bytes = file->GetLength();
}
size_t header_bytes = sizeof(ImageHeader);
- const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
+ const auto& object_section = image_header_.GetImageSection(ImageHeader::kSectionObjects);
const auto& field_section = image_header_.GetImageSection(ImageHeader::kSectionArtFields);
const auto& method_section = image_header_.GetMethodsSection();
const auto& dex_cache_arrays_section = image_header_.GetImageSection(
@@ -1663,17 +1693,46 @@
ImageHeader::kSectionInternedStrings);
const auto& class_table_section = image_header_.GetImageSection(
ImageHeader::kSectionClassTable);
+ const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
+
stats_.header_bytes = header_bytes;
- stats_.alignment_bytes += RoundUp(header_bytes, kObjectAlignment) - header_bytes;
- // Add padding between the field and method section.
- // (Field section is 4-byte aligned, method section is 8-byte aligned on 64-bit targets.)
- stats_.alignment_bytes += method_section.Offset() -
- (field_section.Offset() + field_section.Size());
- // Add padding between the dex cache arrays section and the intern table. (Dex cache
- // arrays section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.)
- stats_.alignment_bytes += intern_section.Offset() -
- (dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size());
- stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize();
+
+ // Objects are kObjectAlignment-aligned.
+ // CHECK_EQ(RoundUp(header_bytes, kObjectAlignment), object_section.Offset());
+ if (object_section.Offset() > header_bytes) {
+ stats_.alignment_bytes += object_section.Offset() - header_bytes;
+ }
+
+ // Field section is 4-byte aligned.
+ constexpr size_t kFieldSectionAlignment = 4U;
+ uint32_t end_objects = object_section.Offset() + object_section.Size();
+ CHECK_EQ(RoundUp(end_objects, kFieldSectionAlignment), field_section.Offset());
+ stats_.alignment_bytes += field_section.Offset() - end_objects;
+
+ // Method section is 4/8 byte aligned depending on target. Just check for 4-byte alignment.
+ uint32_t end_fields = field_section.Offset() + field_section.Size();
+ CHECK_ALIGNED(method_section.Offset(), 4);
+ stats_.alignment_bytes += method_section.Offset() - end_fields;
+
+ // Dex cache arrays section is aligned depending on the target. Just check for 4-byte alignment.
+ uint32_t end_methods = method_section.Offset() + method_section.Size();
+ CHECK_ALIGNED(dex_cache_arrays_section.Offset(), 4);
+ stats_.alignment_bytes += dex_cache_arrays_section.Offset() - end_methods;
+
+ // Intern table is 8-byte aligned.
+ uint32_t end_caches = dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size();
+ CHECK_EQ(RoundUp(end_caches, 8U), intern_section.Offset());
+ stats_.alignment_bytes += intern_section.Offset() - end_caches;
+
+ // Add space between intern table and class table.
+ uint32_t end_intern = intern_section.Offset() + intern_section.Size();
+ stats_.alignment_bytes += class_table_section.Offset() - end_intern;
+
+ // Add space between class table and bitmap. Expect the bitmap to be page-aligned.
+ uint32_t end_ctable = class_table_section.Offset() + class_table_section.Size();
+ CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
+ stats_.alignment_bytes += bitmap_section.Offset() - end_ctable;
+
stats_.bitmap_bytes += bitmap_section.Size();
stats_.art_field_bytes += field_section.Size();
stats_.art_method_bytes += method_section.Size();
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index 5c8ff8f..4db9411 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -113,6 +113,25 @@
qpoints->pShrLong = nullptr;
qpoints->pUshrLong = nullptr;
+ // More math.
+ qpoints->pCos = cos;
+ qpoints->pSin = sin;
+ qpoints->pAcos = acos;
+ qpoints->pAsin = asin;
+ qpoints->pAtan = atan;
+ qpoints->pAtan2 = atan2;
+ qpoints->pCbrt = cbrt;
+ qpoints->pCosh = cosh;
+ qpoints->pExp = exp;
+ qpoints->pExpm1 = expm1;
+ qpoints->pHypot = hypot;
+ qpoints->pLog = log;
+ qpoints->pLog10 = log10;
+ qpoints->pNextAfter = nextafter;
+ qpoints->pSinh = sinh;
+ qpoints->pTan = tan;
+ qpoints->pTanh = tanh;
+
// Intrinsics
qpoints->pIndexOf = art_quick_indexof;
qpoints->pStringCompareTo = art_quick_string_compareto;
diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h
index 1d07d47..b027c95 100644
--- a/runtime/arch/mips64/registers_mips64.h
+++ b/runtime/arch/mips64/registers_mips64.h
@@ -61,6 +61,7 @@
RA = 31, // Return address.
TR = S1, // ART Thread Register
TMP = T8, // scratch register (in addition to AT)
+ TMP2 = T3, // scratch register (in addition to AT, reserved for assembler)
kNumberOfGpuRegisters = 32,
kNoGpuRegister = -1 // Signals an illegal register.
};
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 74eb722..28540c8 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -41,17 +41,15 @@
namespace art {
+template <ReadBarrierOption kReadBarrierOption>
inline mirror::Class* ArtMethod::GetDeclaringClassUnchecked() {
GcRootSource gc_root_source(this);
- return declaring_class_.Read(&gc_root_source);
+ return declaring_class_.Read<kReadBarrierOption>(&gc_root_source);
}
-inline mirror::Class* ArtMethod::GetDeclaringClassNoBarrier() {
- return declaring_class_.Read<kWithoutReadBarrier>();
-}
-
+template <ReadBarrierOption kReadBarrierOption>
inline mirror::Class* ArtMethod::GetDeclaringClass() {
- mirror::Class* result = GetDeclaringClassUnchecked();
+ mirror::Class* result = GetDeclaringClassUnchecked<kReadBarrierOption>();
if (kIsDebugBuild) {
if (!IsRuntimeMethod()) {
CHECK(result != nullptr) << this;
@@ -79,24 +77,28 @@
// AssertSharedHeld doesn't work in GetAccessFlags, so use a NO_THREAD_SAFETY_ANALYSIS helper.
// TODO: Figure out why ASSERT_SHARED_CAPABILITY doesn't work.
-ALWAYS_INLINE
-static inline void DoGetAccessFlagsHelper(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS {
- CHECK(method->IsRuntimeMethod() || method->GetDeclaringClass()->IsIdxLoaded() ||
- method->GetDeclaringClass()->IsErroneous());
+template <ReadBarrierOption kReadBarrierOption>
+ALWAYS_INLINE static inline void DoGetAccessFlagsHelper(ArtMethod* method)
+ NO_THREAD_SAFETY_ANALYSIS {
+ CHECK(method->IsRuntimeMethod() ||
+ method->GetDeclaringClass<kReadBarrierOption>()->IsIdxLoaded() ||
+ method->GetDeclaringClass<kReadBarrierOption>()->IsErroneous());
}
+template <ReadBarrierOption kReadBarrierOption>
inline uint32_t ArtMethod::GetAccessFlags() {
if (kIsDebugBuild) {
Thread* self = Thread::Current();
if (!Locks::mutator_lock_->IsSharedHeld(self)) {
ScopedObjectAccess soa(self);
- CHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() ||
- GetDeclaringClass()->IsErroneous());
+ CHECK(IsRuntimeMethod() ||
+ GetDeclaringClass<kReadBarrierOption>()->IsIdxLoaded() ||
+ GetDeclaringClass<kReadBarrierOption>()->IsErroneous());
} else {
// We cannot use SOA in this case. We might be holding the lock, but may not be in the
// runnable state (e.g., during GC).
Locks::mutator_lock_->AssertSharedHeld(self);
- DoGetAccessFlagsHelper(this);
+ DoGetAccessFlagsHelper<kReadBarrierOption>(this);
}
}
return access_flags_;
@@ -469,7 +471,7 @@
template <typename Visitor>
inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor) {
- mirror::Class* old_class = GetDeclaringClassNoBarrier();
+ mirror::Class* old_class = GetDeclaringClassUnchecked<kWithoutReadBarrier>();
mirror::Class* new_class = visitor(old_class);
if (old_class != new_class) {
SetDeclaringClass(new_class);
@@ -486,9 +488,9 @@
}
}
-template <typename Visitor>
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor) {
- if (IsNative()) {
+ if (IsNative<kReadBarrierOption>()) {
const void* old_native_code = GetEntryPointFromJni();
const void* new_native_code = visitor(old_native_code);
if (old_native_code != new_native_code) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 440e796..ce23c2a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -57,11 +57,10 @@
jobject jlr_method)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE mirror::Class* GetDeclaringClass() SHARED_REQUIRES(Locks::mutator_lock_);
- ALWAYS_INLINE mirror::Class* GetDeclaringClassNoBarrier()
- SHARED_REQUIRES(Locks::mutator_lock_);
-
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE mirror::Class* GetDeclaringClassUnchecked()
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -77,6 +76,7 @@
// Note: GetAccessFlags acquires the mutator lock in debug mode to check that it is not called for
// a proxy method.
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE uint32_t GetAccessFlags();
void SetAccessFlags(uint32_t new_access_flags) {
@@ -154,8 +154,9 @@
return (GetAccessFlags() & kAccDefault) != 0;
}
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
bool IsNative() {
- return (GetAccessFlags() & kAccNative) != 0;
+ return (GetAccessFlags<kReadBarrierOption>() & kAccNative) != 0;
}
bool IsFastNative() {
@@ -485,7 +486,7 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Update entry points by passing them through the visitor.
- template <typename Visitor>
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor);
protected:
diff --git a/runtime/atomic.h b/runtime/atomic.h
index 0faa3c6..d4a7f37 100644
--- a/runtime/atomic.h
+++ b/runtime/atomic.h
@@ -278,6 +278,10 @@
return this->fetch_add(value, std::memory_order_seq_cst); // Return old_value.
}
+ T FetchAndAddRelaxed(const T value) {
+ return this->fetch_add(value, std::memory_order_relaxed); // Return old_value.
+ }
+
T FetchAndSubSequentiallyConsistent(const T value) {
return this->fetch_sub(value, std::memory_order_seq_cst); // Return old value.
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index ea1afa8..7e8a4a4 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -116,6 +116,30 @@
return resolved_method;
}
+inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(Thread* self,
+ uint32_t method_idx,
+ ArtMethod* referrer) {
+ // NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is
+ // because if we did so than an invoke-super could be incorrectly dispatched in cases where
+ // GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass
+ // (super*-class?) of the referrer and the direct superclass of the referrer contains a concrete
+ // implementation of the method. If this class's implementation of the method is copied from an
+ // interface (either miranda, default or conflict) we would incorrectly assume that is what we
+ // want to invoke on, instead of the 'concrete' implementation that the direct superclass
+ // contains.
+ mirror::Class* declaring_class = referrer->GetDeclaringClass();
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ const DexFile* dex_file = h_dex_cache->GetDexFile();
+ const DexFile::MethodId& method = dex_file->GetMethodId(method_idx);
+ mirror::Class* resolved_type = h_dex_cache->GetResolvedType(method.class_idx_);
+ if (UNLIKELY(resolved_type == nullptr)) {
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+ resolved_type = ResolveType(*dex_file, method.class_idx_, h_dex_cache, class_loader);
+ }
+ return resolved_type;
+}
+
template <ClassLinker::ResolveMode kResolveMode>
inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
uint32_t method_idx,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index bb709e8..c739490 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -164,10 +164,12 @@
if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime.
std::string extra;
if (c->GetVerifyError() != nullptr) {
- mirror::Class* descr_from = c->GetVerifyError()->IsClass()
- ? c->GetVerifyError()->AsClass()
- : c->GetVerifyError()->GetClass();
- extra = PrettyDescriptor(descr_from);
+ mirror::Object* verify_error = c->GetVerifyError();
+ if (verify_error->IsClass()) {
+ extra = PrettyDescriptor(verify_error->AsClass());
+ } else {
+ extra = verify_error->AsThrowable()->Dump();
+ }
}
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c) << ": " << extra;
}
@@ -1180,11 +1182,15 @@
ClassTable* const table_;
};
-void ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
+bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
- bool added_class_table) {
+ bool added_class_table,
+ bool* out_forward_dex_cache_array,
+ std::string* out_error_msg) {
+ DCHECK(out_forward_dex_cache_array != nullptr);
+ DCHECK(out_error_msg != nullptr);
Thread* const self = Thread::Current();
gc::Heap* const heap = Runtime::Current()->GetHeap();
const ImageHeader& header = space->GetImageHeader();
@@ -1192,8 +1198,11 @@
// class loader fields.
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
ClassTable* table = InsertClassTableForClassLoader(class_loader.Get());
- // TODO: Store class table in the image to avoid manually adding the classes.
- for (int32_t i = 0, num_dex_caches = dex_caches->GetLength(); i < num_dex_caches; i++) {
+ // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
+ // rely on clobering the dex cache arrays in the image to forward to bss.
+ size_t num_dex_caches_with_bss_arrays = 0;
+ const size_t num_dex_caches = dex_caches->GetLength();
+ for (size_t i = 0; i < num_dex_caches; i++) {
mirror::DexCache* const dex_cache = dex_caches->Get(i);
const DexFile* const dex_file = dex_cache->GetDexFile();
// If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
@@ -1207,22 +1216,23 @@
CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
- if (dex_file->GetOatDexFile() != nullptr &&
- dex_file->GetOatDexFile()->GetDexCacheArrays() != nullptr) {
+ const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
+ if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
+ ++num_dex_caches_with_bss_arrays;
DexCacheArraysLayout layout(image_pointer_size_, dex_file);
- uint8_t* const raw_arrays = dex_file->GetOatDexFile()->GetDexCacheArrays();
- // The space is not yet visible to the GC, we can avoid the read barriers and use
- // std::copy_n.
+ uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
+ // The space is not yet visible to the GC, we can avoid the read barriers and use std::copy_n.
if (num_strings != 0u) {
+ GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings();
GcRoot<mirror::String>* const strings =
reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) {
DCHECK(strings[j].IsNull());
}
- std::copy_n(dex_cache->GetStrings(), num_strings, strings);
+ std::copy_n(image_resolved_strings, num_strings, strings);
+ *reinterpret_cast<GcRoot<mirror::String>**>(image_resolved_strings) = strings;
dex_cache->SetStrings(strings);
}
-
if (num_types != 0u) {
GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
GcRoot<mirror::Class>* const types =
@@ -1280,6 +1290,9 @@
// Update the class loader from the one in the image class loader to the one that loaded
// the app image.
klass->SetClassLoader(class_loader.Get());
+ // The resolved type could be from another dex cache, go through the dex cache just in
+ // case.
+ klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
// If there are multiple dex caches, there may be the same class multiple times
// in different dex caches. Check for this since inserting will add duplicates
// otherwise.
@@ -1324,7 +1337,6 @@
CHECK_EQ(table->LookupByDescriptor(super_class), super_class);
}
}
- DCHECK_EQ(klass->GetClassLoader(), class_loader.Get());
if (kIsDebugBuild) {
for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
@@ -1352,20 +1364,66 @@
}
}
}
- {
+ *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
+ if (*out_forward_dex_cache_array) {
+ if (num_dex_caches_with_bss_arrays != num_dex_caches) {
+ // Reject application image since we cannot forward only some of the dex cache arrays.
+ // TODO: We could get around this by having a dedicated forwarding slot. It should be an
+ // uncommon case.
+ *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
+ num_dex_caches_with_bss_arrays,
+ num_dex_caches);
+ return false;
+ }
FixupArtMethodArrayVisitor visitor(header);
header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
- &visitor, space->Begin(), sizeof(void*));
+ &visitor,
+ space->Begin(),
+ sizeof(void*));
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
}
if (kIsDebugBuild) {
ClassTable* const class_table = class_loader.Get()->GetClassTable();
VerifyClassInTableArtMethodVisitor visitor2(class_table);
header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
- &visitor2, space->Begin(), sizeof(void*));
+ &visitor2,
+ space->Begin(),
+ sizeof(void*));
}
+ return true;
}
+class UpdateClassLoaderAndResolvedStringsVisitor {
+ public:
+ UpdateClassLoaderAndResolvedStringsVisitor(gc::space::ImageSpace* space,
+ mirror::ClassLoader* class_loader,
+ bool forward_strings)
+ : space_(space),
+ class_loader_(class_loader),
+ forward_strings_(forward_strings) {}
+
+ bool operator()(mirror::Class* klass) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (forward_strings_) {
+ GcRoot<mirror::String>* strings = klass->GetDexCacheStrings();
+ if (strings != nullptr) {
+ DCHECK(space_->GetImageHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
+ reinterpret_cast<uint8_t*>(strings) - space_->Begin()))
+ << "String dex cache array for " << PrettyClass(klass) << " is not in app image";
+ GcRoot<mirror::String>* new_strings = *reinterpret_cast<GcRoot<mirror::String>**>(strings);
+ DCHECK_NE(strings, new_strings);
+ klass->SetDexCacheStrings(new_strings);
+ }
+ }
+ // Finally, update class loader.
+ klass->SetClassLoader(class_loader_);
+ return true;
+ }
+
+ gc::space::ImageSpace* const space_;
+ mirror::ClassLoader* const class_loader_;
+ const bool forward_strings_;
+};
+
bool ClassLinker::AddImageSpace(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
@@ -1533,6 +1591,14 @@
}
}
if (!equal) {
+ VLOG(image) << "Image dex files " << image_dex_file_names.size();
+ for (mirror::String* name : image_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
+ }
+ VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
+ for (mirror::String* name : loader_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
+ }
*error_msg = "Rejecting application image due to class loader mismatch";
return false;
}
@@ -1566,21 +1632,39 @@
if (app_image) {
GetOrCreateAllocatorForClassLoader(class_loader.Get()); // Make sure we have a linear alloc.
}
- if (class_table_section.Size() > 0u) {
- const uint64_t start_time2 = NanoTime();
+ ClassTable* class_table = nullptr;
+ {
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get());
- class_table->ReadFromMemory(space->Begin() + class_table_section.Offset());
- if (app_image) {
- class_table->SetClassLoader(class_loader.Get());
- } else {
- dex_cache_boot_image_class_lookup_required_ = false;
+ class_table = InsertClassTableForClassLoader(class_loader.Get());
+ if (class_table_section.Size() > 0u) {
+ const uint64_t start_time2 = NanoTime();
+ class_table->ReadFromMemory(space->Begin() + class_table_section.Offset());
+ if (!app_image) {
+ dex_cache_boot_image_class_lookup_required_ = false;
+ }
+ VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2);
+ added_class_table = true;
}
- VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2);
- added_class_table = true;
}
if (app_image) {
- UpdateAppImageClassLoadersAndDexCaches(space, class_loader, dex_caches, added_class_table);
+ bool forward_dex_cache_arrays = false;
+ if (!UpdateAppImageClassLoadersAndDexCaches(space,
+ class_loader,
+ dex_caches,
+ added_class_table,
+ /*out*/&forward_dex_cache_arrays,
+ /*out*/error_msg)) {
+ return false;
+ }
+ if (added_class_table) {
+ WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ // Update class loader and resolved strings. If added_class_table is false, the resolved
+ // strings were already updated in UpdateAppImageClassLoadersAndDexCaches.
+ UpdateClassLoaderAndResolvedStringsVisitor visitor(space,
+ class_loader.Get(),
+ forward_dex_cache_arrays);
+ class_table->Visit(visitor);
+ }
}
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
@@ -1667,7 +1751,7 @@
void Visit(mirror::ClassLoader* class_loader)
SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
ClassTable* const class_table = class_loader->GetClassTable();
- if (!done_ && class_table != nullptr && !class_table->Visit(visitor_)) {
+ if (!done_ && class_table != nullptr && !class_table->Visit(*visitor_)) {
// If the visitor ClassTable returns false it means that we don't need to continue.
done_ = true;
}
@@ -1680,7 +1764,7 @@
};
void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) {
- if (boot_class_table_.Visit(visitor)) {
+ if (boot_class_table_.Visit(*visitor)) {
VisitClassLoaderClassesVisitor loader_visitor(visitor);
VisitClassLoaders(&loader_visitor);
}
@@ -1703,7 +1787,7 @@
class GetClassesInToVector : public ClassVisitor {
public:
- bool Visit(mirror::Class* klass) OVERRIDE {
+ bool operator()(mirror::Class* klass) OVERRIDE {
classes_.push_back(klass);
return true;
}
@@ -1715,7 +1799,7 @@
explicit GetClassInToObjectArray(mirror::ObjectArray<mirror::Class>* arr)
: arr_(arr), index_(0) {}
- bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool operator()(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
++index_;
if (index_ <= arr_->GetLength()) {
arr_->Set(index_ - 1, klass);
@@ -1736,16 +1820,17 @@
void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) {
// TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
// is avoiding duplicates.
+ Thread* const self = Thread::Current();
if (!kMovingClasses) {
+ ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
GetClassesInToVector accumulator;
VisitClasses(&accumulator);
for (mirror::Class* klass : accumulator.classes_) {
- if (!visitor->Visit(klass)) {
+ if (!visitor->operator()(klass)) {
return;
}
}
} else {
- Thread* const self = Thread::Current();
StackHandleScope<1> hs(self);
auto classes = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
// We size the array assuming classes won't be added to the class table during the visit.
@@ -1773,7 +1858,7 @@
// the class table grew then the loop repeats. If classes are created after the loop has
// finished then we don't visit.
mirror::Class* klass = classes->Get(i);
- if (klass != nullptr && !visitor->Visit(klass)) {
+ if (klass != nullptr && !visitor->operator()(klass)) {
return;
}
}
@@ -3656,7 +3741,7 @@
}
self->AssertNoPendingException();
// Make sure all classes referenced by catch blocks are resolved.
- ResolveClassExceptionHandlerTypes(dex_file, klass);
+ ResolveClassExceptionHandlerTypes(klass);
if (verifier_failure == verifier::MethodVerifier::kNoFailure) {
// Even though there were no verifier failures we need to respect whether the super-class and
// super-default-interfaces were verified or requiring runtime reverification.
@@ -3792,17 +3877,16 @@
UNREACHABLE();
}
-void ClassLinker::ResolveClassExceptionHandlerTypes(const DexFile& dex_file,
- Handle<mirror::Class> klass) {
+void ClassLinker::ResolveClassExceptionHandlerTypes(Handle<mirror::Class> klass) {
for (ArtMethod& method : klass->GetMethods(image_pointer_size_)) {
- ResolveMethodExceptionHandlerTypes(dex_file, &method);
+ ResolveMethodExceptionHandlerTypes(&method);
}
}
-void ClassLinker::ResolveMethodExceptionHandlerTypes(const DexFile& dex_file,
- ArtMethod* method) {
+void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) {
// similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod.
- const DexFile::CodeItem* code_item = dex_file.GetCodeItem(method->GetCodeItemOffset());
+ const DexFile::CodeItem* code_item =
+ method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset());
if (code_item == nullptr) {
return; // native or abstract method
}
@@ -5612,10 +5696,10 @@
k,
iface,
image_pointer_size_)) {
- LOG(WARNING) << "Conflicting default method implementations found: "
- << PrettyMethod(current_method) << " and "
- << PrettyMethod(*out_default_method) << " in class "
- << PrettyClass(klass.Get()) << " conflict.";
+ VLOG(class_linker) << "Conflicting default method implementations found: "
+ << PrettyMethod(current_method) << " and "
+ << PrettyMethod(*out_default_method) << " in class "
+ << PrettyClass(klass.Get()) << " conflict.";
*out_default_method = nullptr;
return DefaultMethodSearchResult::kDefaultConflict;
} else {
@@ -6245,15 +6329,7 @@
} // case kAbstractFound
}
if (LIKELY(fill_tables)) {
- if (current_method != nullptr) {
- // We found a default method implementation. Record it in the iftable and IMT.
- method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
- SetIMTRef(unimplemented_method,
- imt_conflict_method,
- image_pointer_size_,
- current_method,
- /*out*/imt_ptr);
- } else if (!super_interface) {
+ if (current_method == nullptr && !super_interface) {
// We could not find an implementation for this method and since it is a brand new
// interface we searched the entire vtable (and all default methods) for an
// implementation but couldn't find one. We therefore need to make a miranda method.
@@ -6269,7 +6345,17 @@
new(miranda_method) ArtMethod(interface_method, image_pointer_size_);
miranda_methods.push_back(miranda_method);
}
- method_array->SetElementPtrSize(j, miranda_method, image_pointer_size_);
+ current_method = miranda_method;
+ }
+
+ if (current_method != nullptr) {
+ // We found a default method implementation. Record it in the iftable and IMT.
+ method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
+ SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ image_pointer_size_,
+ current_method,
+ /*out*/imt_ptr);
}
}
} // For each method in interface end.
@@ -7146,7 +7232,7 @@
public:
explicit DumpClassVisitor(int flags) : flags_(flags) {}
- bool Visit(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool operator()(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
klass->DumpClass(LOG(ERROR), flags_);
return true;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 4975c29..9217c32 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -60,6 +60,13 @@
enum VisitRootFlags : uint8_t;
+class ClassVisitor {
+ public:
+ virtual ~ClassVisitor() {}
+ // Return true to continue visiting.
+ virtual bool operator()(mirror::Class* klass) = 0;
+};
+
class ClassLoaderVisitor {
public:
virtual ~ClassLoaderVisitor() {}
@@ -284,6 +291,15 @@
ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer)
SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be
+ // different then the declaring class of the resolved method due to copied
+ // miranda/default/conflict methods.
+ mirror::Class* ResolveReferencedClassOfMethod(Thread* self,
+ uint32_t method_idx,
+ ArtMethod* referrer)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_, !Roles::uninterruptible_);
template <ResolveMode kResolveMode>
ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type)
SHARED_REQUIRES(Locks::mutator_lock_)
@@ -432,11 +448,10 @@
mirror::Class::Status& oat_file_class_status)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
- void ResolveClassExceptionHandlerTypes(const DexFile& dex_file,
- Handle<mirror::Class> klass)
+ void ResolveClassExceptionHandlerTypes(Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
- void ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, ArtMethod* klass)
+ void ResolveMethodExceptionHandlerTypes(ArtMethod* klass)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
@@ -999,11 +1014,13 @@
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!Locks::classlinker_classes_lock_);
- void UpdateAppImageClassLoadersAndDexCaches(
+ bool UpdateAppImageClassLoadersAndDexCaches(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
- bool added_class_table)
+ bool added_class_table,
+ bool* out_forward_dex_cache_array,
+ std::string* out_error_msg)
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index aef02b6..e512906 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -28,6 +28,9 @@
visitor.VisitRoot(root.AddressWithoutBarrier());
}
}
+ for (GcRoot<mirror::Object>& root : dex_files_) {
+ visitor.VisitRoot(root.AddressWithoutBarrier());
+ }
}
template<class Visitor>
@@ -42,6 +45,19 @@
}
}
+template <typename Visitor>
+bool ClassTable::Visit(Visitor& visitor) {
+ for (ClassSet& class_set : classes_) {
+ for (GcRoot<mirror::Class>& root : class_set) {
+ if (!visitor(root.Read())) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
} // namespace art
#endif // ART_RUNTIME_CLASS_TABLE_INL_H_
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 2a4f0e0..afb0556 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -73,17 +73,6 @@
return existing;
}
-bool ClassTable::Visit(ClassVisitor* visitor) {
- for (ClassSet& class_set : classes_) {
- for (GcRoot<mirror::Class>& root : class_set) {
- if (!visitor->Visit(root.Read())) {
- return false;
- }
- }
- }
- return true;
-}
-
size_t ClassTable::NumZygoteClasses() const {
size_t sum = 0;
for (size_t i = 0; i < classes_.size() - 1; ++i) {
@@ -183,12 +172,4 @@
return read_count;
}
-void ClassTable::SetClassLoader(mirror::ClassLoader* class_loader) {
- for (const ClassSet& class_set : classes_) {
- for (const GcRoot<mirror::Class>& root : class_set) {
- root.Read()->SetClassLoader(class_loader);
- }
- }
-}
-
} // namespace art
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 0b42035..5f2eb48 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -36,13 +36,6 @@
class ClassLoader;
} // namespace mirror
-class ClassVisitor {
- public:
- virtual ~ClassVisitor() {}
- // Return true to continue visiting.
- virtual bool Visit(mirror::Class* klass) = 0;
-};
-
// Each loader has a ClassTable
class ClassTable {
public:
@@ -80,8 +73,9 @@
NO_THREAD_SAFETY_ANALYSIS
SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
- // Return false if the callback told us to exit.
- bool Visit(ClassVisitor* visitor)
+ // Stops visit if the visitor returns false.
+ template <typename Visitor>
+ bool Visit(Visitor& visitor)
SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
// Return the first class that matches the descriptor. Returns null if there are none.
@@ -118,11 +112,6 @@
REQUIRES(Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Change the class loader of all the contained classes.
- void SetClassLoader(mirror::ClassLoader* class_loader)
- REQUIRES(Locks::classlinker_classes_lock_)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
private:
class ClassDescriptorHashEquals {
public:
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index a0f875d..904490a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -983,7 +983,7 @@
public:
explicit ClassListCreator(std::vector<JDWP::RefTypeId>* classes) : classes_(classes) {}
- bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ bool operator()(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
if (!c->IsPrimitive()) {
classes_->push_back(Dbg::GetObjectRegistry()->AddRefType(c));
}
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 9b93c13..81a3e4b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -195,6 +195,30 @@
}
}
+std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ std::string* error_msg) {
+ std::unique_ptr<const DexFile> dex_file = OpenMemory(base,
+ size,
+ location,
+ location_checksum,
+ nullptr,
+ oat_dex_file,
+ error_msg);
+ if (verify && !DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location.c_str(),
+ error_msg)) {
+ return nullptr;
+ }
+
+ return dex_file;
+}
+
std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify,
std::string* error_msg) {
CHECK(location != nullptr);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 200121e..e497e9c 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -417,9 +417,8 @@
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
- std::string* error_msg) {
- return OpenMemory(base, size, location, location_checksum, nullptr, oat_dex_file, error_msg);
- }
+ bool verify,
+ std::string* error_msg);
// Open all classesXXX.dex files from a zip archive.
static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 638fdb4..24986253 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1036,8 +1036,15 @@
} else {
DCHECK_EQ(invoke_type, kSuper);
CHECK(caller != nullptr) << invoke_type;
- called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry(
- called->GetMethodIndex(), sizeof(void*));
+ // TODO Maybe put this into a mirror::Class function.
+ mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod(
+ self, called_method.dex_method_index, caller);
+ if (ref_class->IsInterface()) {
+ called = ref_class->FindVirtualMethodForInterfaceSuper(called, sizeof(void*));
+ } else {
+ called = caller->GetDeclaringClass()->GetSuperClass()->GetVTableEntry(
+ called->GetMethodIndex(), sizeof(void*));
+ }
}
CHECK(called != nullptr) << PrettyMethod(orig_called) << " "
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 52ccbee..5c5abeb 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -146,43 +146,13 @@
}
}
-void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
- // BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...)
- //
- // If malloc calls abort, it will be holding its lock.
- // If the handler tries to call malloc, it will deadlock.
- VLOG(signals) << "Handling fault";
- if (IsInGeneratedCode(info, context, true)) {
- VLOG(signals) << "in generated code, looking for handler";
- for (const auto& handler : generated_code_handlers_) {
- VLOG(signals) << "invoking Action on handler " << handler;
- if (handler->Action(sig, info, context)) {
-#ifdef TEST_NESTED_SIGNAL
- // In test mode we want to fall through to stack trace handler
- // on every signal (in reality this will cause a crash on the first
- // signal).
- break;
-#else
- // We have handled a signal so it's time to return from the
- // signal handler to the appropriate place.
- return;
-#endif
- }
- }
- }
-
- // We hit a signal we didn't handle. This might be something for which
- // we can give more information about so call all registered handlers to see
- // if it is.
-
+bool FaultManager::HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context) {
Thread* self = Thread::Current();
- // If ART is not running, or the thread is not attached to ART pass the
- // signal on to the next handler in the chain.
- if (self == nullptr || Runtime::Current() == nullptr || !Runtime::Current()->IsStarted()) {
- InvokeUserSignalHandler(sig, info, context);
- return;
- }
+ DCHECK(self != nullptr);
+ DCHECK(Runtime::Current() != nullptr);
+ DCHECK(Runtime::Current()->IsStarted());
+
// Now set up the nested signal handler.
// TODO: add SIGSEGV back to the nested signals when we can handle running out stack gracefully.
@@ -231,6 +201,7 @@
break;
}
}
+
if (success) {
// Save the current state and call the handlers. If anything causes a signal
// our nested signal handler will be invoked and this will longjmp to the saved
@@ -247,7 +218,7 @@
}
}
fault_manager.Init();
- return;
+ return true;
}
}
} else {
@@ -265,6 +236,40 @@
// Now put the fault manager back in place.
fault_manager.Init();
+ return false;
+}
+
+void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) {
+ // BE CAREFUL ALLOCATING HERE INCLUDING USING LOG(...)
+ //
+ // If malloc calls abort, it will be holding its lock.
+ // If the handler tries to call malloc, it will deadlock.
+ VLOG(signals) << "Handling fault";
+ if (IsInGeneratedCode(info, context, true)) {
+ VLOG(signals) << "in generated code, looking for handler";
+ for (const auto& handler : generated_code_handlers_) {
+ VLOG(signals) << "invoking Action on handler " << handler;
+ if (handler->Action(sig, info, context)) {
+#ifdef TEST_NESTED_SIGNAL
+ // In test mode we want to fall through to stack trace handler
+ // on every signal (in reality this will cause a crash on the first
+ // signal).
+ break;
+#else
+ // We have handled a signal so it's time to return from the
+ // signal handler to the appropriate place.
+ return;
+#endif
+ }
+ }
+
+ // We hit a signal we didn't handle. This might be something for which
+ // we can give more information about so call all registered handlers to see
+ // if it is.
+ if (HandleFaultByOtherHandlers(sig, info, context)) {
+ return;
+ }
+ }
// Set a breakpoint in this function to catch unhandled signals.
art_sigsegv_fault();
@@ -344,7 +349,7 @@
// Check that the class pointer inside the object is not null and is aligned.
// TODO: Method might be not a heap address, and GetClass could fault.
// No read barrier because method_obj may not be a real object.
- mirror::Class* cls = method_obj->GetDeclaringClassNoBarrier();
+ mirror::Class* cls = method_obj->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
if (cls == nullptr) {
VLOG(signals) << "not a class";
return false;
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 3b03a14..625b1e8 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -62,6 +62,10 @@
NO_THREAD_SAFETY_ANALYSIS;
private:
+ // The HandleFaultByOtherHandlers function is only called by HandleFault function for generated code.
+ bool HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context)
+ NO_THREAD_SAFETY_ANALYSIS;
+
std::vector<FaultHandler*> generated_code_handlers_;
std::vector<FaultHandler*> other_handlers_;
struct sigaction oldaction_;
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index d1ab587..f437830 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -145,9 +145,9 @@
WriteBarrierField(obj, mirror::Object::ClassOffset(), klass);
}
pre_fence_visitor(obj, usable_size);
+ QuasiAtomic::ThreadFenceForConstructor();
new_num_bytes_allocated = static_cast<size_t>(
- num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_tl_bulk_allocated))
- + bytes_tl_bulk_allocated;
+ num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated)) + bytes_tl_bulk_allocated;
}
if (kIsDebugBuild && Runtime::Current()->IsStarted()) {
CHECK_LE(obj->SizeOf(), usable_size);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3964539..d76a8d1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -114,6 +114,9 @@
// timeout on how long we wait for finalizers to run. b/21544853
static constexpr uint64_t kNativeAllocationFinalizeTimeout = MsToNs(250u);
+// For deterministic compilation, we need the heap to be at a well-known address.
+static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
+
Heap::Heap(size_t initial_size,
size_t growth_limit,
size_t min_free,
@@ -352,6 +355,15 @@
}
std::unique_ptr<MemMap> main_mem_map_1;
std::unique_ptr<MemMap> main_mem_map_2;
+
+ // Gross hack to make dex2oat deterministic.
+ if (foreground_collector_type_ == kCollectorTypeMS &&
+ requested_alloc_space_begin == nullptr &&
+ Runtime::Current()->IsAotCompiler()) {
+ // Currently only enabled for MS collector since that is what the deterministic dex2oat uses.
+ // b/26849108
+ requested_alloc_space_begin = reinterpret_cast<uint8_t*>(kAllocSpaceBeginForDeterministicAoT);
+ }
uint8_t* request_begin = requested_alloc_space_begin;
if (request_begin != nullptr && separate_non_moving_space) {
request_begin += non_moving_space_capacity;
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 5e7f1a2..8356814 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -277,11 +277,7 @@
MutexLock mu2(self, *Locks::reference_queue_finalizer_references_lock_);
if (!reference->IsEnqueued()) {
CHECK(reference->IsFinalizerReferenceInstance());
- if (Runtime::Current()->IsActiveTransaction()) {
- reference->SetPendingNext<true>(reference);
- } else {
- reference->SetPendingNext<false>(reference);
- }
+ reference->SetPendingNext(reference);
return true;
}
return false;
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index 56957ba..67dcc2d 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -49,17 +49,9 @@
list_ = ref;
} else {
mirror::Reference* head = list_->GetPendingNext();
- if (Runtime::Current()->IsActiveTransaction()) {
- ref->SetPendingNext<true>(head);
- } else {
- ref->SetPendingNext<false>(head);
- }
+ ref->SetPendingNext(head);
}
- if (Runtime::Current()->IsActiveTransaction()) {
- list_->SetPendingNext<true>(ref);
- } else {
- list_->SetPendingNext<false>(ref);
- }
+ list_->SetPendingNext(ref);
}
mirror::Reference* ReferenceQueue::DequeuePendingReference() {
@@ -74,18 +66,10 @@
list_ = nullptr;
} else {
mirror::Reference* next = head->GetPendingNext();
- if (Runtime::Current()->IsActiveTransaction()) {
- list_->SetPendingNext<true>(next);
- } else {
- list_->SetPendingNext<false>(next);
- }
+ list_->SetPendingNext(next);
ref = head;
}
- if (Runtime::Current()->IsActiveTransaction()) {
- ref->SetPendingNext<true>(nullptr);
- } else {
- ref->SetPendingNext<false>(nullptr);
- }
+ ref->SetPendingNext(nullptr);
Heap* heap = Runtime::Current()->GetHeap();
if (kUseBakerOrBrooksReadBarrier && heap->CurrentCollectorType() == kCollectorTypeCC &&
heap->ConcurrentCopyingCollector()->IsActive()) {
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 891e280..08b1a00 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1041,29 +1041,29 @@
auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)->
AsObjectArray<mirror::DexCache>();
for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) {
- mirror::DexCache* dex_cache = dex_caches->Get(i);
+ mirror::DexCache* dex_cache = dex_caches->Get<kVerifyNone, kWithoutReadBarrier>(i);
// Fix up dex cache pointers.
GcRoot<mirror::String>* strings = dex_cache->GetStrings();
if (strings != nullptr) {
GcRoot<mirror::String>* new_strings = fixup_adapter.ForwardObject(strings);
if (strings != new_strings) {
- dex_cache->SetFieldPtr64<false>(mirror::DexCache::StringsOffset(), new_strings);
+ dex_cache->SetStrings(new_strings);
}
- dex_cache->FixupStrings(new_strings, fixup_adapter);
+ dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
}
GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
if (types != nullptr) {
GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types);
if (types != new_types) {
- dex_cache->SetFieldPtr64<false>(mirror::DexCache::ResolvedTypesOffset(), new_types);
+ dex_cache->SetResolvedTypes(new_types);
}
- dex_cache->FixupResolvedTypes(new_types, fixup_adapter);
+ dex_cache->FixupResolvedTypes<kWithoutReadBarrier>(new_types, fixup_adapter);
}
ArtMethod** methods = dex_cache->GetResolvedMethods();
if (methods != nullptr) {
ArtMethod** new_methods = fixup_adapter.ForwardObject(methods);
if (methods != new_methods) {
- dex_cache->SetFieldPtr64<false>(mirror::DexCache::ResolvedMethodsOffset(), new_methods);
+ dex_cache->SetResolvedMethods(new_methods);
}
for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) {
ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, sizeof(void*));
@@ -1077,7 +1077,7 @@
if (fields != nullptr) {
ArtField** new_fields = fixup_adapter.ForwardObject(fields);
if (fields != new_fields) {
- dex_cache->SetFieldPtr64<false>(mirror::DexCache::ResolvedFieldsOffset(), new_fields);
+ dex_cache->SetResolvedFields(new_fields);
}
for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) {
ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, sizeof(void*));
@@ -1167,6 +1167,20 @@
return nullptr;
}
+ if (oat_file != nullptr) {
+ // If we have an oat file, check the oat file checksum. The oat file is only non-null for the
+ // app image case. Otherwise, we open the oat file after the image and check the checksum there.
+ const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
+ const uint32_t image_oat_checksum = image_header->GetOatChecksum();
+ if (oat_checksum != image_oat_checksum) {
+ *error_msg = StringPrintf("Oat checksum 0x%x does not match the image one 0x%x in image %s",
+ oat_checksum,
+ image_oat_checksum,
+ image_filename);
+ return nullptr;
+ }
+ }
+
if (VLOG_IS_ON(startup)) {
LOG(INFO) << "Dumping image sections";
for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
diff --git a/runtime/image.cc b/runtime/image.cc
index de00343..1f54e3e 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '6', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '7', '\0' };
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index c57b1bb..7484635 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -55,7 +55,7 @@
explicit InstallStubsClassVisitor(Instrumentation* instrumentation)
: instrumentation_(instrumentation) {}
- bool Visit(mirror::Class* klass) OVERRIDE REQUIRES(Locks::mutator_lock_) {
+ bool operator()(mirror::Class* klass) OVERRIDE REQUIRES(Locks::mutator_lock_) {
instrumentation_->InstallStubsForClass(klass);
return true; // we visit all classes.
}
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index ec63fdf..e93bbdb 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -26,6 +26,7 @@
#include "stack.h"
#include "unstarted_runtime.h"
#include "mterp/mterp.h"
+#include "jit/jit.h"
namespace art {
namespace interpreter {
@@ -280,6 +281,33 @@
ShadowFrame& shadow_frame, JValue result_register) {
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
+ if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization.
+ if (kIsDebugBuild) {
+ self->AssertNoPendingException();
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ ArtMethod *method = shadow_frame.GetMethod();
+
+ if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
+ instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ method, 0);
+ }
+
+ if (UNLIKELY(Runtime::Current()->GetJit() != nullptr &&
+ Runtime::Current()->GetJit()->JitAtFirstUse() &&
+ method->HasAnyCompiledCode())) {
+ JValue result;
+
+ // Pop the shadow frame before calling into compiled code.
+ self->PopShadowFrame();
+ ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
+ // Push the shadow frame back as the caller will expect it.
+ self->PushShadowFrame(&shadow_frame);
+
+ return result;
+ }
+ }
+
shadow_frame.GetMethod()->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
bool transaction_active = Runtime::Current()->IsActiveTransaction();
@@ -308,11 +336,11 @@
return result_register;
} else {
// Mterp didn't like that instruction. Single-step it with the reference interpreter.
- JValue res = ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame,
+ result_register = ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame,
result_register, true);
if (shadow_frame.GetDexPC() == DexFile::kDexNoIndex) {
// Single-stepped a return or an exception not handled locally. Return to caller.
- return res;
+ return result_register;
}
}
}
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index d50cfe6..940d344 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -21,7 +21,6 @@
#include "base/stl_util.h" // MakeUnique
#include "experimental_flags.h"
#include "interpreter_common.h"
-#include "jit/jit.h"
#include "safe_math.h"
#include <memory> // std::unique_ptr
@@ -179,33 +178,6 @@
uint16_t inst_data;
const void* const* currentHandlersTable;
UPDATE_HANDLER_TABLE();
- if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.
- if (kIsDebugBuild) {
- self->AssertNoPendingException();
- }
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- ArtMethod *method = shadow_frame.GetMethod();
-
- if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
- instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
- method, 0);
- }
-
- if (UNLIKELY(Runtime::Current()->GetJit() != nullptr &&
- Runtime::Current()->GetJit()->JitAtFirstUse() &&
- method->HasAnyCompiledCode())) {
- JValue result;
-
- // Pop the shadow frame before calling into compiled code.
- self->PopShadowFrame();
- ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
- // Push the shadow frame back as the caller will expect it.
- self->PushShadowFrame(&shadow_frame);
-
- return result;
- }
- }
-
std::unique_ptr<lambda::ClosureBuilder> lambda_closure_builder;
size_t lambda_captured_variable_index = 0;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 56a213c..f606978 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -17,7 +17,6 @@
#include "base/stl_util.h" // MakeUnique
#include "experimental_flags.h"
#include "interpreter_common.h"
-#include "jit/jit.h"
#include "safe_math.h"
#include <memory> // std::unique_ptr
@@ -93,32 +92,6 @@
uint32_t dex_pc = shadow_frame.GetDexPC();
const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
- if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing.
- if (kIsDebugBuild) {
- self->AssertNoPendingException();
- }
-
- ArtMethod* method = shadow_frame.GetMethod();
-
- if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
- instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
- method, 0);
- }
-
- if (UNLIKELY(Runtime::Current()->GetJit() != nullptr &&
- Runtime::Current()->GetJit()->JitAtFirstUse() &&
- method->HasAnyCompiledCode())) {
- JValue result;
-
- // Pop the shadow frame before calling into compiled code.
- self->PopShadowFrame();
- ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
- // Push the shadow frame back as the caller will expect it.
- self->PushShadowFrame(&shadow_frame);
-
- return result;
- }
- }
const uint16_t* const insns = code_item->insns_;
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data;
@@ -2410,7 +2383,7 @@
} while (!interpret_one_instruction);
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
- return JValue();
+ return result_register;
} // NOLINT(readability/fn_size)
// Explicit definitions of ExecuteSwitchImpl.
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index f540814..b711181 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -167,6 +167,7 @@
// Don't compile the method if we are supposed to be deoptimized.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (instrumentation->AreAllMethodsDeoptimized() || instrumentation->IsDeoptimized(method)) {
+ VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to deoptimization";
return false;
}
@@ -238,11 +239,11 @@
void Jit::DumpTypeInfoForLoadedTypes(ClassLinker* linker) {
struct CollectClasses : public ClassVisitor {
- bool Visit(mirror::Class* klass) override {
+ bool operator()(mirror::Class* klass) override {
classes_.push_back(klass);
return true;
}
- std::vector<mirror::Class*> classes_;
+ mutable std::vector<mirror::Class*> classes_;
};
if (generate_debug_info_) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index bec6e13..f325949 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -321,8 +321,8 @@
code_size);
}
- __builtin___clear_cache(reinterpret_cast<char*>(code_ptr),
- reinterpret_cast<char*>(code_ptr + code_size));
+ FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
+ reinterpret_cast<char*>(code_ptr + code_size));
number_of_compilations_++;
}
// We need to update the entry point in the runnable state for the instrumentation.
@@ -737,6 +737,7 @@
if (ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
return false;
}
+
MutexLock mu(self, lock_);
ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*));
if (info == nullptr || info->IsMethodBeingCompiled()) {
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index ddaf02f..ab72373 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -68,8 +68,13 @@
return !classes_[1].IsNull() && classes_[kIndividualCacheSize - 1].IsNull();
}
- private:
+ mirror::Class* GetTypeAt(size_t i) const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return classes_[i].Read();
+ }
+
static constexpr uint16_t kIndividualCacheSize = 5;
+
+ private:
uint32_t dex_pc_;
GcRoot<mirror::Class> classes_[kIndividualCacheSize];
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index 3429962..b829934 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -24,6 +24,13 @@
#define NATIVE_METHOD(className, functionName, signature) \
{ #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
#endif
+
+// TODO: Can we do a better job of supporting overloading ?
+#ifndef OVERLOADED_NATIVE_METHOD
+#define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \
+ { #functionName, signature, reinterpret_cast<void*>(className ## _ ## identifier) }
+#endif
+
#define REGISTER_NATIVE_METHODS(jni_class_name) \
RegisterNativeMethods(env, jni_class_name, gMethods, arraysize(gMethods))
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index b6f424b..b3439f7 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -381,19 +381,26 @@
return (T)static_cast<uintptr_t>(AsIntArray()->GetWithoutChecks(idx));
}
-template<bool kTransactionActive, bool kUnchecked, typename T>
-inline void PointerArray::SetElementPtrSize(uint32_t idx, T element, size_t ptr_size) {
+template<bool kTransactionActive, bool kUnchecked>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size) {
if (ptr_size == 8) {
(kUnchecked ? down_cast<LongArray*>(static_cast<Object*>(this)) : AsLongArray())->
- SetWithoutChecks<kTransactionActive>(idx, (uint64_t)(element));
+ SetWithoutChecks<kTransactionActive>(idx, element);
} else {
DCHECK_EQ(ptr_size, 4u);
- DCHECK_LE((uintptr_t)element, 0xFFFFFFFFu);
+ DCHECK_LE(element, static_cast<uint64_t>(0xFFFFFFFFu));
(kUnchecked ? down_cast<IntArray*>(static_cast<Object*>(this)) : AsIntArray())
- ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>((uintptr_t)element));
+ ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>(element));
}
}
+template<bool kTransactionActive, bool kUnchecked, typename T>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size) {
+ SetElementPtrSize<kTransactionActive, kUnchecked>(idx,
+ reinterpret_cast<uintptr_t>(element),
+ ptr_size);
+}
+
template <typename Visitor>
inline void PointerArray::Fixup(mirror::PointerArray* dest,
size_t pointer_size,
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 50d77eb..2bd6c5b 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -187,8 +187,11 @@
T GetElementPtrSize(uint32_t idx, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template<bool kTransactionActive = false, bool kUnchecked = false>
+ void SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size)
+ SHARED_REQUIRES(Locks::mutator_lock_);
template<bool kTransactionActive = false, bool kUnchecked = false, typename T>
- void SetElementPtrSize(uint32_t idx, T element, size_t ptr_size)
+ void SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index b49fc74..b97d994 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -457,6 +457,16 @@
return nullptr;
}
+ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name, size_t pointer_size) {
+ for (auto& method : GetDirectMethods(pointer_size)) {
+ ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
+ if (name == np_method->GetName()) {
+ return &method;
+ }
+ }
+ return nullptr;
+}
+
// TODO These should maybe be changed to be named FindOwnedVirtualMethod or something similar
// because they do not only find 'declared' methods and will return copied methods. This behavior is
// desired and correct but the naming can lead to confusion because in the java language declared
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 3017820..be5c668 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -922,6 +922,9 @@
ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* FindDeclaredDirectMethodByName(const StringPiece& name, size_t pointer_size)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& signature,
size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 2ecc9fb..2da3d84 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -142,12 +142,11 @@
}
}
-template <typename Visitor>
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
inline void DexCache::FixupStrings(GcRoot<mirror::String>* dest, const Visitor& visitor) {
GcRoot<mirror::String>* src = GetStrings();
for (size_t i = 0, count = NumStrings(); i < count; ++i) {
- // TODO: Probably don't need read barrier for most callers.
- mirror::String* source = src[i].Read();
+ mirror::String* source = src[i].Read<kReadBarrierOption>();
mirror::String* new_source = visitor(source);
if (source != new_source) {
dest[i] = GcRoot<mirror::String>(new_source);
@@ -155,12 +154,11 @@
}
}
-template <typename Visitor>
+template <ReadBarrierOption kReadBarrierOption, typename Visitor>
inline void DexCache::FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor) {
GcRoot<mirror::Class>* src = GetResolvedTypes();
for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) {
- // TODO: Probably don't need read barrier for most callers.
- mirror::Class* source = src[i].Read();
+ mirror::Class* source = src[i].Read<kReadBarrierOption>();
mirror::Class* new_source = visitor(source);
if (source != new_source) {
dest[i] = GcRoot<mirror::Class>(new_source);
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 0002076..7912510 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -61,11 +61,11 @@
void Fixup(ArtMethod* trampoline, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
- template <typename Visitor>
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
void FixupStrings(GcRoot<mirror::String>* dest, const Visitor& visitor)
SHARED_REQUIRES(Locks::mutator_lock_);
- template <typename Visitor>
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
void FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h
index 51ae760..5e467ab 100644
--- a/runtime/mirror/reference.h
+++ b/runtime/mirror/reference.h
@@ -22,6 +22,7 @@
#include "object.h"
#include "object_callbacks.h"
#include "read_barrier_option.h"
+#include "runtime.h"
#include "thread.h"
namespace art {
@@ -80,9 +81,14 @@
Reference* GetPendingNext() SHARED_REQUIRES(Locks::mutator_lock_) {
return GetFieldObject<Reference>(PendingNextOffset());
}
- template<bool kTransactionActive>
- void SetPendingNext(Reference* pending_next) SHARED_REQUIRES(Locks::mutator_lock_) {
- SetFieldObject<kTransactionActive>(PendingNextOffset(), pending_next);
+
+ void SetPendingNext(Reference* pending_next)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(PendingNextOffset(), pending_next);
+ } else {
+ SetFieldObject<false>(PendingNextOffset(), pending_next);
+ }
}
bool IsEnqueued() SHARED_REQUIRES(Locks::mutator_lock_) {
diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc
index 49cacdf..2a36059 100644
--- a/runtime/native/java_lang_Object.cc
+++ b/runtime/native/java_lang_Object.cc
@@ -20,10 +20,6 @@
#include "mirror/object-inl.h"
#include "scoped_fast_native_object_access.h"
-// TODO: better support for overloading.
-#undef NATIVE_METHOD
-#define NATIVE_METHOD(className, functionName, signature, identifier) \
- { #functionName, signature, reinterpret_cast<void*>(className ## _ ## identifier) }
namespace art {
@@ -58,11 +54,11 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;", internalClone),
- NATIVE_METHOD(Object, notify, "!()V", notify),
- NATIVE_METHOD(Object, notifyAll, "!()V", notifyAll),
- NATIVE_METHOD(Object, wait, "!()V", wait),
- NATIVE_METHOD(Object, wait, "!(JI)V", waitJI),
+ NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;"),
+ NATIVE_METHOD(Object, notify, "!()V"),
+ NATIVE_METHOD(Object, notifyAll, "!()V"),
+ OVERLOADED_NATIVE_METHOD(Object, wait, "!()V", wait),
+ OVERLOADED_NATIVE_METHOD(Object, wait, "!(JI)V", waitJI),
};
void register_java_lang_Object(JNIEnv* env) {
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 8a2c7e4..6ffd476 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -231,58 +231,58 @@
memset(reinterpret_cast<void*>(static_cast<uintptr_t>(address)), value, bytes);
}
-static jbyte Unsafe_getByte$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+static jbyte Unsafe_getByteJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
return *reinterpret_cast<jbyte*>(address);
}
-static void Unsafe_putByte$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jbyte value) {
+static void Unsafe_putByteJB(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jbyte value) {
*reinterpret_cast<jbyte*>(address) = value;
}
-static jshort Unsafe_getShort$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+static jshort Unsafe_getShortJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
return *reinterpret_cast<jshort*>(address);
}
-static void Unsafe_putShort$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jshort value) {
+static void Unsafe_putShortJS(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jshort value) {
*reinterpret_cast<jshort*>(address) = value;
}
-static jchar Unsafe_getChar$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+static jchar Unsafe_getCharJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
return *reinterpret_cast<jchar*>(address);
}
-static void Unsafe_putChar$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jchar value) {
+static void Unsafe_putCharJC(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jchar value) {
*reinterpret_cast<jchar*>(address) = value;
}
-static jint Unsafe_getInt$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+static jint Unsafe_getIntJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
return *reinterpret_cast<jint*>(address);
}
-static void Unsafe_putInt$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jint value) {
+static void Unsafe_putIntJI(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jint value) {
*reinterpret_cast<jint*>(address) = value;
}
-static jlong Unsafe_getLong$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+static jlong Unsafe_getLongJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
return *reinterpret_cast<jlong*>(address);
}
-static void Unsafe_putLong$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jlong value) {
+static void Unsafe_putLongJJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jlong value) {
*reinterpret_cast<jlong*>(address) = value;
}
-static jfloat Unsafe_getFloat$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+static jfloat Unsafe_getFloatJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
return *reinterpret_cast<jfloat*>(address);
}
-static void Unsafe_putFloat$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jfloat value) {
+static void Unsafe_putFloatJF(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jfloat value) {
*reinterpret_cast<jfloat*>(address) = value;
}
-static jdouble Unsafe_getDouble$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
+static jdouble Unsafe_getDoubleJ(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address) {
return *reinterpret_cast<jdouble*>(address);
}
-static void Unsafe_putDouble$(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jdouble value) {
+static void Unsafe_putDoubleJD(JNIEnv* env ATTRIBUTE_UNUSED, jobject, jlong address, jdouble value) {
*reinterpret_cast<jdouble*>(address) = value;
}
@@ -499,24 +499,11 @@
NATIVE_METHOD(Unsafe, allocateMemory, "!(J)J"),
NATIVE_METHOD(Unsafe, freeMemory, "!(J)V"),
NATIVE_METHOD(Unsafe, setMemory, "!(JJB)V"),
- NATIVE_METHOD(Unsafe, getByte$, "!(J)B"),
- NATIVE_METHOD(Unsafe, putByte$, "!(JB)V"),
- NATIVE_METHOD(Unsafe, getShort$, "!(J)S"),
- NATIVE_METHOD(Unsafe, putShort$, "!(JS)V"),
- NATIVE_METHOD(Unsafe, getChar$, "!(J)C"),
- NATIVE_METHOD(Unsafe, putChar$, "!(JC)V"),
- NATIVE_METHOD(Unsafe, getInt$, "!(J)I"),
- NATIVE_METHOD(Unsafe, putInt$, "!(JI)V"),
- NATIVE_METHOD(Unsafe, getLong$, "!(J)J"),
- NATIVE_METHOD(Unsafe, putLong$, "!(JJ)V"),
- NATIVE_METHOD(Unsafe, getFloat$, "!(J)F"),
- NATIVE_METHOD(Unsafe, putFloat$, "!(JF)V"),
- NATIVE_METHOD(Unsafe, getDouble$, "!(J)D"),
- NATIVE_METHOD(Unsafe, putDouble$, "!(JD)V"),
NATIVE_METHOD(Unsafe, copyMemory, "!(JJJ)V"),
NATIVE_METHOD(Unsafe, copyMemoryToPrimitiveArray, "!(JLjava/lang/Object;JJ)V"),
NATIVE_METHOD(Unsafe, copyMemoryFromPrimitiveArray, "!(Ljava/lang/Object;JJJ)V"),
NATIVE_METHOD(Unsafe, getBoolean, "!(Ljava/lang/Object;J)Z"),
+
NATIVE_METHOD(Unsafe, getByte, "!(Ljava/lang/Object;J)B"),
NATIVE_METHOD(Unsafe, getChar, "!(Ljava/lang/Object;J)C"),
NATIVE_METHOD(Unsafe, getShort, "!(Ljava/lang/Object;J)S"),
@@ -528,6 +515,23 @@
NATIVE_METHOD(Unsafe, putShort, "!(Ljava/lang/Object;JS)V"),
NATIVE_METHOD(Unsafe, putFloat, "!(Ljava/lang/Object;JF)V"),
NATIVE_METHOD(Unsafe, putDouble, "!(Ljava/lang/Object;JD)V"),
+
+ // Each of the getFoo variants are overloaded with a call that operates
+ // directively on a native pointer.
+ OVERLOADED_NATIVE_METHOD(Unsafe, getByte, "!(J)B", getByteJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, getChar, "!(J)C", getCharJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, getShort, "!(J)S", getShortJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, getInt, "!(J)I", getIntJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, getLong, "!(J)J", getLongJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, getFloat, "!(J)F", getFloatJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, getDouble, "!(J)D", getDoubleJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, putByte, "!(JB)V", putByteJB),
+ OVERLOADED_NATIVE_METHOD(Unsafe, putChar, "!(JC)V", putCharJC),
+ OVERLOADED_NATIVE_METHOD(Unsafe, putShort, "!(JS)V", putShortJS),
+ OVERLOADED_NATIVE_METHOD(Unsafe, putInt, "!(JI)V", putIntJI),
+ OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ),
+ OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF),
+ OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD),
};
void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index c787b9a..4948558 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -466,6 +466,10 @@
return IsKeyEnabled(OatHeader::kDebuggableKey);
}
+bool OatHeader::IsExtractOnly() const {
+ return IsKeyEnabled(OatHeader::kExtractOnlyKey);
+}
+
bool OatHeader::IsKeyEnabled(const char* key) const {
const char* key_value = GetStoreValueByKey(key);
return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0);
diff --git a/runtime/oat.h b/runtime/oat.h
index 989e3f9..fde386f 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -38,6 +38,7 @@
static constexpr const char* kDex2OatHostKey = "dex2oat-host";
static constexpr const char* kPicKey = "pic";
static constexpr const char* kDebuggableKey = "debuggable";
+ static constexpr const char* kExtractOnlyKey = "extract-only";
static constexpr const char* kClassPathKey = "classpath";
static constexpr const char* kBootClassPath = "bootclasspath";
@@ -106,6 +107,7 @@
size_t GetHeaderSize() const;
bool IsPic() const;
bool IsDebuggable() const;
+ bool IsExtractOnly() const;
private:
OatHeader(InstructionSet instruction_set,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 82b3933..f912598 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1072,8 +1072,13 @@
}
std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
- return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
- dex_file_location_checksum_, this, error_msg);
+ return DexFile::Open(dex_file_pointer_,
+ FileSize(),
+ dex_file_location_,
+ dex_file_location_checksum_,
+ this,
+ false /* verify */,
+ error_msg);
}
uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
@@ -1216,6 +1221,10 @@
return GetOatHeader().IsDebuggable();
}
+bool OatFile::IsExtractOnly() const {
+ return GetOatHeader().IsExtractOnly();
+}
+
static constexpr char kDexClassPathEncodingSeparator = '*';
std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index dbd7541..bcc2d33 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -85,6 +85,8 @@
// Indicates whether the oat file was compiled with full debugging capability.
bool IsDebuggable() const;
+ bool IsExtractOnly() const;
+
const std::string& GetLocation() const {
return location_;
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index a8f84a2..262c932 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -461,6 +461,23 @@
}
}
+ if (file.IsExtractOnly()) {
+ VLOG(oat) << "Oat file is extract-only. Image checksum test skipped.";
+ if (kIsDebugBuild) {
+ // Sanity check that no classes have compiled code. Does not test that
+ // the DEX code has not been quickened.
+ std::string error_msg;
+ for (const OatFile::OatDexFile* current : file.GetOatDexFiles()) {
+ std::unique_ptr<const DexFile> dex_file = current->OpenDexFile(&error_msg);
+ DCHECK(dex_file != nullptr);
+ for (size_t i = 0, e = dex_file->NumClassDefs(); i < e; ++i) {
+ DCHECK_EQ(current->GetOatClass(i).GetType(), kOatClassNoneCompiled);
+ }
+ }
+ }
+ return false;
+ }
+
// Verify the image checksum
const ImageInfo* image_info = GetImageInfo();
if (image_info == nullptr) {
@@ -486,7 +503,10 @@
return false;
}
- if (file.IsPic()) {
+ if (file.IsPic() || file.IsExtractOnly()) {
+ // Oat files compiled in PIC mode do not require relocation and extract-only
+ // oat files do not contain any compiled code. Skip the relocation test.
+ VLOG(oat) << "Oat relocation test skipped.";
return true;
}
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 25dcbe4..83d4457 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -259,6 +259,26 @@
EXPECT_TRUE(odex_file->IsPic());
}
+ void GenerateExtractOnlyOdexForTest(const std::string& dex_location,
+ const std::string& odex_location) {
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--compiler-filter=verify-at-runtime");
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(
+ odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
+ false, dex_location.c_str(), &error_msg));
+ ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+ EXPECT_TRUE(odex_file->IsExtractOnly());
+ EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u);
+ EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u);
+ EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0);
+}
+
private:
// Reserve memory around where the image will be loaded so other memory
// won't conflict when it comes time to load the image.
@@ -488,6 +508,32 @@
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
+// Case: We have a DEX file and an extract-only ODEX file out of date relative
+// to the DEX file.
+// Expect: The status is kDex2OatNeeded.
+TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar";
+ std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex";
+
+ // We create a dex, generate an oat for it, then overwrite the dex with a
+ // different dex to make the oat out of date.
+ Copy(GetDexSrc1(), dex_location);
+ GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str());
+ Copy(GetDexSrc2(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
// Case: We have a DEX file and an ODEX file, but no OAT file.
// Expect: The status is kPatchOatNeeded.
TEST_F(OatFileAssistantTest, DexOdexNoOat) {
@@ -784,6 +830,31 @@
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
+// Case: We have a DEX file and a ExtractOnly ODEX file, but no OAT file.
+// Expect: The status is kNoDexOptNeeded, because ExtractOnly contains no code.
+TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexExtractOnlyOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexExtractOnlyOdexNoOat.odex";
+
+ // Create the dex and odex files
+ Copy(GetDexSrc1(), dex_location);
+ GenerateExtractOnlyOdexForTest(dex_location, odex_location);
+
+ // Verify the status.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+ EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+ EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+ EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+ EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileExists());
+ EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+ EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+ EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+}
+
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: We should load an executable dex file.
TEST_F(OatFileAssistantTest, LoadOatUpToDate) {
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 6554394..17306c9 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -71,14 +71,37 @@
// we need to be able to detect possibly inlined method, we pass a null inline method to indicate
// we don't want to take unresolved methods and fields into account during analysis.
bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
- InlineMethod* method) {
+ InlineMethod* result) {
DCHECK(verifier != nullptr);
if (!Runtime::Current()->UseJit()) {
- DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
+ DCHECK_EQ(verifier->CanLoadClasses(), result != nullptr);
}
+
+ // Note: verifier->GetMethod() may be null.
+ return AnalyseMethodCode(verifier->CodeItem(),
+ verifier->GetMethodReference(),
+ (verifier->GetAccessFlags() & kAccStatic) != 0u,
+ verifier->GetMethod(),
+ result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
+ const DexFile::CodeItem* code_item = method->GetCodeItem();
+ if (code_item == nullptr) {
+ // Native or abstract.
+ return false;
+ }
+ return AnalyseMethodCode(
+ code_item, method->ToMethodReference(), method->IsStatic(), method, result);
+}
+
+bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result) {
// We currently support only plain return or 2-instruction methods.
- const DexFile::CodeItem* code_item = verifier->CodeItem();
DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
@@ -86,21 +109,21 @@
switch (opcode) {
case Instruction::RETURN_VOID:
if (method != nullptr) {
- method->opcode = kInlineOpNop;
- method->flags = kInlineSpecial;
- method->d.data = 0u;
+ result->opcode = kInlineOpNop;
+ result->flags = kInlineSpecial;
+ result->d.data = 0u;
}
return true;
case Instruction::RETURN:
case Instruction::RETURN_OBJECT:
case Instruction::RETURN_WIDE:
- return AnalyseReturnMethod(code_item, method);
+ return AnalyseReturnMethod(code_item, result);
case Instruction::CONST:
case Instruction::CONST_4:
case Instruction::CONST_16:
case Instruction::CONST_HIGH16:
// TODO: Support wide constants (RETURN_WIDE).
- return AnalyseConstMethod(code_item, method);
+ return AnalyseConstMethod(code_item, result);
case Instruction::IGET:
case Instruction::IGET_OBJECT:
case Instruction::IGET_BOOLEAN:
@@ -112,7 +135,7 @@
// case Instruction::IGET_QUICK:
// case Instruction::IGET_WIDE_QUICK:
// case Instruction::IGET_OBJECT_QUICK:
- return AnalyseIGetMethod(verifier, method);
+ return AnalyseIGetMethod(code_item, method_ref, is_static, method, result);
case Instruction::IPUT:
case Instruction::IPUT_OBJECT:
case Instruction::IPUT_BOOLEAN:
@@ -124,7 +147,7 @@
// case Instruction::IPUT_QUICK:
// case Instruction::IPUT_WIDE_QUICK:
// case Instruction::IPUT_OBJECT_QUICK:
- return AnalyseIPutMethod(verifier, method);
+ return AnalyseIPutMethod(code_item, method_ref, is_static, method, result);
default:
return false;
}
@@ -194,9 +217,11 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIGetMethod(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
InlineMethod* result) {
- const DexFile::CodeItem* code_item = verifier->CodeItem();
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIGet(opcode));
@@ -227,10 +252,10 @@
return false; // Not returning the value retrieved by IGET?
}
- if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+ if (is_static || object_arg != 0u) {
// TODO: Implement inlining of IGET on non-"this" registers (needs correct stack trace for NPE).
// Allow synthetic accessors. We don't care about losing their stack frame in NPE.
- if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
+ if (!IsSyntheticAccessor(method_ref)) {
return false;
}
}
@@ -243,13 +268,13 @@
if (result != nullptr) {
InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(field_idx, false, verifier, data)) {
+ if (!ComputeSpecialAccessorInfo(method, field_idx, false, data)) {
return false;
}
result->opcode = kInlineOpIGet;
result->flags = kInlineSpecial;
data->op_variant = IGetVariant(opcode);
- data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+ data->method_is_static = is_static ? 1u : 0u;
data->object_arg = object_arg; // Allow IGET on any register, not just "this".
data->src_arg = 0u;
data->return_arg_plus1 = 0u;
@@ -257,9 +282,11 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIPutMethod(verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
InlineMethod* result) {
- const DexFile::CodeItem* code_item = verifier->CodeItem();
const Instruction* instruction = Instruction::At(code_item->insns_);
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIPut(opcode));
@@ -292,10 +319,10 @@
uint32_t object_arg = object_reg - arg_start;
uint32_t src_arg = src_reg - arg_start;
- if ((verifier->GetAccessFlags() & kAccStatic) != 0u || object_arg != 0u) {
+ if (is_static || object_arg != 0u) {
// TODO: Implement inlining of IPUT on non-"this" registers (needs correct stack trace for NPE).
// Allow synthetic accessors. We don't care about losing their stack frame in NPE.
- if (!IsSyntheticAccessor(verifier->GetMethodReference())) {
+ if (!IsSyntheticAccessor(method_ref)) {
return false;
}
}
@@ -310,13 +337,13 @@
if (result != nullptr) {
InlineIGetIPutData* data = &result->d.ifield_data;
- if (!ComputeSpecialAccessorInfo(field_idx, true, verifier, data)) {
+ if (!ComputeSpecialAccessorInfo(method, field_idx, true, data)) {
return false;
}
result->opcode = kInlineOpIPut;
result->flags = kInlineSpecial;
data->op_variant = IPutVariant(opcode);
- data->method_is_static = (verifier->GetAccessFlags() & kAccStatic) != 0u ? 1u : 0u;
+ data->method_is_static = is_static ? 1u : 0u;
data->object_arg = object_arg; // Allow IPUT on any register, not just "this".
data->src_arg = src_arg;
data->return_arg_plus1 = return_arg_plus1;
@@ -324,15 +351,17 @@
return true;
}
-bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
- verifier::MethodVerifier* verifier,
+bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method,
+ uint32_t field_idx,
+ bool is_put,
InlineIGetIPutData* result) {
- mirror::DexCache* dex_cache = verifier->GetDexCache();
- uint32_t method_idx = verifier->GetMethodReference().dex_method_index;
- auto* cl = Runtime::Current()->GetClassLinker();
- ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, cl->GetImagePointerSize());
- ArtField* field = cl->GetResolvedField(field_idx, dex_cache);
- if (method == nullptr || field == nullptr || field->IsStatic()) {
+ if (method == nullptr) {
+ return false;
+ }
+ mirror::DexCache* dex_cache = method->GetDexCache();
+ size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ ArtField* field = dex_cache->GetResolvedField(field_idx, pointer_size);
+ if (field == nullptr || field->IsStatic()) {
return false;
}
mirror::Class* method_class = method->GetDeclaringClass();
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index 1bb816b..046d225 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -188,7 +188,9 @@
* @param method placeholder for the inline method data.
* @return true if the method is a candidate for inlining, false otherwise.
*/
- static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* method)
+ static bool AnalyseMethodCode(verifier::MethodVerifier* verifier, InlineMethod* result)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ static bool AnalyseMethodCode(ArtMethod* method, InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
static constexpr bool IsInstructionIGet(Instruction::Code opcode) {
@@ -211,17 +213,32 @@
static bool IsSyntheticAccessor(MethodReference ref);
private:
+ static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
+ SHARED_REQUIRES(Locks::mutator_lock_);
static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
- static bool AnalyseIGetMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+ static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
- static bool AnalyseIPutMethod(verifier::MethodVerifier* verifier, InlineMethod* result)
+ static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ const MethodReference& method_ref,
+ bool is_static,
+ ArtMethod* method,
+ InlineMethod* result)
SHARED_REQUIRES(Locks::mutator_lock_);
// Can we fast path instance field access in a verified accessor?
// If yes, computes field's offset and volatility and whether the method is static or not.
- static bool ComputeSpecialAccessorInfo(uint32_t field_idx, bool is_put,
- verifier::MethodVerifier* verifier,
+ static bool ComputeSpecialAccessorInfo(ArtMethod* method,
+ uint32_t field_idx,
+ bool is_put,
InlineIGetIPutData* result)
SHARED_REQUIRES(Locks::mutator_lock_);
};
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index 3169a8b..b7bd99b 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -80,8 +80,8 @@
static void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref)
SHARED_REQUIRES(Locks::mutator_lock_);
- ALWAYS_INLINE static mirror::Object* Mark(mirror::Object* obj)
- SHARED_REQUIRES(Locks::mutator_lock_);
+ // ALWAYS_INLINE on this caused a performance regression b/26744236.
+ static mirror::Object* Mark(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_);
static mirror::Object* WhitePtr() {
return reinterpret_cast<mirror::Object*>(white_ptr_);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 21241d2..2abcd67 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2704,7 +2704,7 @@
// Visiting the declaring class is necessary so that we don't unload the class of a method that
// is executing. We need to ensure that the code stays mapped.
void VisitDeclaringClass(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
- mirror::Class* klass = method->GetDeclaringClassNoBarrier();
+ mirror::Class* klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
// klass can be null for runtime methods.
if (klass != nullptr) {
mirror::Object* new_ref = klass;
diff --git a/runtime/utils.h b/runtime/utils.h
index 153749e..c00db11 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -388,6 +388,24 @@
// Sleep forever and never come back.
NO_RETURN void SleepForever();
+inline void FlushInstructionCache(char* begin, char* end) {
+ // Only use __builtin___clear_cache with Clang or with GCC >= 4.3.0
+ // (__builtin___clear_cache was introduced in GCC 4.3.0).
+#if defined(__clang__) || GCC_VERSION >= 40300
+ __builtin___clear_cache(begin, end);
+#else
+ // Only warn on non-Intel platforms, as x86 and x86-64 do not need
+ // cache flush instructions, as long as the "code uses the same
+ // linear address for modifying and fetching the instruction". See
+ // "Intel(R) 64 and IA-32 Architectures Software Developer's Manual
+ // Volume 3A: System Programming Guide, Part 1", section 11.6
+ // "Self-Modifying Code".
+#if !defined(__i386__) && !defined(__x86_64__)
+ UNIMPLEMENTED(WARNING) << "cache flush";
+#endif
+#endif
+}
+
} // namespace art
#endif // ART_RUNTIME_UTILS_H_
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index f6ee6a2..d63083c 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -87,11 +87,13 @@
}
inline size_t DexCacheArraysLayout::StringsSize(size_t num_elements) const {
- return ArraySize(sizeof(GcRoot<mirror::String>), num_elements);
+ // App image patching relies on having enough room for a forwarding pointer in the types array.
+ return std::max(ArraySize(sizeof(GcRoot<mirror::String>), num_elements), pointer_size_);
}
inline size_t DexCacheArraysLayout::StringsAlignment() const {
- return alignof(GcRoot<mirror::String>);
+ // App image patching relies on having enough room for a forwarding pointer in the strings array.
+ return pointer_size_;
}
inline size_t DexCacheArraysLayout::FieldOffset(uint32_t field_idx) const {
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
index f52d011..def61db 100644
--- a/runtime/verifier/method_verifier-inl.h
+++ b/runtime/verifier/method_verifier-inl.h
@@ -50,6 +50,10 @@
return dex_cache_.Get();
}
+inline ArtMethod* MethodVerifier::GetMethod() const {
+ return mirror_method_;
+}
+
inline MethodReference MethodVerifier::GetMethodReference() const {
return MethodReference(dex_file_, dex_method_idx_);
}
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 2890a98..56154c6 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2076,7 +2076,7 @@
} else if (reg_type.IsConflict()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning register with conflict";
} else if (reg_type.IsUninitializedTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "returning uninitialized object '"
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning uninitialized object '"
<< reg_type << "'";
} else if (!reg_type.IsReferenceTypes()) {
// We really do expect a reference here.
@@ -2233,7 +2233,6 @@
opcode_flags &= ~Instruction::kThrow;
work_line_->PopMonitor(this, inst->VRegA_11x());
break;
-
case Instruction::CHECK_CAST:
case Instruction::INSTANCE_OF: {
/*
@@ -2279,6 +2278,14 @@
} else {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on non-reference in v" << orig_type_reg;
}
+ } else if (orig_type.IsUninitializedTypes()) {
+ if (is_checkcast) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "check-cast on uninitialized reference in v"
+ << orig_type_reg;
+ } else {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "instance-of on uninitialized reference in v"
+ << orig_type_reg;
+ }
} else {
if (is_checkcast) {
work_line_->SetRegisterType<LockOp::kKeep>(this, inst->VRegA_21c(), res_type);
@@ -2373,8 +2380,12 @@
case Instruction::THROW: {
const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegA_11x());
if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) {
- Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT)
- << "thrown class " << res_type << " not instanceof Throwable";
+ if (res_type.IsUninitializedTypes()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "thrown exception not initialized";
+ } else {
+ Fail(res_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS : VERIFY_ERROR_BAD_CLASS_SOFT)
+ << "thrown class " << res_type << " not instanceof Throwable";
+ }
}
break;
}
@@ -3596,6 +3607,7 @@
} else {
const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex());
if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) {
+ DCHECK(!exception.IsUninitializedTypes()); // Comes from dex, shouldn't be uninit.
if (exception.IsUnresolvedTypes()) {
// We don't know enough about the type. Fail here and let runtime handle it.
Fail(VERIFY_ERROR_NO_CLASS) << "unresolved exception class " << exception;
@@ -3786,7 +3798,8 @@
CHECK(have_pending_hard_failure_);
return nullptr;
}
- if (actual_arg_type.IsUninitializedReference()) {
+ bool is_init = false;
+ if (actual_arg_type.IsUninitializedTypes()) {
if (res_method) {
if (!res_method->IsConstructor()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
@@ -3800,8 +3813,12 @@
return nullptr;
}
}
+ is_init = true;
}
- if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) {
+ const RegType& adjusted_type = is_init
+ ? GetRegTypeCache()->FromUninitialized(actual_arg_type)
+ : actual_arg_type;
+ if (method_type != METHOD_INTERFACE && !adjusted_type.IsZero()) {
const RegType* res_method_class;
// Miranda methods have the declaring interface as their declaring class, not the abstract
// class. It would be wrong to use this for the type check (interface type checks are
@@ -3819,10 +3836,12 @@
dex_file_->StringByTypeIdx(class_idx),
false);
}
- if (!res_method_class->IsAssignableFrom(actual_arg_type)) {
- Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS:
- VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
- << "' not instance of '" << *res_method_class << "'";
+ if (!res_method_class->IsAssignableFrom(adjusted_type)) {
+ Fail(adjusted_type.IsUnresolvedTypes()
+ ? VERIFY_ERROR_NO_CLASS
+ : VERIFY_ERROR_BAD_CLASS_SOFT)
+ << "'this' argument '" << actual_arg_type << "' not instance of '"
+ << *res_method_class << "'";
// Continue on soft failures. We need to find possible hard failures to avoid problems in
// the compiler.
if (have_pending_hard_failure_) {
@@ -3948,11 +3967,27 @@
// If we're using invoke-super(method), make sure that the executing method's class' superclass
// has a vtable entry for the target method. Or the target is on a interface.
if (method_type == METHOD_SUPER) {
- if (res_method->GetDeclaringClass()->IsInterface()) {
- // TODO Fill in this part. Verify what we can...
- if (Runtime::Current()->IsAotCompiler()) {
- Fail(VERIFY_ERROR_FORCE_INTERPRETER) << "Currently we only allow invoke-super in "
- << "interpreter when using interface methods";
+ uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
+ mirror::Class* reference_class = dex_cache_->GetResolvedType(class_idx);
+ if (reference_class == nullptr) {
+ Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Unable to find referenced class from invoke-super";
+ return nullptr;
+ }
+ if (reference_class->IsInterface()) {
+ // TODO Can we verify anything else.
+ if (class_idx == class_def_->class_idx_) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE) << "Cannot invoke-super on self as interface";
+ }
+ // TODO Revisit whether we want to allow invoke-super on direct interfaces only like the JLS
+ // does.
+ mirror::Class* this_class = GetDeclaringClass().GetClass();
+ if (!reference_class->IsAssignableFrom(this_class)) {
+ Fail(VERIFY_ERROR_CLASS_CHANGE)
+ << "invoke-super in " << PrettyClass(this_class) << " in method "
+ << PrettyMethod(dex_method_idx_, *dex_file_) << " to method "
+ << PrettyMethod(method_idx, *dex_file_) << " references "
+ << "non-super-interface type " << PrettyClass(reference_class);
+ return nullptr;
}
} else {
const RegType& super = GetDeclaringClass().GetSuperClass(®_types_);
@@ -4067,7 +4102,8 @@
* For an interface class, we don't do the full interface merge (see JoinClass), so we can't do a
* rigorous check here (which is okay since we have to do it at runtime).
*/
- if (actual_arg_type.IsUninitializedReference() && !res_method->IsConstructor()) {
+ // Note: given an uninitialized type, this should always fail. Constructors aren't virtual.
+ if (actual_arg_type.IsUninitializedTypes() && !res_method->IsConstructor()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized";
return nullptr;
}
@@ -4077,8 +4113,11 @@
const RegType& res_method_class =
FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes());
if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
- Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS :
- VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
+ Fail(actual_arg_type.IsUninitializedTypes() // Just overcautious - should have never
+ ? VERIFY_ERROR_BAD_CLASS_HARD // quickened this.
+ : actual_arg_type.IsUnresolvedTypes()
+ ? VERIFY_ERROR_NO_CLASS
+ : VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
<< "' not instance of '" << res_method_class << "'";
return nullptr;
}
@@ -4405,15 +4444,20 @@
const RegType& field_klass =
FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id),
klass, klass->CannotBeAssignedFromOtherTypes());
- if (obj_type.IsUninitializedTypes() &&
- (!IsConstructor() || GetDeclaringClass().Equals(obj_type) ||
- !field_klass.Equals(GetDeclaringClass()))) {
+ if (obj_type.IsUninitializedTypes()) {
// Field accesses through uninitialized references are only allowable for constructors where
- // the field is declared in this class
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
- << " of a not fully initialized object within the context"
- << " of " << PrettyMethod(dex_method_idx_, *dex_file_);
- return nullptr;
+ // the field is declared in this class.
+ // Note: this IsConstructor check is technically redundant, as UninitializedThis should only
+ // appear in constructors.
+ if (!obj_type.IsUninitializedThisReference() ||
+ !IsConstructor() ||
+ !field_klass.Equals(GetDeclaringClass())) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
+ << " of a not fully initialized object within the context"
+ << " of " << PrettyMethod(dex_method_idx_, *dex_file_);
+ return nullptr;
+ }
+ return field;
} else if (!field_klass.IsAssignableFrom(obj_type)) {
// Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
// of C1. For resolution to occur the declared class of the field must be compatible with
@@ -4436,7 +4480,18 @@
field = GetStaticField(field_idx);
} else {
const RegType& object_type = work_line_->GetRegisterType(this, inst->VRegB_22c());
- field = GetInstanceField(object_type, field_idx);
+
+ // One is not allowed to access fields on uninitialized references, except to write to
+ // fields in the constructor (before calling another constructor).
+ // GetInstanceField does an assignability check which will fail for uninitialized types.
+ // We thus modify the type if the uninitialized reference is a "this" reference (this also
+ // checks at the same time that we're verifying a constructor).
+ bool should_adjust = (kAccType == FieldAccessType::kAccPut) &&
+ object_type.IsUninitializedThisReference();
+ const RegType& adjusted_type = should_adjust
+ ? GetRegTypeCache()->FromUninitialized(object_type)
+ : object_type;
+ field = GetInstanceField(adjusted_type, field_idx);
if (UNLIKELY(have_pending_hard_failure_)) {
return;
}
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index a26e0fb..613d5af 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -270,6 +270,7 @@
ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index);
mirror::ClassLoader* GetClassLoader() SHARED_REQUIRES(Locks::mutator_lock_);
mirror::DexCache* GetDexCache() SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* GetMethod() const SHARED_REQUIRES(Locks::mutator_lock_);
MethodReference GetMethodReference() const;
uint32_t GetAccessFlags() const;
bool HasCheckCasts() const;
diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h
index 11a53e5..861db3c 100644
--- a/runtime/verifier/reg_type-inl.h
+++ b/runtime/verifier/reg_type-inl.h
@@ -93,6 +93,10 @@
return true; // All reference types can be assigned null.
} else if (!rhs.IsReferenceTypes()) {
return false; // Expect rhs to be a reference type.
+ } else if (lhs.IsUninitializedTypes() || rhs.IsUninitializedTypes()) {
+ // Uninitialized types are only allowed to be assigned to themselves.
+ // TODO: Once we have a proper "reference" super type, this needs to be extended.
+ return false;
} else if (lhs.IsJavaLangObject()) {
return true; // All reference types can be assigned to Object.
} else if (!strict && !lhs.IsUnresolvedTypes() && lhs.GetClass()->IsInterface()) {
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 71ed4a2..30f613c 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -155,7 +155,7 @@
} else {
klass = class_linker->LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor),
loader);
- if (klass != nullptr && !klass->IsLoaded()) {
+ if (klass != nullptr && !klass->IsResolved()) {
// We found the class but without it being loaded its not safe for use.
klass = nullptr;
}
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 57fb701..08f85b3 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -147,6 +147,9 @@
if (!check_type.IsNonZeroReferenceTypes() || !src_type.IsNonZeroReferenceTypes()) {
// Hard fail if one of the types is primitive, since they are concretely known.
fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
+ } else if (check_type.IsUninitializedTypes() || src_type.IsUninitializedTypes()) {
+ // Hard fail for uninitialized types, which don't match anything but themselves.
+ fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
} else if (check_type.IsUnresolvedTypes() || src_type.IsUnresolvedTypes()) {
fail_type = VERIFY_ERROR_NO_CLASS;
} else {
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index 5b22e88..a9a7a05 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -110,23 +110,35 @@
check(unsafe.getObject(t, objectOffset), objectValue, "Unsafe.getObject(Object, long)");
if (unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
- System.out.println("Unexpectedly succeeding compareAndSwap...");
+ System.out.println("Unexpectedly succeeding compareAndSwapInt(t, intOffset, 0, 1)");
}
if (!unsafe.compareAndSwapInt(t, intOffset, intValue, 0)) {
- System.out.println("Unexpectedly not succeeding compareAndSwap...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapInt(t, intOffset, intValue, 0)");
}
if (!unsafe.compareAndSwapInt(t, intOffset, 0, 1)) {
- System.out.println("Unexpectedly not succeeding compareAndSwap...");
+ System.out.println("Unexpectedly not succeeding compareAndSwapInt(t, intOffset, 0, 1)");
+ }
+ // Exercise sun.misc.Unsafe.compareAndSwapInt using the same
+ // integer (1) for the `expectedValue` and `newValue` arguments.
+ if (!unsafe.compareAndSwapInt(t, intOffset, 1, 1)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwapInt(t, intOffset, 1, 1)");
}
if (unsafe.compareAndSwapLong(t, longOffset, 0, 1)) {
- System.out.println("Unexpectedly succeeding compareAndSwapLong...");
+ System.out.println("Unexpectedly succeeding compareAndSwapLong(t, longOffset, 0, 1)");
}
if (!unsafe.compareAndSwapLong(t, longOffset, longValue, 0)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapLong...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapLong(t, longOffset, longValue, 0)");
}
if (!unsafe.compareAndSwapLong(t, longOffset, 0, 1)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapLong...");
+ System.out.println("Unexpectedly not succeeding compareAndSwapLong(t, longOffset, 0, 1)");
+ }
+ // Exercise sun.misc.Unsafe.compareAndSwapLong using the same
+ // integer (1) for the `expectedValue` and `newValue` arguments.
+ if (!unsafe.compareAndSwapLong(t, longOffset, 1, 1)) {
+ System.out.println("Unexpectedly not succeeding compareAndSwapLong(t, longOffset, 1, 1)");
}
// We do not use `null` as argument to sun.misc.Unsafe.compareAndSwapObject
@@ -135,31 +147,41 @@
// references). This way, when heap poisoning is enabled, we can
// better exercise its implementation within that method.
if (unsafe.compareAndSwapObject(t, objectOffset, new Object(), new Object())) {
- System.out.println("Unexpectedly succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly succeeding " +
+ "compareAndSwapObject(t, objectOffset, new Object(), new Object())");
}
Object objectValue2 = new Object();
if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue, objectValue2)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly not succeeding " +
+ "compareAndSwapObject(t, objectOffset, objectValue, objectValue2)");
}
Object objectValue3 = new Object();
if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue2, objectValue3)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly not succeeding " +
+ "compareAndSwapObject(t, objectOffset, objectValue2, objectValue3)");
}
-
+ // Exercise sun.misc.Unsafe.compareAndSwapObject using the same
+ // object (`objectValue3`) for the `expectedValue` and `newValue` arguments.
+ if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue3, objectValue3)) {
+ System.out.println("Unexpectedly not succeeding " +
+ "compareAndSwapObject(t, objectOffset, objectValue3, objectValue3)");
+ }
// Exercise sun.misc.Unsafe.compareAndSwapObject using the same
// object (`t`) for the `obj` and `newValue` arguments.
if (!unsafe.compareAndSwapObject(t, objectOffset, objectValue3, t)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapObject(t, objectOffset, objectValue3, t)");
}
// Exercise sun.misc.Unsafe.compareAndSwapObject using the same
// object (`t`) for the `obj`, `expectedValue` and `newValue` arguments.
if (!unsafe.compareAndSwapObject(t, objectOffset, t, t)) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println("Unexpectedly not succeeding compareAndSwapObject(t, objectOffset, t, t)");
}
// Exercise sun.misc.Unsafe.compareAndSwapObject using the same
// object (`t`) for the `obj` and `expectedValue` arguments.
if (!unsafe.compareAndSwapObject(t, objectOffset, t, new Object())) {
- System.out.println("Unexpectedly not succeeding compareAndSwapObject...");
+ System.out.println(
+ "Unexpectedly not succeeding compareAndSwapObject(t, objectOffset, t, new Object())");
}
}
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index 1337442..82e1fc8 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -46,7 +46,7 @@
return oat_dex_file != nullptr && oat_dex_file->GetOatFile()->IsExecutable();
}
- static bool isPic(jclass cls) {
+ static bool needsRelocation(jclass cls) {
const OatFile::OatDexFile* oat_dex_file = getOatDexFile(cls);
if (oat_dex_file == nullptr) {
@@ -54,7 +54,7 @@
}
const OatFile* oat_file = oat_dex_file->GetOatFile();
- return oat_file->IsPic();
+ return !oat_file->IsPic() && !oat_file->IsExtractOnly();
}
};
@@ -66,8 +66,8 @@
return NoPatchoatTest::hasExecutableOat(cls);
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_isPic(JNIEnv*, jclass cls) {
- return NoPatchoatTest::isPic(cls);
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_needsRelocation(JNIEnv*, jclass cls) {
+ return NoPatchoatTest::needsRelocation(cls);
}
} // namespace art
diff --git a/test/117-nopatchoat/src/Main.java b/test/117-nopatchoat/src/Main.java
index 425cf48..816eb17 100644
--- a/test/117-nopatchoat/src/Main.java
+++ b/test/117-nopatchoat/src/Main.java
@@ -22,9 +22,9 @@
// ANDROID_DATA has been relocated, since a non-relocated oat file always has a 0 delta.
// Hitting this condition should be rare and ideally we would prevent it from happening but
// there is no way to do so without major changes to the run-test framework.
- boolean executable_correct = (isPic() ?
- hasExecutableOat() == true :
- hasExecutableOat() == (isDex2OatEnabled() || isRelocationDeltaZero()));
+ boolean executable_correct = (needsRelocation() ?
+ hasExecutableOat() == (isDex2OatEnabled() || isRelocationDeltaZero()) :
+ hasExecutableOat() == true);
System.out.println(
"dex2oat & patchoat are " + ((isDex2OatEnabled()) ? "enabled" : "disabled") +
@@ -49,7 +49,7 @@
private native static boolean isDex2OatEnabled();
- private native static boolean isPic();
+ private native static boolean needsRelocation();
private native static boolean hasOatFile();
diff --git a/test/144-static-field-sigquit/expected.txt b/test/144-static-field-sigquit/expected.txt
new file mode 100644
index 0000000..e0c3e90
--- /dev/null
+++ b/test/144-static-field-sigquit/expected.txt
@@ -0,0 +1,4 @@
+Starting threads...
+Performing sigquits for 5 seconds
+Got date field
+Joined threads
diff --git a/test/144-static-field-sigquit/info.txt b/test/144-static-field-sigquit/info.txt
new file mode 100644
index 0000000..5dcfc76
--- /dev/null
+++ b/test/144-static-field-sigquit/info.txt
@@ -0,0 +1,8 @@
+Regression test for ag/853775
+
+Tests that unresolved classes are not put into the dex cache by the verifier.
+This was potentially happening when receiving a signal while in the static
+initilizer of a class and also within a synchronized block.
+
+This test is flaky and produces the issue rarely, but it should be good enough
+to trigger occasionally with the buildbots.
diff --git a/test/144-static-field-sigquit/src/ClassWithStaticField.java b/test/144-static-field-sigquit/src/ClassWithStaticField.java
new file mode 100644
index 0000000..0b2c855
--- /dev/null
+++ b/test/144-static-field-sigquit/src/ClassWithStaticField.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Date;
+
+public class ClassWithStaticField {
+ public static Date mDate = new Date();
+}
diff --git a/test/144-static-field-sigquit/src/Main.java b/test/144-static-field-sigquit/src/Main.java
new file mode 100644
index 0000000..ab94da3
--- /dev/null
+++ b/test/144-static-field-sigquit/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ Thread thread1 = new Thread(new SigQuit());
+ Thread thread2 = new Thread(new SynchronizedUse());
+
+ System.out.println("Starting threads...");
+ thread1.start();
+ Thread.sleep(2000);
+ thread2.start();
+
+ thread1.join();
+ thread2.join();
+ System.out.println("Joined threads");
+ }
+}
diff --git a/test/144-static-field-sigquit/src/SigQuit.java b/test/144-static-field-sigquit/src/SigQuit.java
new file mode 100644
index 0000000..bed23e4
--- /dev/null
+++ b/test/144-static-field-sigquit/src/SigQuit.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+
+public class SigQuit implements Runnable {
+ private final static int sigquit;
+ private final static Method kill;
+ private final static int pid;
+
+ static {
+ int pidTemp = -1;
+ int sigquitTemp = -1;
+ Method killTemp = null;
+
+ try {
+ Class<?> osClass = Class.forName("android.system.Os");
+ Method getpid = osClass.getDeclaredMethod("getpid");
+ pidTemp = (Integer) getpid.invoke(null);
+
+ Class<?> osConstants = Class.forName("android.system.OsConstants");
+ Field sigquitField = osConstants.getDeclaredField("SIGQUIT");
+ sigquitTemp = (Integer) sigquitField.get(null);
+
+ killTemp = osClass.getDeclaredMethod("kill", int.class, int.class);
+ } catch (Exception e) {
+ if (!e.getClass().getName().equals("ErrnoException")) {
+ e.printStackTrace(System.out);
+ }
+ }
+
+ pid = pidTemp;
+ sigquit = sigquitTemp;
+ kill = killTemp;
+ }
+
+ public boolean perform() {
+ try {
+ kill.invoke(null, pid, sigquit);
+ } catch (Exception e) {
+ if (!e.getClass().getName().equals("ErrnoException")) {
+ e.printStackTrace(System.out);
+ }
+ }
+ return true;
+ }
+
+ public void run() {
+ long endTime = System.currentTimeMillis() + 5000;
+ System.out.println("Performing sigquits for 5 seconds");
+ while (System.currentTimeMillis() < endTime) {
+ perform();
+ }
+ }
+}
diff --git a/test/144-static-field-sigquit/src/SynchronizedUse.java b/test/144-static-field-sigquit/src/SynchronizedUse.java
new file mode 100644
index 0000000..43af1d9
--- /dev/null
+++ b/test/144-static-field-sigquit/src/SynchronizedUse.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Date;
+
+public class SynchronizedUse implements Runnable {
+ public void run() {
+ synchronized (this) {
+ Date dateField = ClassWithStaticField.mDate;
+ System.out.println("Got date field");
+ }
+ }
+}
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 0e07f47..5479818 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -666,11 +666,11 @@
/// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (before)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<Null>>,<<Null>>]
- /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Cond>>]
/// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (after)
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: If [<<Const0>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Const0>>]
/// CHECK-START: int Main.StaticConditionNulls() constant_folding_after_inlining (after)
/// CHECK-NOT: NotEqual
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index 92cf807..d48b30e 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -275,23 +275,6 @@
}
}
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) inliner (after)
- /// CHECK-DAG: <<IOf:z\d+>> InstanceOf
- /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<IOf>>]
- /// CHECK-DAG: If [<<Not>>]
-
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (before)
- /// CHECK: CheckCast
- /// CHECK-NOT: CheckCast
-
- /// CHECK-START: void Main.testNotInstanceOf_Inlined(java.lang.Object) instruction_simplifier_after_bce (after)
- /// CHECK-NOT: CheckCast
- public void testNotInstanceOf_Inlined(Object o) {
- if ($inline$InstanceofSubclassC(o)) {
- ((SubclassC)o).$noinline$g();
- }
- }
-
/// CHECK-START: void Main.testInstanceOfKeep(java.lang.Object) instruction_simplifier (before)
/// CHECK: CheckCast
/// CHECK: CheckCast
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index 0fd7801..3c8abeb 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -969,6 +969,13 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int EqualTrueRhs(boolean arg) {
return (arg != true) ? 3 : 5;
}
@@ -983,6 +990,13 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.EqualTrueLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int EqualTrueLhs(boolean arg) {
return (true != arg) ? 3 : 5;
}
@@ -995,8 +1009,14 @@
/// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int EqualFalseRhs(boolean arg) {
return (arg != false) ? 3 : 5;
@@ -1010,8 +1030,14 @@
/// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.EqualFalseLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int EqualFalseLhs(boolean arg) {
return (false != arg) ? 3 : 5;
@@ -1025,8 +1051,14 @@
/// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int NotEqualTrueRhs(boolean arg) {
return (arg == true) ? 3 : 5;
@@ -1040,8 +1072,14 @@
/// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: If [<<NotArg>>]
+ /// CHECK-DAG: If [<<Arg>>]
+
+ /// CHECK-START: int Main.NotEqualTrueLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const5>>,<<Const3>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int NotEqualTrueLhs(boolean arg) {
return (true == arg) ? 3 : 5;
@@ -1057,6 +1095,13 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int NotEqualFalseRhs(boolean arg) {
return (arg == false) ? 3 : 5;
}
@@ -1071,38 +1116,51 @@
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
/// CHECK-DAG: If [<<Arg>>]
+ /// CHECK-START: int Main.NotEqualFalseLhs(boolean) instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const3>>,<<Const5>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
public static int NotEqualFalseLhs(boolean arg) {
return (false == arg) ? 3 : 5;
}
/// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
- /// CHECK-DAG: <<BoolNot:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<BoolNot>>,<<Const2>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<NotArg>>,<<Const2>>]
+ /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<NotCond>>]
/// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
- /// CHECK-DAG: <<False:i\d+>> IntConstant 0
- /// CHECK-DAG: Return [<<False>>]
+ /// CHECK-DAG: <<True:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<True>>]
public static boolean EqualBoolVsIntConst(boolean arg) {
- return (arg ? 0 : 1) == 2;
+ return (arg ? 0 : 1) != 2;
}
/// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
- /// CHECK-DAG: <<BoolNot:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<BoolNot>>,<<Const2>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Cond:z\d+>> NotEqual [<<NotArg>>,<<Const2>>]
+ /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<NotCond>>]
/// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after)
- /// CHECK-DAG: <<True:i\d+>> IntConstant 1
- /// CHECK-DAG: Return [<<True>>]
+ /// CHECK-DAG: <<False:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<False>>]
public static boolean NotEqualBoolVsIntConst(boolean arg) {
- return (arg ? 0 : 1) != 2;
+ return (arg ? 0 : 1) == 2;
}
/*
@@ -1113,19 +1171,16 @@
/// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<NotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<NotNotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<NotArg>>]
/// CHECK-DAG: Return [<<NotNotArg>>]
/// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: BooleanNot [<<Arg>>]
/// CHECK-DAG: Return [<<Arg>>]
- /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after)
- /// CHECK: BooleanNot
- /// CHECK-NOT: BooleanNot
-
public static boolean NegateValue(boolean arg) {
return !arg;
}
@@ -1254,8 +1309,14 @@
/// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier (after)
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
- /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Field>>]
- /// CHECK-DAG: If [<<Not>>]
+ /// CHECK-DAG: If [<<Field>>]
+
+ /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int booleanFieldNotEqualOne() {
return (booleanField == true) ? 13 : 54;
@@ -1269,8 +1330,14 @@
/// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier (after)
/// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
- /// CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Field>>]
- /// CHECK-DAG: If [<<Not>>]
+ /// CHECK-DAG: If [<<Field>>]
+
+ /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_before_codegen (after)
+ /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int booleanFieldEqualZero() {
return (booleanField != false) ? 13 : 54;
@@ -1278,18 +1345,27 @@
/// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: <<GT:z\d+>> GreaterThan [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<GT:i\d+>> Select [<<Const1>>,<<Const0>>,<<LE>>]
/// CHECK-DAG: <<NE:z\d+>> NotEqual [<<GT>>,<<Const1>>]
- /// CHECK-DAG: If [<<NE>>]
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
+ /// CHECK-DAG: Return [<<Result>>]
/// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: If [<<LE:z\d+>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>]
/// CHECK-DAG: <<LE>> LessThanOrEqual [<<Arg>>,<<Const42>>]
- // Note that we match `LE` from If because there are two identical LessThanOrEqual instructions.
+ /// CHECK-DAG: Return [<<Result>>]
+ // Note that we match `LE` from Select because there are two identical
+ // LessThanOrEqual instructions.
public static int intConditionNotEqualOne(int i) {
return ((i > 42) == true) ? 13 : 54;
@@ -1298,17 +1374,26 @@
/// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: <<GT:z\d+>> GreaterThan [<<Arg>>,<<Const42>>]
- /// CHECK-DAG: <<EQ:z\d+>> Equal [<<GT>>,<<Const0>>]
- /// CHECK-DAG: If [<<EQ>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<GT:i\d+>> Select [<<Const1>>,<<Const0>>,<<LE>>]
+ /// CHECK-DAG: <<NE:z\d+>> Equal [<<GT>>,<<Const0>>]
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>]
+ /// CHECK-DAG: Return [<<Result>>]
/// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: If [<<LE:z\d+>>]
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE:z\d+>>]
/// CHECK-DAG: <<LE>> LessThanOrEqual [<<Arg>>,<<Const42>>]
- // Note that we match `LE` from If because there are two identical LessThanOrEqual instructions.
+ /// CHECK-DAG: Return [<<Result>>]
+ // Note that we match `LE` from Select because there are two identical
+ // LessThanOrEqual instructions.
public static int intConditionEqualZero(int i) {
return ((i > 42) != false) ? 13 : 54;
@@ -1316,17 +1401,33 @@
// Test that conditions on float/double are not flipped.
+ /// CHECK-START: int Main.floatConditionNotEqualOne(float) ssa_builder (after)
+ /// CHECK: LessThanOrEqual
+
/// CHECK-START: int Main.floatConditionNotEqualOne(float) register (before)
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: NotEqual [{{i\d+}},<<Const1>>]
+ /// CHECK-DAG: <<Arg:f\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Const42:f\d+>> FloatConstant 42
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int floatConditionNotEqualOne(float f) {
return ((f > 42.0f) == true) ? 13 : 54;
}
+ /// CHECK-START: int Main.doubleConditionEqualZero(double) ssa_builder (after)
+ /// CHECK: LessThanOrEqual
+
/// CHECK-START: int Main.doubleConditionEqualZero(double) register (before)
- /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: Equal [{{i\d+}},<<Const0>>]
+ /// CHECK-DAG: <<Arg:d\d+>> ParameterValue
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54
+ /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42
+ /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Arg>>,<<Const42>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static int doubleConditionEqualZero(double d) {
return ((d > 42.0) != false) ? 13 : 54;
@@ -1374,6 +1475,10 @@
assertIntEquals(NotEqualTrueLhs(true), 3);
assertIntEquals(NotEqualFalseRhs(true), 5);
assertIntEquals(NotEqualFalseLhs(true), 5);
+ assertBooleanEquals(EqualBoolVsIntConst(true), true);
+ assertBooleanEquals(EqualBoolVsIntConst(true), true);
+ assertBooleanEquals(NotEqualBoolVsIntConst(false), false);
+ assertBooleanEquals(NotEqualBoolVsIntConst(false), false);
assertBooleanEquals(NotNotBool(true), true);
assertBooleanEquals(NotNotBool(false), false);
assertFloatEquals(Div2(100.0f), 50.0f);
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index 61510d8..682f126 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -37,7 +37,7 @@
* empty branches removed.
*/
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -45,23 +45,24 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before)
/// CHECK: Goto
/// CHECK: Goto
/// CHECK: Goto
/// CHECK-NOT: Goto
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
- /// CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<NotParam:i\d+>> Select [<<Const1>>,<<Const0>>,<<Param>>]
/// CHECK-DAG: Return [<<NotParam>>]
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
/// CHECK-NOT: If
/// CHECK-NOT: Phi
- /// CHECK-START: boolean Main.BooleanNot(boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after)
/// CHECK: Goto
/// CHECK-NOT: Goto
@@ -74,7 +75,7 @@
* and 0 when False.
*/
- /// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (before)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
@@ -84,13 +85,14 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0>>,<<Const1>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: boolean Main.GreaterThan(int, int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.GreaterThan(int, int) select_generator (after)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Cond:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const0>>,<<Const1>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static boolean GreaterThan(int x, int y) {
return (x <= y) ? false : true;
@@ -101,7 +103,7 @@
* and 1 when False.
*/
- /// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.LessThan(int, int) select_generator (before)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
@@ -111,13 +113,14 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: boolean Main.LessThan(int, int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.LessThan(int, int) select_generator (after)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> LessThan [<<ParamX>>,<<ParamY>>]
- /// CHECK-DAG: Return [<<Cond>>]
+ /// CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<ParamX>>,<<ParamY>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<Select>>]
public static boolean LessThan(int x, int y) {
return (x < y) ? true : false;
@@ -128,7 +131,7 @@
* Note that Phis are discovered retrospectively.
*/
- /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (before)
+ /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (before)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
@@ -145,29 +148,25 @@
/// CHECK-DAG: <<PhiYZ>> Phi [<<Const1>>,<<Const0>>]
/// CHECK-DAG: <<PhiXYZ>> Phi [<<Const1>>,<<Const0>>]
- /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (after)
/// CHECK-DAG: <<ParamX:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
/// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
- /// CHECK-DAG: <<CmpXY:z\d+>> LessThanOrEqual [<<ParamX>>,<<ParamY>>]
- /// CHECK-DAG: <<CmpYZ:z\d+>> LessThanOrEqual [<<ParamY>>,<<ParamZ>>]
- /// CHECK-DAG: <<CmpXYZ:z\d+>> Equal [<<CmpXY>>,<<CmpYZ>>]
- /// CHECK-DAG: Return [<<CmpXYZ>>]
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<CmpXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>]
+ /// CHECK-DAG: <<SelXY:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXY>>]
+ /// CHECK-DAG: <<CmpYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>]
+ /// CHECK-DAG: <<SelYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpYZ>>]
+ /// CHECK-DAG: <<CmpXYZ:z\d+>> NotEqual [<<SelXY>>,<<SelYZ>>]
+ /// CHECK-DAG: <<SelXYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXYZ>>]
+ /// CHECK-DAG: Return [<<SelXYZ>>]
public static boolean ValuesOrdered(int x, int y, int z) {
return (x <= y) == (y <= z);
}
- /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (before)
- /// CHECK-DAG: <<Param:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
- /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
- /// CHECK-DAG: <<NotParam:z\d+>> BooleanNot [<<Param>>]
- /// CHECK-DAG: If [<<NotParam>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>]
- /// CHECK-DAG: Return [<<Phi>>]
-
- /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after)
+ /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (before)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
/// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
@@ -175,9 +174,14 @@
/// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>]
/// CHECK-DAG: Return [<<Phi>>]
- // Note: The fact that branches are swapped is verified by running the test.
+ /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after)
+ /// CHECK-DAG: <<Param:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Const42>>,<<Param>>]
+ /// CHECK-DAG: Return [<<Select>>]
- /// CHECK-START: int Main.NegatedCondition(boolean) boolean_simplifier (after)
+ /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after)
/// CHECK-NOT: BooleanNot
public static int NegatedCondition(boolean x) {
@@ -188,6 +192,179 @@
}
}
+ /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<ParamY>>,<<Const42>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Add>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after)
+ /// CHECK-NOT: If
+
+ public static int SimpleTrueBlock(boolean x, int y) {
+ return x ? y + 42 : 43;
+ }
+
+ /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<ParamY>>,<<Const43>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Add>>,<<Const42>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: int Main.SimpleFalseBlock(boolean, int) select_generator (after)
+ /// CHECK-NOT: If
+
+ public static int SimpleFalseBlock(boolean x, int y) {
+ return x ? 42 : y + 43;
+ }
+
+ /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: <<AddTrue:i\d+>> Add [<<ParamY>>,<<Const42>>]
+ /// CHECK-DAG: <<AddFalse:i\d+>> Add [<<ParamZ>>,<<Const43>>]
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<AddFalse>>,<<AddTrue>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select>>]
+
+ /// CHECK-START: int Main.SimpleBothBlocks(boolean, int, int) select_generator (after)
+ /// CHECK-NOT: If
+
+ public static int SimpleBothBlocks(boolean x, int y, int z) {
+ return x ? y + 42 : z + 43;
+ }
+
+ /// CHECK-START: int Main.ThreeBlocks(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue
+ /// CHECK-DAG: <<ParamY:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Select23:i\d+>> Select [<<Const3>>,<<Const2>>,<<ParamY>>]
+ /// CHECK-DAG: <<Select123:i\d+>> Select [<<Select23>>,<<Const1>>,<<ParamX>>]
+ /// CHECK-DAG: Return [<<Select123>>]
+
+ public static int ThreeBlocks(boolean x, boolean y) {
+ if (x) {
+ return 1;
+ } else if (y) {
+ return 2;
+ } else {
+ return 3;
+ }
+ }
+
+ /// CHECK-START: int Main.MultiplePhis() select_generator (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Const13>>,<<Const42>>]
+ /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>,<<Add>>]
+ /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>]
+ /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>]
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: Return [<<PhiX>>]
+
+ /// CHECK-START: int Main.MultiplePhis() select_generator (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Select:i\d+>>]
+ /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>]
+ /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>]
+ /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>]
+ /// CHECK-DAG: <<Select>> Select [<<Const13>>,<<Const42>>,<<Cond>>]
+ /// CHECK-DAG: Return [<<PhiX>>]
+
+ public static int MultiplePhis() {
+ int x = 0;
+ int y = 1;
+ while (y++ < 10) {
+ if (y > 1) {
+ x = 13;
+ } else {
+ x = 42;
+ }
+ }
+ return x;
+ }
+
+ /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Iget:i\d+>> InstanceFieldGet [<<This>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Iget>>,<<Const2>>]
+ /// CHECK-DAG: Phi [<<Add>>,<<Const43>>]
+
+ /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int TrueBlockWithTooManyInstructions(boolean x) {
+ return x ? (read_field + 2) : 43;
+ }
+
+ /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: <<Iget:i\d+>> InstanceFieldGet [<<This>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Iget>>,<<Const3>>]
+ /// CHECK-DAG: Phi [<<Const42>>,<<Add>>]
+
+ /// CHECK-START: int Main.FalseBlockWithTooManyInstructions(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int FalseBlockWithTooManyInstructions(boolean x) {
+ return x ? 42 : (read_field + 3);
+ }
+
+ /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: InstanceFieldSet [<<This>>,<<Const42>>]
+ /// CHECK-DAG: Phi [<<Const42>>,<<Const43>>]
+
+ /// CHECK-START: int Main.TrueBlockWithSideEffects(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int TrueBlockWithSideEffects(boolean x) {
+ return x ? (write_field = 42) : 43;
+ }
+
+ /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (before)
+ /// CHECK-DAG: <<This:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
+ /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43
+ /// CHECK-DAG: If [<<Cond>>]
+ /// CHECK-DAG: InstanceFieldSet [<<This>>,<<Const43>>]
+ /// CHECK-DAG: Phi [<<Const42>>,<<Const43>>]
+
+ /// CHECK-START: int Main.FalseBlockWithSideEffects(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+
+ public int FalseBlockWithSideEffects(boolean x) {
+ return x ? 42 : (write_field = 43);
+ }
+
public static void main(String[] args) {
assertBoolEquals(false, BooleanNot(true));
assertBoolEquals(true, BooleanNot(false));
@@ -206,5 +383,30 @@
assertBoolEquals(false, ValuesOrdered(5, 5, 3));
assertIntEquals(42, NegatedCondition(true));
assertIntEquals(43, NegatedCondition(false));
+ assertIntEquals(46, SimpleTrueBlock(true, 4));
+ assertIntEquals(43, SimpleTrueBlock(false, 4));
+ assertIntEquals(42, SimpleFalseBlock(true, 7));
+ assertIntEquals(50, SimpleFalseBlock(false, 7));
+ assertIntEquals(48, SimpleBothBlocks(true, 6, 2));
+ assertIntEquals(45, SimpleBothBlocks(false, 6, 2));
+ assertIntEquals(1, ThreeBlocks(true, true));
+ assertIntEquals(1, ThreeBlocks(true, false));
+ assertIntEquals(2, ThreeBlocks(false, true));
+ assertIntEquals(3, ThreeBlocks(false, false));
+ assertIntEquals(13, MultiplePhis());
+
+ Main m = new Main();
+ assertIntEquals(42, m.TrueBlockWithTooManyInstructions(true));
+ assertIntEquals(43, m.TrueBlockWithTooManyInstructions(false));
+ assertIntEquals(42, m.FalseBlockWithTooManyInstructions(true));
+ assertIntEquals(43, m.FalseBlockWithTooManyInstructions(false));
+ assertIntEquals(42, m.TrueBlockWithSideEffects(true));
+ assertIntEquals(43, m.TrueBlockWithSideEffects(false));
+ assertIntEquals(42, m.FalseBlockWithSideEffects(true));
+ assertIntEquals(43, m.FalseBlockWithSideEffects(false));
}
+
+ // These need to be instance fields so as to not generate a LoadClass for iget/iput.
+ public int read_field = 40;
+ public int write_field = 42;
}
diff --git a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
index da1c5ec..87ad21e 100644
--- a/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
+++ b/test/468-checker-bool-simplifier-regression/smali/TestCase.smali
@@ -18,7 +18,7 @@
.field public static value:Z
-## CHECK-START: boolean TestCase.testCase() boolean_simplifier (before)
+## CHECK-START: boolean TestCase.testCase() select_generator (before)
## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
## CHECK-DAG: <<Value:z\d+>> StaticFieldGet
@@ -26,10 +26,12 @@
## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
## CHECK-DAG: Return [<<Phi>>]
-## CHECK-START: boolean TestCase.testCase() boolean_simplifier (after)
+## CHECK-START: boolean TestCase.testCase() select_generator (after)
+## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
## CHECK-DAG: <<Value:z\d+>> StaticFieldGet
-## CHECK-DAG: <<Not:z\d+>> BooleanNot [<<Value>>]
-## CHECK-DAG: Return [<<Not>>]
+## CHECK-DAG: <<Select:i\d+>> Select [<<Const1>>,<<Const0>>,<<Value>>]
+## CHECK-DAG: Return [<<Select>>]
.method public static testCase()Z
.registers 2
diff --git a/test/474-checker-boolean-input/src/Main.java b/test/474-checker-boolean-input/src/Main.java
index a2b219d..fbc28d8 100644
--- a/test/474-checker-boolean-input/src/Main.java
+++ b/test/474-checker-boolean-input/src/Main.java
@@ -27,9 +27,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestPhiAsBoolean(int) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestPhiAsBoolean(int) select_generator (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi
- /// CHECK-DAG: BooleanNot [<<Phi>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Phi>>]
public static boolean f1;
public static boolean f2;
@@ -47,9 +47,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestAndAsBoolean(boolean, boolean) select_generator (after)
/// CHECK-DAG: <<And:i\d+>> And
- /// CHECK-DAG: BooleanNot [<<And>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<And>>]
public static boolean InlineAnd(boolean x, boolean y) {
return x & y;
@@ -64,9 +64,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestOrAsBoolean(boolean, boolean) select_generator (after)
/// CHECK-DAG: <<Or:i\d+>> Or
- /// CHECK-DAG: BooleanNot [<<Or>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Or>>]
public static boolean InlineOr(boolean x, boolean y) {
return x | y;
@@ -81,9 +81,9 @@
* we implement a suitable type analysis.
*/
- /// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) boolean_simplifier (after)
+ /// CHECK-START: boolean Main.TestXorAsBoolean(boolean, boolean) select_generator (after)
/// CHECK-DAG: <<Xor:i\d+>> Xor
- /// CHECK-DAG: BooleanNot [<<Xor>>]
+ /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Xor>>]
public static boolean InlineXor(boolean x, boolean y) {
return x ^ y;
diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java
index 5adafaf..e5171f0 100644
--- a/test/480-checker-dead-blocks/src/Main.java
+++ b/test/480-checker-dead-blocks/src/Main.java
@@ -56,6 +56,8 @@
z = x + y;
} else {
z = x - y;
+ // Prevent HSelect simplification by having a branch with multiple instructions.
+ System.nanoTime();
}
return z;
}
@@ -86,6 +88,8 @@
z = x + y;
} else {
z = x - y;
+ // Prevent HSelect simplification by having a branch with multiple instructions.
+ System.nanoTime();
}
return z;
}
diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali
index 1de0bae..056f22c 100644
--- a/test/485-checker-dce-loop-update/smali/TestCase.smali
+++ b/test/485-checker-dce-loop-update/smali/TestCase.smali
@@ -137,15 +137,14 @@
## CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
## CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
## CHECK-DAG: <<Cst11:i\d+>> IntConstant 11
-## CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+## CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add5:i\d+>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
-## CHECK-DAG: If [<<ArgZ>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst11>>] loop:<<HeaderY>>
-## CHECK-DAG: <<PhiX2:i\d+>> Phi [<<PhiX1>>,<<Mul9>>] loop:<<HeaderY>>
+## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX>>,<<Cst11>>] loop:<<HeaderY>>
+## CHECK-DAG: <<SelX:i\d+>> Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>] loop:<<HeaderY>>
## CHECK-DAG: If [<<Cst1>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Add5>> Add [<<PhiX2>>,<<Cst5>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>>
-## CHECK-DAG: Return [<<PhiX2>>] loop:none
+## CHECK-DAG: <<Add5>> Add [<<SelX>>,<<Cst5>>] loop:<<HeaderY>>
+## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+## CHECK-DAG: Return [<<SelX>>] loop:none
## CHECK-START: int TestCase.testExitPredecessors(int, boolean, boolean) dead_code_elimination_final (after)
## CHECK-DAG: <<ArgX:i\d+>> ParameterValue
@@ -153,13 +152,12 @@
## CHECK-DAG: <<ArgZ:z\d+>> ParameterValue
## CHECK-DAG: <<Cst7:i\d+>> IntConstant 7
## CHECK-DAG: <<Cst11:i\d+>> IntConstant 11
-## CHECK-DAG: <<PhiX1:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
+## CHECK-DAG: <<PhiX:i\d+>> Phi [<<ArgX>>,<<Add7:i\d+>>] loop:<<HeaderY:B\d+>>
## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
-## CHECK-DAG: <<Add7>> Add [<<PhiX1>>,<<Cst7>>] loop:<<HeaderY>>
-## CHECK-DAG: If [<<ArgZ>>] loop:none
-## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX1>>,<<Cst11>>] loop:none
-## CHECK-DAG: <<PhiX2:i\d+>> Phi [<<PhiX1>>,<<Mul9>>] loop:none
-## CHECK-DAG: Return [<<PhiX2>>] loop:none
+## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
+## CHECK-DAG: <<Mul9:i\d+>> Mul [<<PhiX>>,<<Cst11>>] loop:none
+## CHECK-DAG: <<SelX:i\d+>> Select [<<PhiX>>,<<Mul9>>,<<ArgZ>>] loop:none
+## CHECK-DAG: Return [<<SelX>>] loop:none
.method public static testExitPredecessors(IZZ)I
.registers 4
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index baee7b3..f87326c 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -595,19 +595,16 @@
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<True>>]
/// CHECK-DAG: InstanceFieldSet [<<Obj>>,<<Float8>>]
/// CHECK-DAG: <<GetTest:z\d+>> InstanceFieldGet [<<Obj>>]
- /// CHECK-DAG: If [<<GetTest>>]
/// CHECK-DAG: <<GetField:f\d+>> InstanceFieldGet [<<Obj>>]
- /// CHECK-DAG: <<Phi:f\d+>> Phi [<<Float42>>,<<GetField>>]
- /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<GetField>>,<<GetTest>>]
+ /// CHECK-DAG: Return [<<Select>>]
/// CHECK-START: float Main.test24() load_store_elimination (after)
/// CHECK-DAG: <<True:i\d+>> IntConstant 1
/// CHECK-DAG: <<Float8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Float42:f\d+>> FloatConstant 42
- /// CHECK-DAG: <<Obj:l\d+>> NewInstance
- /// CHECK-DAG: If [<<True>>]
- /// CHECK-DAG: <<Phi:f\d+>> Phi [<<Float42>>,<<Float8>>]
- /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-DAG: <<Select:f\d+>> Select [<<Float42>>,<<Float8>>,<<True>>]
+ /// CHECK-DAG: Return [<<Select>>]
static float test24() {
float a = 42.0f;
diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali
index 1756fa4..62511df 100644
--- a/test/543-checker-dce-trycatch/smali/TestCase.smali
+++ b/test/543-checker-dce-trycatch/smali/TestCase.smali
@@ -15,6 +15,8 @@
.class public LTestCase;
.super Ljava/lang/Object;
+.field public static sField:I
+
.method private static $inline$False()Z
.registers 1
const/4 v0, 0x0
@@ -240,24 +242,25 @@
shr-int/2addr p2, p3
:try_start
- const v1, 0xa # dead catch phi input, defined in entry block (HInstruction)
- add-int v2, p0, p1 # dead catch phi input, defined in the dead block (HInstruction)
+ const v1, 0xa # dead catch phi input, defined in entry block (HInstruction)
+ add-int v2, p0, p1 # dead catch phi input, defined in the dead block (HInstruction)
move v3, v2
if-eqz v3, :define_phi
+ sput v3, LTestCase;->sField:I # beat HSelect simplification (has side-effects, does not throw)
const v3, 0xf
:define_phi
- # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi)
+ # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi)
div-int/2addr p0, v2
:else
- const v1, 0xb # live catch phi input
- const v2, 0xc # live catch phi input
- const v3, 0x10 # live catch phi input
+ const v1, 0xb # live catch phi input
+ const v2, 0xc # live catch phi input
+ const v3, 0x10 # live catch phi input
div-int/2addr p0, p3
- const v1, 0xd # live catch phi input
- const v2, 0xe # live catch phi input
- const v3, 0x11 # live catch phi input
+ const v1, 0xd # live catch phi input
+ const v2, 0xe # live catch phi input
+ const v3, 0x11 # live catch phi input
div-int/2addr p0, p1
:try_end
.catchall {:try_start .. :try_end} :catch_all
diff --git a/test/563-checker-invoke-super/build b/test/563-checker-invoke-super/build
new file mode 100755
index 0000000..e06193b
--- /dev/null
+++ b/test/563-checker-invoke-super/build
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# Make us exit on a failure.
+#
+set -e
+
+# Hard-wired use of experimental jack.
+# TODO: fix this temporary work-around for lambdas, see b/19467889
+export USE_JACK=true
+export JACK_SERVER=false
+export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks"
+
+# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA
+export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')"
+./default-build "$@" --experimental default-methods
diff --git a/test/563-checker-invoke-super/expected.txt b/test/563-checker-invoke-super/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/563-checker-invoke-super/expected.txt
diff --git a/test/563-checker-invoke-super/info.txt b/test/563-checker-invoke-super/info.txt
new file mode 100644
index 0000000..23c0d2f
--- /dev/null
+++ b/test/563-checker-invoke-super/info.txt
@@ -0,0 +1,2 @@
+Tests that invoke-super's to interface methods are optimized to direct method
+calls when in the same dex file.
diff --git a/test/563-checker-invoke-super/src/Main.java b/test/563-checker-invoke-super/src/Main.java
new file mode 100644
index 0000000..8554dbd
--- /dev/null
+++ b/test/563-checker-invoke-super/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface IFace {
+ public default void $noinline$aMethod() { throw new RuntimeException("Should not be called"); }
+}
+
+class ClassImplA implements IFace {
+ /// CHECK-START: void ClassImplA.testSuperInvoke() builder (after)
+ /// CHECK: InvokeStaticOrDirect
+ public void testSuperInvoke() {
+ IFace.super.$noinline$aMethod();
+ }
+}
+
+class ClassImplB extends ClassImplA {
+ /// CHECK-START: void ClassImplB.testSuperInvoke2() builder (after)
+ /// CHECK: InvokeStaticOrDirect
+ public void testSuperInvoke2() {
+ super.$noinline$aMethod();
+ }
+}
+
+public class Main {
+ public static void main(String[] args) { }
+}
diff --git a/test/565-checker-condition-liveness/expected.txt b/test/565-checker-condition-liveness/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/565-checker-condition-liveness/expected.txt
diff --git a/test/565-checker-condition-liveness/info.txt b/test/565-checker-condition-liveness/info.txt
new file mode 100644
index 0000000..67b6ceb
--- /dev/null
+++ b/test/565-checker-condition-liveness/info.txt
@@ -0,0 +1 @@
+Test the use positions of inputs of non-materialized conditions.
\ No newline at end of file
diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java
new file mode 100644
index 0000000..a811e5b
--- /dev/null
+++ b/test/565-checker-condition-liveness/src/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: void Main.main(java.lang.String[]) liveness (after)
+ /// CHECK: <<X:i\d+>> ArrayLength uses:[<<UseInput:\d+>>]
+ /// CHECK: <<Y:i\d+>> StaticFieldGet uses:[<<UseInput>>]
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [<<X>>,<<Y>>]
+ /// CHECK-NEXT: If [<<Cond>>] liveness:<<LivIf:\d+>>
+ /// CHECK-EVAL: <<UseInput>> == <<LivIf>> + 1
+
+ public static void main(String[] args) {
+ int x = args.length;
+ int y = field;
+ if (x > y) {
+ System.nanoTime();
+ }
+ }
+
+ public static int field = 42;
+}
diff --git a/test/565-checker-irreducible-loop/expected.txt b/test/565-checker-irreducible-loop/expected.txt
new file mode 100644
index 0000000..6ed281c
--- /dev/null
+++ b/test/565-checker-irreducible-loop/expected.txt
@@ -0,0 +1,2 @@
+1
+1
diff --git a/test/565-checker-irreducible-loop/info.txt b/test/565-checker-irreducible-loop/info.txt
new file mode 100644
index 0000000..1e0dd02
--- /dev/null
+++ b/test/565-checker-irreducible-loop/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing in the presence of
+an irreducible loop.
diff --git a/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..29547ca
--- /dev/null
+++ b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -0,0 +1,101 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+# Check that both the irreducible loop and the other loop entry
+# move the constant-folded value to where it's expected.
+
+## CHECK-START-X86: int IrreducibleLoop.test1(int, long) register (after)
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test1(IJ)I
+ .registers 10
+ const/16 v6, 2
+ const/16 v4, 1
+ const-wide/16 v0, 42
+ add-long v2, v0, v0
+
+ if-eqz p0, :loop_entry
+ goto :other_loop_pre_entry
+
+ # The then part: beginning of the irreducible loop.
+ :loop_entry
+ if-eqz p0, :exit
+ cmp-long v6, v2, p1
+ :other_loop_entry
+ sub-int p0, p0, v4
+ goto :loop_entry
+
+ # The other block branching to the irreducible loop.
+ # In that block, v4 has no live range.
+ :other_loop_pre_entry
+ goto :other_loop_entry
+
+ :exit
+ return v6
+.end method
+
+# Check that the compiler does not crash when
+# a live interval is found while connecting siblings, but that
+# live interval is inactive at the desired position.
+
+## CHECK-START-X86: int IrreducibleLoop.test2(int, long) register (after)
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test2(IJ)I
+ .registers 14
+ const/16 v6, 2
+ const/16 v4, 1
+ const-wide/16 v0, 42
+ const-wide/16 v8, 68
+ add-long v2, v0, v0
+
+ if-eqz p0, :loop_entry
+ goto :other_loop_pre_entry
+
+ # The then part: beginning of the irreducible loop.
+ :loop_entry
+ if-eqz p0, :exit
+ cmp-long v6, v2, p1
+ :other_loop_entry
+ sub-int p0, p0, v4
+ goto :loop_entry
+
+ # The other block branching to the irreducible loop.
+ :other_loop_pre_entry
+ # Make v2 have a register location.
+ sput-wide v2, LIrreducibleLoop;->myField:J
+ # Stress register allocator on x86 to split v2.
+ sput-wide v0, LIrreducibleLoop;->myField:J
+ sput-wide p1, LIrreducibleLoop;->myField:J
+ sput-wide v8, LIrreducibleLoop;->myField:J
+ if-eqz p0, :join
+ # Stress register allocator on x86 to split v2.
+ sput-wide p1, LIrreducibleLoop;->myField:J
+ sput-wide v8, LIrreducibleLoop;->myField:J
+ sput-wide v0, LIrreducibleLoop;->myField:J
+ # Last use of v2 before the irreducible loop, that
+ # will create an interval hole.
+ sput-wide v2, LIrreducibleLoop;->myField:J
+ :join
+ goto :other_loop_entry
+
+ :exit
+ return v6
+.end method
+
+.field public static volatile myField:J
diff --git a/test/565-checker-irreducible-loop/src/Main.java b/test/565-checker-irreducible-loop/src/Main.java
new file mode 100644
index 0000000..e48bd6b
--- /dev/null
+++ b/test/565-checker-irreducible-loop/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ {
+ Method m = c.getMethod("test1", int.class, long.class);
+ Object[] arguments = { 42, 31L };
+ System.out.println(m.invoke(null, arguments));
+ }
+
+ {
+ Method m = c.getMethod("test2", int.class, long.class);
+ Object[] arguments = { 42, 31L };
+ System.out.println(m.invoke(null, arguments));
+ }
+ }
+}
diff --git a/test/566-checker-codegen-select/expected.txt b/test/566-checker-codegen-select/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/566-checker-codegen-select/expected.txt
diff --git a/test/566-checker-codegen-select/info.txt b/test/566-checker-codegen-select/info.txt
new file mode 100644
index 0000000..67b6ceb
--- /dev/null
+++ b/test/566-checker-codegen-select/info.txt
@@ -0,0 +1 @@
+Test the use positions of inputs of non-materialized conditions.
\ No newline at end of file
diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java
new file mode 100644
index 0000000..edb31e6
--- /dev/null
+++ b/test/566-checker-codegen-select/src/Main.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: long Main.$noinline$longSelect(long) register (before)
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}]
+ /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+
+ // Condition must be materialized on X86 because it would need too many
+ // registers otherwise.
+ /// CHECK-START-X86: long Main.$noinline$longSelect(long) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: cmp
+ /// CHECK: Select
+
+ public long $noinline$longSelect(long param) {
+ if (doThrow) { throw new Error(); }
+ long val_true = longB;
+ long val_false = longC;
+ return (param > longA) ? val_true : val_false;
+ }
+
+ /// CHECK-START: long Main.$noinline$longSelect_Constant(long) register (before)
+ /// CHECK: <<Const:j\d+>> LongConstant
+ /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},<<Const>>]
+ /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
+
+ // Condition can be non-materialized on X86 because the condition does not
+ // request 4 registers any more.
+ /// CHECK-START-X86: long Main.$noinline$longSelect_Constant(long) disassembly (after)
+ /// CHECK: LessThanOrEqual
+ /// CHECK-NEXT: Select
+
+ public long $noinline$longSelect_Constant(long param) {
+ if (doThrow) { throw new Error(); }
+ long val_true = longB;
+ long val_false = longC;
+ return (param > 3L) ? val_true : val_false;
+ }
+
+ public static void main(String[] args) {
+ Main m = new Main();
+ assertLongEquals(5L, m.$noinline$longSelect(4L));
+ assertLongEquals(7L, m.$noinline$longSelect(2L));
+ assertLongEquals(5L, m.$noinline$longSelect_Constant(4L));
+ assertLongEquals(7L, m.$noinline$longSelect_Constant(2L));
+ }
+
+ public static void assertLongEquals(long expected, long actual) {
+ if (expected != actual) {
+ throw new Error(expected + " != " + actual);
+ }
+ }
+
+ public boolean doThrow = false;
+
+ public long longA = 3L;
+ public long longB = 5L;
+ public long longC = 7L;
+}
diff --git a/test/566-polymorphic-inlining/expected.txt b/test/566-polymorphic-inlining/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/566-polymorphic-inlining/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/566-polymorphic-inlining/info.txt b/test/566-polymorphic-inlining/info.txt
new file mode 100644
index 0000000..1a47f89
--- /dev/null
+++ b/test/566-polymorphic-inlining/info.txt
@@ -0,0 +1 @@
+Test polymorphic inlining.
diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc
new file mode 100644
index 0000000..b2934ed
--- /dev/null
+++ b/test/566-polymorphic-inlining/polymorphic_inline.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "art_method.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "oat_quick_method_header.h"
+#include "scoped_thread_state_change.h"
+#include "stack_map.h"
+
+namespace art {
+
+static void do_checks(jclass cls, const char* method_name) {
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ ArtMethod* method = klass->FindDeclaredDirectMethodByName(method_name, sizeof(void*));
+
+ OatQuickMethodHeader* header = nullptr;
+ // Infinite loop... Test harness will have its own timeout.
+ while (true) {
+ header = OatQuickMethodHeader::FromEntryPoint(method->GetEntryPointFromQuickCompiledCode());
+ if (code_cache->ContainsPc(header->GetCode())) {
+ break;
+ } else {
+ // sleep one second to give time to the JIT compiler.
+ sleep(1);
+ }
+ }
+
+ CodeInfo info = header->GetOptimizedCodeInfo();
+ CHECK(info.HasInlineInfo());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureJittedAndPolymorphicInline(JNIEnv*, jclass cls) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit == nullptr) {
+ return;
+ }
+
+ if (kIsDebugBuild) {
+ // A debug build might often compile the methods without profiling informations filled.
+ return;
+ }
+
+ do_checks(cls, "testInvokeVirtual");
+ do_checks(cls, "testInvokeInterface");
+}
+
+} // namespace art
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
new file mode 100644
index 0000000..7283e86
--- /dev/null
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Itf {
+ public Class sameInvokeInterface();
+}
+
+public class Main implements Itf {
+ public static void assertEquals(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Main[] mains = new Main[3];
+ Itf[] itfs = new Itf[3];
+ itfs[0] = mains[0] = new Main();
+ itfs[1] = mains[1] = new Subclass();
+ itfs[2] = mains[2] = new OtherSubclass();
+
+ // Make testInvokeVirtual and testInvokeInterface hot to get them jitted.
+ // We pass Main and Subclass to get polymorphic inlining based on calling
+ // the same method.
+ for (int i = 0; i < 10000; ++i) {
+ testInvokeVirtual(mains[0]);
+ testInvokeVirtual(mains[1]);
+ testInvokeInterface(itfs[0]);
+ testInvokeInterface(itfs[1]);
+ }
+
+ ensureJittedAndPolymorphicInline();
+
+ // At this point, the JIT should have compiled both methods, and inline
+ // sameInvokeVirtual and sameInvokeInterface.
+ assertEquals(Main.class, testInvokeVirtual(mains[0]));
+ assertEquals(Main.class, testInvokeVirtual(mains[1]));
+
+ assertEquals(Itf.class, testInvokeInterface(itfs[0]));
+ assertEquals(Itf.class, testInvokeInterface(itfs[1]));
+
+ // This will trigger a deoptimization of the compiled code.
+ assertEquals(OtherSubclass.class, testInvokeVirtual(mains[2]));
+ assertEquals(OtherSubclass.class, testInvokeInterface(itfs[2]));
+ }
+
+ public Class sameInvokeVirtual() {
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
+ return Main.class;
+ }
+
+ public Class sameInvokeInterface() {
+ field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo
+ return Itf.class;
+ }
+
+ public static Class testInvokeInterface(Itf i) {
+ return i.sameInvokeInterface();
+ }
+
+ public static Class testInvokeVirtual(Main m) {
+ return m.sameInvokeVirtual();
+ }
+
+ public Object field = new Object();
+
+ public static native void ensureJittedAndPolymorphicInline();
+}
+
+class Subclass extends Main {
+}
+
+class OtherSubclass extends Main {
+ public Class sameInvokeVirtual() {
+ return OtherSubclass.class;
+ }
+
+ public Class sameInvokeInterface() {
+ return OtherSubclass.class;
+ }
+}
diff --git a/test/568-checker-onebit/src/Main.java b/test/568-checker-onebit/src/Main.java
index 7007c6a..6ce4ffb 100644
--- a/test/568-checker-onebit/src/Main.java
+++ b/test/568-checker-onebit/src/Main.java
@@ -45,6 +45,10 @@
}
public static void main(String args[]) {
+ // Hidden zeros.
+ int[] xi = new int[32];
+ long[] xj = new long[64];
+
expectEquals32(0x00000000, hi32(0x00000000));
expectEquals32(0x00000000, lo32(0x00000000));
expectEquals32(0x00010000, hi32(0x00010000));
@@ -55,6 +59,8 @@
expectEquals32(0x00000001, lo32(0xFFFFFFFF));
for (int i = 0; i < 32; i++) {
+ expectEquals32(0, hi32(xi[i]));
+ expectEquals32(0, lo32(xi[i]));
expectEquals32(1 << i, hi32(1 << i));
expectEquals32(1 << i, lo32(1 << i));
int expected = i < 29 ? 0x8 << i : 0x80000000;
@@ -72,6 +78,8 @@
expectEquals64(0x0000000000000001L, lo64(0xFFFFFFFFFFFFFFFFL));
for (int i = 0; i < 64; i++) {
+ expectEquals64(0L, hi64(xj[i]));
+ expectEquals64(0L, lo64(xj[i]));
expectEquals64(1L << i, hi64(1L << i));
expectEquals64(1L << i, lo64(1L << i));
long expected = i < 61 ? 0x8L << i : 0x8000000000000000L;
diff --git a/test/569-checker-pattern-replacement/expected.txt b/test/569-checker-pattern-replacement/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/569-checker-pattern-replacement/expected.txt
diff --git a/test/569-checker-pattern-replacement/info.txt b/test/569-checker-pattern-replacement/info.txt
new file mode 100644
index 0000000..4dfa932
--- /dev/null
+++ b/test/569-checker-pattern-replacement/info.txt
@@ -0,0 +1 @@
+Test pattern substitution used when we cannot inline.
diff --git a/test/569-checker-pattern-replacement/run b/test/569-checker-pattern-replacement/run
new file mode 100755
index 0000000..f7e9df2
--- /dev/null
+++ b/test/569-checker-pattern-replacement/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} "$@" \
+ -Xcompiler-option --no-inline-from=core-oj,569-checker-pattern-replacement.jar:classes2.dex
diff --git a/test/569-checker-pattern-replacement/src-multidex/Second.java b/test/569-checker-pattern-replacement/src-multidex/Second.java
new file mode 100644
index 0000000..cba1dc8
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src-multidex/Second.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class Second {
+ public static void staticNop(int unused) { }
+
+ public void nop() { }
+
+ public static Object staticReturnArg2(int unused1, String arg2) {
+ return arg2;
+ }
+
+ public long returnArg1(long arg1) {
+ return arg1;
+ }
+
+ public static int staticReturn9() {
+ return 9;
+ }
+
+ public int return7(Object unused) {
+ return 7;
+ }
+
+ public static String staticReturnNull() {
+ return null;
+ }
+
+ public Object returnNull() {
+ return null;
+ }
+
+ public int getInstanceIntField() {
+ return instanceIntField;
+ }
+
+ public double getInstanceDoubleField(int unused1) {
+ return instanceDoubleField;
+ }
+
+ public Object getInstanceObjectField(long unused1) {
+ return instanceObjectField;
+ }
+
+ public String getInstanceStringField(Object unused1, String unused2, long unused3) {
+ return instanceStringField;
+ }
+
+ public static int staticGetInstanceIntField(Second s) {
+ return s.instanceIntField;
+ }
+
+ public double getInstanceDoubleFieldFromParam(Second s) {
+ return s.instanceDoubleField;
+ }
+
+ public int getStaticIntField() {
+ return staticIntField;
+ }
+
+ public void setInstanceLongField(int ignored, long value) {
+ instanceLongField = value;
+ }
+
+ public int setInstanceLongFieldReturnArg2(long value, int arg2) {
+ instanceLongField = value;
+ return arg2;
+ }
+
+ public static void staticSetInstanceLongField(Second s, long value) {
+ s.instanceLongField = value;
+ }
+
+ public void setInstanceLongFieldThroughParam(Second s, long value) {
+ s.instanceLongField = value;
+ }
+
+ public void setStaticFloatField(float value) {
+ staticFloatField = value;
+ }
+
+ public int instanceIntField = 42;
+ public double instanceDoubleField = -42.0;
+ public Object instanceObjectField = null;
+ public String instanceStringField = "dummy";
+ public long instanceLongField = 0; // Overwritten by setters.
+
+ public static int staticIntField = 4242;
+ public static float staticFloatField = 0.0f; // Overwritten by setters.
+}
diff --git a/test/569-checker-pattern-replacement/src/Main.java b/test/569-checker-pattern-replacement/src/Main.java
new file mode 100644
index 0000000..e2d451c
--- /dev/null
+++ b/test/569-checker-pattern-replacement/src/Main.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ /// CHECK-START: void Main.staticNop() inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: void Main.staticNop() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static void staticNop() {
+ Second.staticNop(11);
+ }
+
+ /// CHECK-START: void Main.nop(Second) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: void Main.nop(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static void nop(Second s) {
+ s.nop();
+ }
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (before)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Ignored:i\d+>> IntConstant 77
+ /// CHECK-DAG: <<ClinitCk:l\d+>> ClinitCheck
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: <<Invoke:l\d+>> InvokeStaticOrDirect [<<Ignored>>,<<Value>>{{(,[ij]\d+)?}},<<ClinitCk>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-DAG: <<Value:l\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: java.lang.Object Main.staticReturnArg2(java.lang.String) inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object staticReturnArg2(String value) {
+ return Second.staticReturnArg2(77, value);
+ }
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (before)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<Invoke:j\d+>> InvokeVirtual [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: Return [<<Invoke>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Value>>]
+
+ /// CHECK-START: long Main.returnArg1(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long returnArg1(Second s, long value) {
+ return s.returnArg1(value);
+ }
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticReturn9() inliner (before)
+ /// CHECK-NOT: IntConstant 9
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-DAG: <<Const9:i\d+>> IntConstant 9
+ /// CHECK-DAG: Return [<<Const9>>]
+
+ /// CHECK-START: int Main.staticReturn9() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int staticReturn9() {
+ return Second.staticReturn9();
+ }
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.return7(Second) inliner (before)
+ /// CHECK-NOT: IntConstant 7
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
+ /// CHECK-DAG: Return [<<Const7>>]
+
+ /// CHECK-START: int Main.return7(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int return7(Second s) {
+ return s.return7(null);
+ }
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK: {{l\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.String Main.staticReturnNull() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static String staticReturnNull() {
+ return Second.staticReturnNull();
+ }
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (before)
+ /// CHECK-NOT: NullConstant
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-DAG: <<Null:l\d+>> NullConstant
+ /// CHECK-DAG: Return [<<Null>>]
+
+ /// CHECK-START: java.lang.Object Main.returnNull(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object returnNull(Second s) {
+ return s.returnNull();
+ }
+
+ /// CHECK-START: int Main.getInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InstanceFieldGet
+
+ /// CHECK-START: int Main.getInt(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static int getInt(Second s) {
+ return s.getInstanceIntField();
+ }
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK: {{d\d+}} InstanceFieldGet
+
+ /// CHECK-START: double Main.getDouble(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static double getDouble(Second s) {
+ return s.getInstanceDoubleField(22);
+ }
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.Object Main.getObject(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static Object getObject(Second s) {
+ return s.getInstanceObjectField(-1L);
+ }
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (before)
+ /// CHECK: {{l\d+}} InvokeVirtual
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK: {{l\d+}} InstanceFieldGet
+
+ /// CHECK-START: java.lang.String Main.getString(Second) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static String getString(Second s) {
+ return s.getInstanceStringField(null, "whatever", 1234L);
+ }
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.staticGetInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static int staticGetInt(Second s) {
+ return Second.staticGetInstanceIntField(s);
+ }
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (before)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK: {{d\d+}} InvokeVirtual
+
+ /// CHECK-START: double Main.getDoubleFromParam(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ public static double getDoubleFromParam(Second s) {
+ return s.getInstanceDoubleFieldFromParam(s);
+ }
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (before)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK: {{i\d+}} InvokeVirtual
+
+ /// CHECK-START: int Main.getStaticInt(Second) inliner (after)
+ /// CHECK-NOT: InstanceFieldGet
+ /// CHECK-NOT: StaticFieldGet
+
+ public static int getStaticInt(Second s) {
+ return s.getStaticIntField();
+ }
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: long Main.setLong(Second, long) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLong(Second s, long value) {
+ s.setInstanceLongField(-1, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-DAG: <<Second:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Value:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: InstanceFieldSet [<<NullCk>>,<<Value>>]
+ /// CHECK-DAG: <<NullCk2:l\d+>> NullCheck [<<Second>>]
+ /// CHECK-DAG: <<IGet:j\d+>> InstanceFieldGet [<<NullCk2>>]
+ /// CHECK-DAG: <<Conv:j\d+>> TypeConversion [<<Arg2>>]
+ /// CHECK-DAG: <<Add:j\d+>> Add [<<IGet>>,<<Conv>>]
+ /// CHECK-DAG: Return [<<Add>>]
+
+ /// CHECK-START: long Main.setLongReturnArg2(Second, long, int) inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ public static long setLongReturnArg2(Second s, long value, int arg2) {
+ int result = s.setInstanceLongFieldReturnArg2(value, arg2);
+ return s.instanceLongField + result;
+ }
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (before)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK: InvokeStaticOrDirect
+
+ /// CHECK-START: long Main.staticSetLong(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long staticSetLong(Second s, long value) {
+ Second.staticSetInstanceLongField(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: long Main.setLongThroughParam(Second, long) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+
+ public static long setLongThroughParam(Second s, long value) {
+ s.setInstanceLongFieldThroughParam(s, value);
+ return s.instanceLongField;
+ }
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (before)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK: InvokeVirtual
+
+ /// CHECK-START: float Main.setStaticFloat(Second, float) inliner (after)
+ /// CHECK-NOT: InstanceFieldSet
+ /// CHECK-NOT: StaticFieldSet
+
+ public static float setStaticFloat(Second s, float value) {
+ s.setStaticFloatField(value);
+ return s.staticFloatField;
+ }
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (before)
+ /// CHECK-DAG: <<Obj:l\d+>> NewInstance
+ // Note: The ArtMethod* (typed as int or long) is optional after sharpening.
+ /// CHECK-DAG: InvokeStaticOrDirect [<<Obj>>{{(,[ij]\d+)?}}] method_name:java.lang.Object.<init>
+
+ /// CHECK-START: java.lang.Object Main.newObject() inliner (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static Object newObject() {
+ return new Object();
+ }
+
+ public static void main(String[] args) throws Exception {
+ Second s = new Second();
+
+ // Replaced NOP pattern.
+ staticNop();
+ nop(s);
+ // Replaced "return arg" pattern.
+ assertEquals("arbitrary string", staticReturnArg2("arbitrary string"));
+ assertEquals(4321L, returnArg1(s, 4321L));
+ // Replaced "return const" pattern.
+ assertEquals(9, staticReturn9());
+ assertEquals(7, return7(s));
+ assertEquals(null, staticReturnNull());
+ assertEquals(null, returnNull(s));
+ // Replaced IGET pattern.
+ assertEquals(42, getInt(s));
+ assertEquals(-42.0, getDouble(s));
+ assertEquals(null, getObject(s));
+ assertEquals("dummy", getString(s));
+ // Not replaced IGET pattern.
+ assertEquals(42, staticGetInt(s));
+ assertEquals(-42.0, getDoubleFromParam(s));
+ // SGET.
+ assertEquals(4242, getStaticInt(s));
+ // Replaced IPUT pattern.
+ assertEquals(111L, setLong(s, 111L));
+ assertEquals(345L, setLongReturnArg2(s, 222L, 123));
+ // Not replaced IPUT pattern.
+ assertEquals(222L, staticSetLong(s, 222L));
+ assertEquals(333L, setLongThroughParam(s, 333L));
+ // SPUT.
+ assertEquals(-11.5f, setStaticFloat(s, -11.5f));
+
+ if (newObject() == null) {
+ throw new AssertionError("new Object() cannot be null.");
+ }
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void assertEquals(double expected, double actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void assertEquals(Object expected, Object actual) {
+ if (expected != actual && (expected == null || !expected.equals(actual))) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 2e66af5..73ce307 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -50,4 +50,12 @@
b/21869691
b/26143249
b/26579108
+b/26594149 (1)
+b/26594149 (2)
+b/26594149 (3)
+b/26594149 (4)
+b/26594149 (5)
+b/26594149 (6)
+b/26594149 (7)
+b/26594149 (8)
Done!
diff --git a/test/800-smali/smali/b_26594149_1.smali b/test/800-smali/smali/b_26594149_1.smali
new file mode 100644
index 0000000..c465859
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_1.smali
@@ -0,0 +1,26 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_1;
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 2
+ new-instance v0, Ljava/lang/String;
+
+ # Illegal operation.
+ instance-of v1, v0, Ljava/lang/String;
+
+ return-void
+ .end method
diff --git a/test/800-smali/smali/b_26594149_2.smali b/test/800-smali/smali/b_26594149_2.smali
new file mode 100644
index 0000000..765afe2
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_2.smali
@@ -0,0 +1,26 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_2;
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 2
+ new-instance v0, Ljava/lang/String;
+
+ # Illegal operation.
+ check-cast v0, Ljava/lang/String;
+
+ return-void
+ .end method
diff --git a/test/800-smali/smali/b_26594149_3.smali b/test/800-smali/smali/b_26594149_3.smali
new file mode 100644
index 0000000..42b5675
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_3.smali
@@ -0,0 +1,28 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_3;
+.super Ljava/lang/Object;
+
+.field public static field:Ljava/lang/String;
+
+.method public static run()V
+ .registers 2
+ new-instance v0, Ljava/lang/String;
+
+ # Illegal operation.
+ sput-object v0, LB26594149_3;->field:Ljava/lang/String;
+
+ return-void
+ .end method
diff --git a/test/800-smali/smali/b_26594149_4.smali b/test/800-smali/smali/b_26594149_4.smali
new file mode 100644
index 0000000..5b2f99b
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_4.smali
@@ -0,0 +1,38 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_4;
+.super Ljava/lang/Object;
+
+.field public field:Ljava/lang/String;
+
+.method public constructor <init>()V
+ .registers 4
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static run()V
+ .registers 4
+
+ new-instance v1, LB26594149_4;
+ invoke-direct {v1}, LB26594149_4;-><init>()V
+
+ new-instance v0, Ljava/lang/String;
+
+ # Illegal operation.
+ iput-object v0, v1, LB26594149_4;->field:Ljava/lang/String;
+
+ return-void
+ .end method
diff --git a/test/800-smali/smali/b_26594149_5.smali b/test/800-smali/smali/b_26594149_5.smali
new file mode 100644
index 0000000..27d6255
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_5.smali
@@ -0,0 +1,28 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_5;
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 4
+
+ new-instance v0, Ljava/lang/Object;
+
+ # Allowed operation on uninitialized objects.
+ monitor-enter v0
+ monitor-exit v0
+
+ return-void
+ .end method
diff --git a/test/800-smali/smali/b_26594149_6.smali b/test/800-smali/smali/b_26594149_6.smali
new file mode 100644
index 0000000..8d26ee8
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_6.smali
@@ -0,0 +1,24 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_6;
+.super Ljava/lang/Object;
+
+.method public static run()V
+ .registers 4
+
+ new-instance v0, Ljava/lang/Exception;
+ throw v0
+
+ .end method
diff --git a/test/800-smali/smali/b_26594149_7.smali b/test/800-smali/smali/b_26594149_7.smali
new file mode 100644
index 0000000..f624d1a
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_7.smali
@@ -0,0 +1,30 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_7;
+.super Ljava/lang/Object;
+
+.method private static foo(Ljava/lang/Object;)V
+ .registers 1
+ return-void
+.end method
+
+.method public static run()V
+ .registers 4
+
+ new-instance v0, Ljava/lang/Object;
+ invoke-static {v0}, LB26594149_7;->foo(Ljava/lang/Object;)V
+ return-void
+
+ .end method
diff --git a/test/800-smali/smali/b_26594149_8.smali b/test/800-smali/smali/b_26594149_8.smali
new file mode 100644
index 0000000..e366de4
--- /dev/null
+++ b/test/800-smali/smali/b_26594149_8.smali
@@ -0,0 +1,24 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LB26594149_8;
+.super Ljava/lang/Object;
+
+.method public static run()Ljava/lang/Object;
+ .registers 4
+
+ new-instance v0, Ljava/lang/Object;
+ return-object v0
+
+ .end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 38aa58d..b0eff5d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -145,6 +145,21 @@
new AbstractMethodError(), null));
testCases.add(new TestCase("b/26579108", "B26579108", "run", null, new VerifyError(),
null));
+ testCases.add(new TestCase("b/26594149 (1)", "B26594149_1", "run", null, new VerifyError(),
+ null));
+ testCases.add(new TestCase("b/26594149 (2)", "B26594149_2", "run", null, new VerifyError(),
+ null));
+ testCases.add(new TestCase("b/26594149 (3)", "B26594149_3", "run", null, new VerifyError(),
+ null));
+ testCases.add(new TestCase("b/26594149 (4)", "B26594149_4", "run", null, new VerifyError(),
+ null));
+ testCases.add(new TestCase("b/26594149 (5)", "B26594149_5", "run", null, null, null));
+ testCases.add(new TestCase("b/26594149 (6)", "B26594149_6", "run", null, new VerifyError(),
+ null));
+ testCases.add(new TestCase("b/26594149 (7)", "B26594149_7", "run", null, new VerifyError(),
+ null));
+ testCases.add(new TestCase("b/26594149 (8)", "B26594149_8", "run", null, new VerifyError(),
+ null));
}
public void runTests() {
diff --git a/test/972-default-imt-collision/expected.txt b/test/972-default-imt-collision/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/972-default-imt-collision/expected.txt
diff --git a/test/972-default-imt-collision/info.txt b/test/972-default-imt-collision/info.txt
new file mode 100644
index 0000000..adecee3
--- /dev/null
+++ b/test/972-default-imt-collision/info.txt
@@ -0,0 +1 @@
+Test for interaction of miranda and non-miranda methods on interface dispatch.
diff --git a/test/972-default-imt-collision/smali/Iface1.smali b/test/972-default-imt-collision/smali/Iface1.smali
new file mode 100644
index 0000000..088c29c
--- /dev/null
+++ b/test/972-default-imt-collision/smali/Iface1.smali
@@ -0,0 +1,32 @@
+# /*
+# * Copyright (C) 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+#
+# public interface Iface1 {
+# public default void sayHi() {
+# System.out.println("FAILED: We should never invoke this method!");
+# }
+# }
+
+.class public abstract interface LIface1;
+.super Ljava/lang/Object;
+
+.method public sayHi()V
+ .locals 2
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v0, "FAILED: We should never invoke this method!"
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
diff --git a/test/972-default-imt-collision/smali/Iface2.smali b/test/972-default-imt-collision/smali/Iface2.smali
new file mode 100644
index 0000000..4320e7e
--- /dev/null
+++ b/test/972-default-imt-collision/smali/Iface2.smali
@@ -0,0 +1,277 @@
+# /*
+# * Copyright (C) 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+#
+# public interface Iface2 {
+# public void notImplementedMethod0();
+# public void notImplementedMethod1();
+# public void notImplementedMethod2();
+# public void notImplementedMethod3();
+# public void notImplementedMethod4();
+# public void notImplementedMethod5();
+# public void notImplementedMethod6();
+# public void notImplementedMethod7();
+# public void notImplementedMethod8();
+# public void notImplementedMethod9();
+# public void notImplementedMethod10();
+# public void notImplementedMethod11();
+# public void notImplementedMethod12();
+# public void notImplementedMethod13();
+# public void notImplementedMethod14();
+# public void notImplementedMethod15();
+# public void notImplementedMethod16();
+# public void notImplementedMethod17();
+# public void notImplementedMethod18();
+# public void notImplementedMethod19();
+# public void notImplementedMethod20();
+# public void notImplementedMethod21();
+# public void notImplementedMethod22();
+# public void notImplementedMethod23();
+# public void notImplementedMethod24();
+# public void notImplementedMethod25();
+# public void notImplementedMethod26();
+# public void notImplementedMethod27();
+# public void notImplementedMethod28();
+# public void notImplementedMethod29();
+# public void notImplementedMethod30();
+# public void notImplementedMethod31();
+# public void notImplementedMethod32();
+# public void notImplementedMethod33();
+# public void notImplementedMethod34();
+# public void notImplementedMethod35();
+# public void notImplementedMethod36();
+# public void notImplementedMethod37();
+# public void notImplementedMethod38();
+# public void notImplementedMethod39();
+# public void notImplementedMethod40();
+# public void notImplementedMethod41();
+# public void notImplementedMethod42();
+# public void notImplementedMethod43();
+# public void notImplementedMethod44();
+# public void notImplementedMethod45();
+# public void notImplementedMethod46();
+# public void notImplementedMethod47();
+# public void notImplementedMethod48();
+# public void notImplementedMethod49();
+# public void notImplementedMethod50();
+# public void notImplementedMethod51();
+# public void notImplementedMethod52();
+# public void notImplementedMethod53();
+# public void notImplementedMethod54();
+# public void notImplementedMethod55();
+# public void notImplementedMethod56();
+# public void notImplementedMethod57();
+# public void notImplementedMethod58();
+# public void notImplementedMethod59();
+# public void notImplementedMethod60();
+# public void notImplementedMethod61();
+# public void notImplementedMethod62();
+# public void notImplementedMethod63();
+# }
+
+.class public abstract interface LIface2;
+.super Ljava/lang/Object;
+
+.method public abstract notImplementedMethod0()V
+.end method
+
+.method public abstract notImplementedMethod1()V
+.end method
+
+.method public abstract notImplementedMethod2()V
+.end method
+
+.method public abstract notImplementedMethod3()V
+.end method
+
+.method public abstract notImplementedMethod4()V
+.end method
+
+.method public abstract notImplementedMethod5()V
+.end method
+
+.method public abstract notImplementedMethod6()V
+.end method
+
+.method public abstract notImplementedMethod7()V
+.end method
+
+.method public abstract notImplementedMethod8()V
+.end method
+
+.method public abstract notImplementedMethod9()V
+.end method
+
+.method public abstract notImplementedMethod10()V
+.end method
+
+.method public abstract notImplementedMethod11()V
+.end method
+
+.method public abstract notImplementedMethod12()V
+.end method
+
+.method public abstract notImplementedMethod13()V
+.end method
+
+.method public abstract notImplementedMethod14()V
+.end method
+
+.method public abstract notImplementedMethod15()V
+.end method
+
+.method public abstract notImplementedMethod16()V
+.end method
+
+.method public abstract notImplementedMethod17()V
+.end method
+
+.method public abstract notImplementedMethod18()V
+.end method
+
+.method public abstract notImplementedMethod19()V
+.end method
+
+.method public abstract notImplementedMethod20()V
+.end method
+
+.method public abstract notImplementedMethod21()V
+.end method
+
+.method public abstract notImplementedMethod22()V
+.end method
+
+.method public abstract notImplementedMethod23()V
+.end method
+
+.method public abstract notImplementedMethod24()V
+.end method
+
+.method public abstract notImplementedMethod25()V
+.end method
+
+.method public abstract notImplementedMethod26()V
+.end method
+
+.method public abstract notImplementedMethod27()V
+.end method
+
+.method public abstract notImplementedMethod28()V
+.end method
+
+.method public abstract notImplementedMethod29()V
+.end method
+
+.method public abstract notImplementedMethod30()V
+.end method
+
+.method public abstract notImplementedMethod31()V
+.end method
+
+.method public abstract notImplementedMethod32()V
+.end method
+
+.method public abstract notImplementedMethod33()V
+.end method
+
+.method public abstract notImplementedMethod34()V
+.end method
+
+.method public abstract notImplementedMethod35()V
+.end method
+
+.method public abstract notImplementedMethod36()V
+.end method
+
+.method public abstract notImplementedMethod37()V
+.end method
+
+.method public abstract notImplementedMethod38()V
+.end method
+
+.method public abstract notImplementedMethod39()V
+.end method
+
+.method public abstract notImplementedMethod40()V
+.end method
+
+.method public abstract notImplementedMethod41()V
+.end method
+
+.method public abstract notImplementedMethod42()V
+.end method
+
+.method public abstract notImplementedMethod43()V
+.end method
+
+.method public abstract notImplementedMethod44()V
+.end method
+
+.method public abstract notImplementedMethod45()V
+.end method
+
+.method public abstract notImplementedMethod46()V
+.end method
+
+.method public abstract notImplementedMethod47()V
+.end method
+
+.method public abstract notImplementedMethod48()V
+.end method
+
+.method public abstract notImplementedMethod49()V
+.end method
+
+.method public abstract notImplementedMethod50()V
+.end method
+
+.method public abstract notImplementedMethod51()V
+.end method
+
+.method public abstract notImplementedMethod52()V
+.end method
+
+.method public abstract notImplementedMethod53()V
+.end method
+
+.method public abstract notImplementedMethod54()V
+.end method
+
+.method public abstract notImplementedMethod55()V
+.end method
+
+.method public abstract notImplementedMethod56()V
+.end method
+
+.method public abstract notImplementedMethod57()V
+.end method
+
+.method public abstract notImplementedMethod58()V
+.end method
+
+.method public abstract notImplementedMethod59()V
+.end method
+
+.method public abstract notImplementedMethod60()V
+.end method
+
+.method public abstract notImplementedMethod61()V
+.end method
+
+.method public abstract notImplementedMethod62()V
+.end method
+
+.method public abstract notImplementedMethod63()V
+.end method
diff --git a/test/972-default-imt-collision/smali/Klass.smali b/test/972-default-imt-collision/smali/Klass.smali
new file mode 100644
index 0000000..a98f9a7
--- /dev/null
+++ b/test/972-default-imt-collision/smali/Klass.smali
@@ -0,0 +1,993 @@
+# /*
+# * Copyright (C) 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+# public class Klass implements Iface1, Iface2 {
+# public static void testMe(Iface2 me) {
+# try {
+# me.notImplementedMethod0();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod0!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod1();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod1!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod2();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod2!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod3();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod3!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod4();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod4!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod5();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod5!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod6();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod6!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod7();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod7!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod8();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod8!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod9();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod9!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod10();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod10!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod11();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod11!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod12();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod12!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod13();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod13!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod14();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod14!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod15();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod15!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod16();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod16!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod17();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod17!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod18();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod18!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod19();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod19!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod20();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod20!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod21();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod21!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod22();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod22!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod23();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod23!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod24();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod24!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod25();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod25!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod26();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod26!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod27();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod27!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod28();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod28!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod29();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod29!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod30();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod30!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod31();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod31!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod32();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod32!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod33();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod33!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod34();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod34!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod35();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod35!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod36();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod36!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod37();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod37!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod38();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod38!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod39();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod39!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod40();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod40!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod41();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod41!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod42();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod42!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod43();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod43!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod44();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod44!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod45();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod45!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod46();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod46!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod47();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod47!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod48();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod48!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod49();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod49!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod50();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod50!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod51();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod51!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod52();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod52!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod53();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod53!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod54();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod54!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod55();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod55!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod56();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod56!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod57();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod57!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod58();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod58!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod59();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod59!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod60();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod60!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod61();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod61!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod62();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod62!");
+# } catch (AbstractMethodError e) { /* do nothing */ }
+# try {
+# me.notImplementedMethod63();
+# System.out.println("FAILED TO THROW AbstractMethodError when calling notImplementedMethod63!");
+# } catch (AbstractMethodError e) { /* do nothing */ } }
+# }
+# }
+
+.class public LKlass;
+.super Ljava/lang/Object;
+.implements LIface1;
+.implements LIface2;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static testMe(LIface2;)V
+.locals 5
+ :try_0_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod0()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod0!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_0_end
+ :try_0_end
+ .catch Ljava/lang/AbstractMethodError; {:try_0_start .. :try_0_end} :catch_0_end
+ :catch_0_end
+
+
+ :try_1_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod1()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod1!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_1_end
+ :try_1_end
+ .catch Ljava/lang/AbstractMethodError; {:try_1_start .. :try_1_end} :catch_1_end
+ :catch_1_end
+
+
+ :try_2_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod2()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod2!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_2_end
+ :try_2_end
+ .catch Ljava/lang/AbstractMethodError; {:try_2_start .. :try_2_end} :catch_2_end
+ :catch_2_end
+
+
+ :try_3_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod3()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod3!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_3_end
+ :try_3_end
+ .catch Ljava/lang/AbstractMethodError; {:try_3_start .. :try_3_end} :catch_3_end
+ :catch_3_end
+
+
+ :try_4_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod4()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod4!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_4_end
+ :try_4_end
+ .catch Ljava/lang/AbstractMethodError; {:try_4_start .. :try_4_end} :catch_4_end
+ :catch_4_end
+
+
+ :try_5_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod5()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod5!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_5_end
+ :try_5_end
+ .catch Ljava/lang/AbstractMethodError; {:try_5_start .. :try_5_end} :catch_5_end
+ :catch_5_end
+
+
+ :try_6_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod6()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod6!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_6_end
+ :try_6_end
+ .catch Ljava/lang/AbstractMethodError; {:try_6_start .. :try_6_end} :catch_6_end
+ :catch_6_end
+
+
+ :try_7_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod7()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod7!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_7_end
+ :try_7_end
+ .catch Ljava/lang/AbstractMethodError; {:try_7_start .. :try_7_end} :catch_7_end
+ :catch_7_end
+
+
+ :try_8_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod8()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod8!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_8_end
+ :try_8_end
+ .catch Ljava/lang/AbstractMethodError; {:try_8_start .. :try_8_end} :catch_8_end
+ :catch_8_end
+
+
+ :try_9_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod9()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod9!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_9_end
+ :try_9_end
+ .catch Ljava/lang/AbstractMethodError; {:try_9_start .. :try_9_end} :catch_9_end
+ :catch_9_end
+
+
+ :try_10_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod10()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod10!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_10_end
+ :try_10_end
+ .catch Ljava/lang/AbstractMethodError; {:try_10_start .. :try_10_end} :catch_10_end
+ :catch_10_end
+
+
+ :try_11_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod11()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod11!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_11_end
+ :try_11_end
+ .catch Ljava/lang/AbstractMethodError; {:try_11_start .. :try_11_end} :catch_11_end
+ :catch_11_end
+
+
+ :try_12_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod12()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod12!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_12_end
+ :try_12_end
+ .catch Ljava/lang/AbstractMethodError; {:try_12_start .. :try_12_end} :catch_12_end
+ :catch_12_end
+
+
+ :try_13_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod13()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod13!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_13_end
+ :try_13_end
+ .catch Ljava/lang/AbstractMethodError; {:try_13_start .. :try_13_end} :catch_13_end
+ :catch_13_end
+
+
+ :try_14_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod14()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod14!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_14_end
+ :try_14_end
+ .catch Ljava/lang/AbstractMethodError; {:try_14_start .. :try_14_end} :catch_14_end
+ :catch_14_end
+
+
+ :try_15_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod15()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod15!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_15_end
+ :try_15_end
+ .catch Ljava/lang/AbstractMethodError; {:try_15_start .. :try_15_end} :catch_15_end
+ :catch_15_end
+
+
+ :try_16_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod16()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod16!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_16_end
+ :try_16_end
+ .catch Ljava/lang/AbstractMethodError; {:try_16_start .. :try_16_end} :catch_16_end
+ :catch_16_end
+
+
+ :try_17_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod17()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod17!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_17_end
+ :try_17_end
+ .catch Ljava/lang/AbstractMethodError; {:try_17_start .. :try_17_end} :catch_17_end
+ :catch_17_end
+
+
+ :try_18_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod18()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod18!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_18_end
+ :try_18_end
+ .catch Ljava/lang/AbstractMethodError; {:try_18_start .. :try_18_end} :catch_18_end
+ :catch_18_end
+
+
+ :try_19_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod19()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod19!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_19_end
+ :try_19_end
+ .catch Ljava/lang/AbstractMethodError; {:try_19_start .. :try_19_end} :catch_19_end
+ :catch_19_end
+
+
+ :try_20_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod20()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod20!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_20_end
+ :try_20_end
+ .catch Ljava/lang/AbstractMethodError; {:try_20_start .. :try_20_end} :catch_20_end
+ :catch_20_end
+
+
+ :try_21_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod21()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod21!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_21_end
+ :try_21_end
+ .catch Ljava/lang/AbstractMethodError; {:try_21_start .. :try_21_end} :catch_21_end
+ :catch_21_end
+
+
+ :try_22_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod22()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod22!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_22_end
+ :try_22_end
+ .catch Ljava/lang/AbstractMethodError; {:try_22_start .. :try_22_end} :catch_22_end
+ :catch_22_end
+
+
+ :try_23_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod23()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod23!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_23_end
+ :try_23_end
+ .catch Ljava/lang/AbstractMethodError; {:try_23_start .. :try_23_end} :catch_23_end
+ :catch_23_end
+
+
+ :try_24_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod24()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod24!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_24_end
+ :try_24_end
+ .catch Ljava/lang/AbstractMethodError; {:try_24_start .. :try_24_end} :catch_24_end
+ :catch_24_end
+
+
+ :try_25_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod25()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod25!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_25_end
+ :try_25_end
+ .catch Ljava/lang/AbstractMethodError; {:try_25_start .. :try_25_end} :catch_25_end
+ :catch_25_end
+
+
+ :try_26_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod26()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod26!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_26_end
+ :try_26_end
+ .catch Ljava/lang/AbstractMethodError; {:try_26_start .. :try_26_end} :catch_26_end
+ :catch_26_end
+
+
+ :try_27_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod27()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod27!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_27_end
+ :try_27_end
+ .catch Ljava/lang/AbstractMethodError; {:try_27_start .. :try_27_end} :catch_27_end
+ :catch_27_end
+
+
+ :try_28_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod28()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod28!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_28_end
+ :try_28_end
+ .catch Ljava/lang/AbstractMethodError; {:try_28_start .. :try_28_end} :catch_28_end
+ :catch_28_end
+
+
+ :try_29_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod29()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod29!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_29_end
+ :try_29_end
+ .catch Ljava/lang/AbstractMethodError; {:try_29_start .. :try_29_end} :catch_29_end
+ :catch_29_end
+
+
+ :try_30_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod30()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod30!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_30_end
+ :try_30_end
+ .catch Ljava/lang/AbstractMethodError; {:try_30_start .. :try_30_end} :catch_30_end
+ :catch_30_end
+
+
+ :try_31_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod31()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod31!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_31_end
+ :try_31_end
+ .catch Ljava/lang/AbstractMethodError; {:try_31_start .. :try_31_end} :catch_31_end
+ :catch_31_end
+
+
+ :try_32_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod32()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod32!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_32_end
+ :try_32_end
+ .catch Ljava/lang/AbstractMethodError; {:try_32_start .. :try_32_end} :catch_32_end
+ :catch_32_end
+
+
+ :try_33_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod33()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod33!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_33_end
+ :try_33_end
+ .catch Ljava/lang/AbstractMethodError; {:try_33_start .. :try_33_end} :catch_33_end
+ :catch_33_end
+
+
+ :try_34_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod34()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod34!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_34_end
+ :try_34_end
+ .catch Ljava/lang/AbstractMethodError; {:try_34_start .. :try_34_end} :catch_34_end
+ :catch_34_end
+
+
+ :try_35_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod35()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod35!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_35_end
+ :try_35_end
+ .catch Ljava/lang/AbstractMethodError; {:try_35_start .. :try_35_end} :catch_35_end
+ :catch_35_end
+
+
+ :try_36_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod36()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod36!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_36_end
+ :try_36_end
+ .catch Ljava/lang/AbstractMethodError; {:try_36_start .. :try_36_end} :catch_36_end
+ :catch_36_end
+
+
+ :try_37_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod37()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod37!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_37_end
+ :try_37_end
+ .catch Ljava/lang/AbstractMethodError; {:try_37_start .. :try_37_end} :catch_37_end
+ :catch_37_end
+
+
+ :try_38_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod38()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod38!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_38_end
+ :try_38_end
+ .catch Ljava/lang/AbstractMethodError; {:try_38_start .. :try_38_end} :catch_38_end
+ :catch_38_end
+
+
+ :try_39_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod39()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod39!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_39_end
+ :try_39_end
+ .catch Ljava/lang/AbstractMethodError; {:try_39_start .. :try_39_end} :catch_39_end
+ :catch_39_end
+
+
+ :try_40_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod40()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod40!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_40_end
+ :try_40_end
+ .catch Ljava/lang/AbstractMethodError; {:try_40_start .. :try_40_end} :catch_40_end
+ :catch_40_end
+
+
+ :try_41_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod41()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod41!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_41_end
+ :try_41_end
+ .catch Ljava/lang/AbstractMethodError; {:try_41_start .. :try_41_end} :catch_41_end
+ :catch_41_end
+
+
+ :try_42_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod42()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod42!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_42_end
+ :try_42_end
+ .catch Ljava/lang/AbstractMethodError; {:try_42_start .. :try_42_end} :catch_42_end
+ :catch_42_end
+
+
+ :try_43_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod43()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod43!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_43_end
+ :try_43_end
+ .catch Ljava/lang/AbstractMethodError; {:try_43_start .. :try_43_end} :catch_43_end
+ :catch_43_end
+
+
+ :try_44_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod44()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod44!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_44_end
+ :try_44_end
+ .catch Ljava/lang/AbstractMethodError; {:try_44_start .. :try_44_end} :catch_44_end
+ :catch_44_end
+
+
+ :try_45_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod45()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod45!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_45_end
+ :try_45_end
+ .catch Ljava/lang/AbstractMethodError; {:try_45_start .. :try_45_end} :catch_45_end
+ :catch_45_end
+
+
+ :try_46_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod46()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod46!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_46_end
+ :try_46_end
+ .catch Ljava/lang/AbstractMethodError; {:try_46_start .. :try_46_end} :catch_46_end
+ :catch_46_end
+
+
+ :try_47_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod47()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod47!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_47_end
+ :try_47_end
+ .catch Ljava/lang/AbstractMethodError; {:try_47_start .. :try_47_end} :catch_47_end
+ :catch_47_end
+
+
+ :try_48_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod48()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod48!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_48_end
+ :try_48_end
+ .catch Ljava/lang/AbstractMethodError; {:try_48_start .. :try_48_end} :catch_48_end
+ :catch_48_end
+
+
+ :try_49_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod49()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod49!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_49_end
+ :try_49_end
+ .catch Ljava/lang/AbstractMethodError; {:try_49_start .. :try_49_end} :catch_49_end
+ :catch_49_end
+
+
+ :try_50_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod50()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod50!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_50_end
+ :try_50_end
+ .catch Ljava/lang/AbstractMethodError; {:try_50_start .. :try_50_end} :catch_50_end
+ :catch_50_end
+
+
+ :try_51_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod51()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod51!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_51_end
+ :try_51_end
+ .catch Ljava/lang/AbstractMethodError; {:try_51_start .. :try_51_end} :catch_51_end
+ :catch_51_end
+
+
+ :try_52_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod52()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod52!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_52_end
+ :try_52_end
+ .catch Ljava/lang/AbstractMethodError; {:try_52_start .. :try_52_end} :catch_52_end
+ :catch_52_end
+
+
+ :try_53_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod53()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod53!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_53_end
+ :try_53_end
+ .catch Ljava/lang/AbstractMethodError; {:try_53_start .. :try_53_end} :catch_53_end
+ :catch_53_end
+
+
+ :try_54_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod54()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod54!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_54_end
+ :try_54_end
+ .catch Ljava/lang/AbstractMethodError; {:try_54_start .. :try_54_end} :catch_54_end
+ :catch_54_end
+
+
+ :try_55_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod55()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod55!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_55_end
+ :try_55_end
+ .catch Ljava/lang/AbstractMethodError; {:try_55_start .. :try_55_end} :catch_55_end
+ :catch_55_end
+
+
+ :try_56_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod56()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod56!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_56_end
+ :try_56_end
+ .catch Ljava/lang/AbstractMethodError; {:try_56_start .. :try_56_end} :catch_56_end
+ :catch_56_end
+
+
+ :try_57_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod57()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod57!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_57_end
+ :try_57_end
+ .catch Ljava/lang/AbstractMethodError; {:try_57_start .. :try_57_end} :catch_57_end
+ :catch_57_end
+
+
+ :try_58_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod58()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod58!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_58_end
+ :try_58_end
+ .catch Ljava/lang/AbstractMethodError; {:try_58_start .. :try_58_end} :catch_58_end
+ :catch_58_end
+
+
+ :try_59_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod59()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod59!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_59_end
+ :try_59_end
+ .catch Ljava/lang/AbstractMethodError; {:try_59_start .. :try_59_end} :catch_59_end
+ :catch_59_end
+
+
+ :try_60_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod60()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod60!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_60_end
+ :try_60_end
+ .catch Ljava/lang/AbstractMethodError; {:try_60_start .. :try_60_end} :catch_60_end
+ :catch_60_end
+
+
+ :try_61_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod61()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod61!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_61_end
+ :try_61_end
+ .catch Ljava/lang/AbstractMethodError; {:try_61_start .. :try_61_end} :catch_61_end
+ :catch_61_end
+
+
+ :try_62_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod62()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod62!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_62_end
+ :try_62_end
+ .catch Ljava/lang/AbstractMethodError; {:try_62_start .. :try_62_end} :catch_62_end
+ :catch_62_end
+
+
+ :try_63_start
+ invoke-interface {p0}, LIface2;->notImplementedMethod63()V
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ const-string v1, "FAILED TO THROW AbstractMethodError when calling notImplementedMethod63!"
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ goto :catch_63_end
+ :try_63_end
+ .catch Ljava/lang/AbstractMethodError; {:try_63_start .. :try_63_end} :catch_63_end
+ :catch_63_end
+ return-void
+.end method
diff --git a/test/972-default-imt-collision/src/Main.java b/test/972-default-imt-collision/src/Main.java
new file mode 100644
index 0000000..6819e43
--- /dev/null
+++ b/test/972-default-imt-collision/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.lang.reflect.*;
+
+public class Main {
+ public static void main(String[] args) {
+ try {
+ Class<?> c = Class.forName("Klass");
+ Object o = c.newInstance();
+ Class<?> iface = Class.forName("Iface2");
+ Method test = c.getMethod("testMe", iface);
+ test.invoke(null, o);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("FAILED: could not run testMe!");
+ }
+ }
+}
diff --git a/test/972-iface-super-multidex/expected.txt b/test/972-iface-super-multidex/expected.txt
new file mode 100644
index 0000000..a9d31a5
--- /dev/null
+++ b/test/972-iface-super-multidex/expected.txt
@@ -0,0 +1,2 @@
+SuperInterface default method called
+Expected ICCE caught
diff --git a/test/972-iface-super-multidex/info.txt b/test/972-iface-super-multidex/info.txt
new file mode 100644
index 0000000..f7948ad
--- /dev/null
+++ b/test/972-iface-super-multidex/info.txt
@@ -0,0 +1,3 @@
+Smali-based tests for experimental interface default methods.
+
+Obviously needs to run under ART or a Java 8 Language runtime and compiler.
diff --git a/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali b/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali
new file mode 100644
index 0000000..2c76213
--- /dev/null
+++ b/test/972-iface-super-multidex/smali-multidex/conflictinterface.smali
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.class public abstract interface LConflictInterface;
+.super Ljava/lang/Object;
+.implements LOneConflict;
+.implements LTwoConflict;
+
+# public interface ConflictInterface extends OneConflict, TwoConflict {
+# }
diff --git a/test/972-iface-super-multidex/smali-multidex/oneconflict.smali b/test/972-iface-super-multidex/smali-multidex/oneconflict.smali
new file mode 100644
index 0000000..7001f02
--- /dev/null
+++ b/test/972-iface-super-multidex/smali-multidex/oneconflict.smali
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.class public abstract interface LOneConflict;
+.super Ljava/lang/Object;
+
+# public interface OneConflict {
+# public String runDefault() {
+# return "OneConflict default method called";
+# }
+# }
+
+.method public runDefault()Ljava/lang/String;
+.registers 2
+ # Do an invoke super on this class, to confuse runtime/compiler.
+ const-string v0, "OneConflict default method called"
+ return-object v0
+.end method
diff --git a/test/972-iface-super-multidex/smali-multidex/superinterface.smali b/test/972-iface-super-multidex/smali-multidex/superinterface.smali
new file mode 100644
index 0000000..d45ecea
--- /dev/null
+++ b/test/972-iface-super-multidex/smali-multidex/superinterface.smali
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.class public abstract interface LSuperInterface;
+.super Ljava/lang/Object;
+
+# public interface SuperInterface {
+# public String runDefault() {
+# return "SuperInterface default method called";
+# }
+# }
+
+.method public runDefault()Ljava/lang/String;
+.registers 2
+ # Do an invoke super on this class, to confuse runtime/compiler.
+ const-string v0, "SuperInterface default method called"
+ return-object v0
+.end method
diff --git a/test/972-iface-super-multidex/smali-multidex/twoconflict.smali b/test/972-iface-super-multidex/smali-multidex/twoconflict.smali
new file mode 100644
index 0000000..b971b74
--- /dev/null
+++ b/test/972-iface-super-multidex/smali-multidex/twoconflict.smali
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.class public abstract interface LTwoConflict;
+.super Ljava/lang/Object;
+
+# public interface TwoConflict {
+# public String runDefault() {
+# return "TwoConflict default method called";
+# }
+# }
+
+.method public runDefault()Ljava/lang/String;
+.registers 2
+ # Do an invoke super on this class, to confuse runtime/compiler.
+ const-string v0, "TwoConflict default method called"
+ return-object v0
+.end method
diff --git a/test/972-iface-super-multidex/smali/concreteclass.smali b/test/972-iface-super-multidex/smali/concreteclass.smali
new file mode 100644
index 0000000..703da94
--- /dev/null
+++ b/test/972-iface-super-multidex/smali/concreteclass.smali
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.class public LConcreteClass;
+.super Ljava/lang/Object;
+.implements LSuperInterface;
+.implements LConflictInterface;
+
+# public class ConcreteClass implements SuperInterface, ConflictInterface {
+# public String runReal() {
+# return SuperInterface.super.runDefault();
+# }
+# public String runConflict() {
+# return ConflictInterface.super.runDefault();
+# }
+# public String runDefault() {
+# return "This is the wrong class to invoke";
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public runConflict()Ljava/lang/String;
+.registers 2
+ # Do an invoke super on this class, to confuse runtime/compiler.
+ invoke-super {p0}, LConflictInterface;->runDefault()Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
+
+
+
+.method public runReal()Ljava/lang/String;
+.registers 2
+ # Do an invoke super on this class, to confuse runtime/compiler.
+ invoke-super {p0}, LSuperInterface;->runDefault()Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
+
+.method public runDefault()Ljava/lang/String;
+.registers 2
+ const-string v0, "This is the wrong class to invoke!"
+ return-object v0
+.end method
diff --git a/test/972-iface-super-multidex/src/Main.java b/test/972-iface-super-multidex/src/Main.java
new file mode 100644
index 0000000..3fb3f45
--- /dev/null
+++ b/test/972-iface-super-multidex/src/Main.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+public class Main {
+ public static void main(String[] args) {
+ Class<?> c = null;
+ try {
+ c = Class.forName("ConcreteClass");
+ } catch (Exception e) {
+ System.out.println("Could not load class");
+ e.printStackTrace();
+ return;
+ }
+ try {
+ Method m = c.getMethod("runReal");
+ System.out.println((String)m.invoke(c.newInstance(), new Object[0]));
+ } catch (Exception e) {
+ System.out.println("Unknown exception occurred");
+ e.printStackTrace();
+ }
+ try {
+ Method m = c.getMethod("runConflict");
+ try {
+ System.out.println((String)m.invoke(c.newInstance(), new Object[0]));
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ } catch (AbstractMethodError e) {
+ System.out.println("Unexpected AME caught");
+ e.printStackTrace();
+ } catch (NoSuchMethodError e) {
+ System.out.println("Unexpected NSME caught");
+ e.printStackTrace();
+ } catch (IncompatibleClassChangeError e) {
+ System.out.println("Expected ICCE caught");
+ } catch (Throwable e) {
+ System.out.println("Unknown exception caught!");
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/test/973-default-multidex/expected.txt b/test/973-default-multidex/expected.txt
new file mode 100644
index 0000000..b376e81
--- /dev/null
+++ b/test/973-default-multidex/expected.txt
@@ -0,0 +1 @@
+STRING!!!STRING!!!
diff --git a/test/973-default-multidex/info.txt b/test/973-default-multidex/info.txt
new file mode 100644
index 0000000..17c0b7d
--- /dev/null
+++ b/test/973-default-multidex/info.txt
@@ -0,0 +1,5 @@
+Smali-based tests for interface default methods.
+
+Obviously needs to run under ART or a Java 8 Language runtime and compiler.
+
+Tests that we handle referenced throws across dex files.
diff --git a/test/973-default-multidex/smali-multidex/iface.smali b/test/973-default-multidex/smali-multidex/iface.smali
new file mode 100644
index 0000000..fa6d27f
--- /dev/null
+++ b/test/973-default-multidex/smali-multidex/iface.smali
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.class public abstract interface LIface;
+.super Ljava/lang/Object;
+
+# public interface Iface {
+# public default String getTwice() {
+# return getString() + getString();
+# }
+# public String getString();
+# }
+
+.method public getTwice()Ljava/lang/String;
+.locals 2
+ invoke-static {p0}, Ljava/util/Objects;->requireNonNull(Ljava/lang/Object;)Ljava/lang/Object;
+ invoke-interface {p0}, LIface;->getString()Ljava/lang/String;
+ move-result-object v0
+ invoke-interface {p0}, LIface;->getString()Ljava/lang/String;
+ move-result-object v1
+ invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
+
+.method public abstract getString()Ljava/lang/String;
+.end method
diff --git a/test/973-default-multidex/smali/concreteclass.smali b/test/973-default-multidex/smali/concreteclass.smali
new file mode 100644
index 0000000..e177f26
--- /dev/null
+++ b/test/973-default-multidex/smali/concreteclass.smali
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+.class public LConcreteClass;
+.super Ljava/lang/Object;
+.implements LIface;
+
+# public class ConcreteClass implements Iface {
+# public String getString() {
+# return "STRING!!!";
+# }
+# public String callMethod() {
+# return this.getTwice();
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public getString()Ljava/lang/String;
+.registers 2
+ const-string v0, "STRING!!!"
+ return-object v0
+.end method
+
+.method public callMethod()Ljava/lang/String;
+.registers 2
+ invoke-virtual {p0}, LConcreteClass;->getTwice()Ljava/lang/String;
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/973-default-multidex/src/Main.java b/test/973-default-multidex/src/Main.java
new file mode 100644
index 0000000..b93265a
--- /dev/null
+++ b/test/973-default-multidex/src/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+public class Main {
+ public static void main(String[] args) {
+ Class<?> c = null;
+ try {
+ c = Class.forName("ConcreteClass");
+ Method m = c.getMethod("callMethod");
+ System.out.println(m.invoke(c.newInstance(), new Object[0]));
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.out.println("FAILED: Could not call method");
+ return;
+ }
+ }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index b922b45..faaf1f0 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -39,7 +39,8 @@
461-get-reference-vreg/get_reference_vreg_jni.cc \
466-get-live-vreg/get_live_vreg_jni.cc \
497-inlining-and-class-loader/clear_dex_cache.cc \
- 543-env-long-ref/env_long_ref.cc
+ 543-env-long-ref/env_long_ref.cc \
+ 566-polymorphic-inlining/polymorphic_inline.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttestd.so
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 429af6a..7400248 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -526,11 +526,7 @@
TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS :=
# Tests that should fail in the read barrier configuration with the interpreter.
-# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154).
-# 141: Occasional failures: "Aborted" (b/25866001).
-TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := \
- 004-ThreadStress \
- 141-class-unload
+TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS :=
# Tests that should fail in the read barrier configuration with the default (Quick) compiler (AOT).
# Quick has no support for read barriers and punts to the interpreter, so this list is composed of
@@ -540,7 +536,6 @@
$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS)
# Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT).
-# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154).
# 484: Baker's fast path based read barrier compiler instrumentation generates code containing
# more parallel moves on x86, thus some Checker assertions may fail.
# 527: On ARM64, the read barrier instrumentation does not support the HArm64IntermediateAddress
@@ -548,19 +543,12 @@
# 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are
# not yet handled in the read barrier configuration.
TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
- 004-ThreadStress \
484-checker-register-hints \
527-checker-array-access-split \
537-checker-arraycopy
-# Tests that should fail in the read barrier configuration with JIT.
-# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154).
-# 141: Disabled because of intermittent failures on the ART Builtbot (b/25866001).
-# 496: Occasional timeout: "Fault message: timeout: the monitored command dumped core" (b/26786304).
-TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \
- 004-ThreadStress \
- 141-class-unload \
- 496-checker-inlining-and-class-loader
+# Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).
+TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
ifeq ($(ART_USE_READ_BARRIER),true)
ifneq (,$(filter interpreter,$(COMPILER_TYPES)))
diff --git a/test/etc/default-build b/test/etc/default-build
index 7242428..6e855ec 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -42,6 +42,12 @@
HAS_SRC_MULTIDEX=false
fi
+if [ -d smali-multidex ]; then
+ HAS_SMALI_MULTIDEX=true
+else
+ HAS_SMALI_MULTIDEX=false
+fi
+
if [ -d src-ex ]; then
HAS_SRC_EX=true
else
@@ -74,6 +80,9 @@
elif [ "x$1" = "x--no-src-multidex" ]; then
HAS_SRC_MULTIDEX=false
shift
+ elif [ "x$1" = "x--no-smali-multidex" ]; then
+ HAS_SMALI_MULTIDEX=false
+ shift
elif [ "x$1" = "x--no-src-ex" ]; then
HAS_SRC_EX=false
shift
@@ -171,6 +180,19 @@
fi
fi
+if [ "${HAS_SMALI_MULTIDEX}" = "true" ]; then
+ # Compile Smali classes
+ ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'`
+
+ # Don't bother with dexmerger if we provide our own main function in a smali file.
+ if [ ${HAS_SRC_MULTIDEX} = "true" ]; then
+ ${DXMERGER} classes2.dex classes2.dex smali_classes2.dex
+ else
+ mv smali_classes2.dex classes2.dex
+ fi
+fi
+
+
if [ ${HAS_SRC_EX} = "true" ]; then
if [ ${USE_JACK} = "true" ]; then
# Rename previous "classes.dex" so it is not overwritten.
@@ -198,7 +220,7 @@
fi
# Create a single jar with two dex files for multidex.
-if [ ${HAS_SRC_MULTIDEX} = "true" ]; then
+if [ ${HAS_SRC_MULTIDEX} = "true" ] || [ ${HAS_SMALI_MULTIDEX} = "true" ]; then
zip $TEST_NAME.jar classes.dex classes2.dex
elif [ ${NEED_DEX} = "true" ]; then
zip $TEST_NAME.jar classes.dex