Merge "Add a script to run jdwp tests on the chrome buildbot."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 948c756..91998fa 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -167,6 +167,7 @@
runtime/mirror/object_test.cc \
runtime/monitor_pool_test.cc \
runtime/monitor_test.cc \
+ runtime/oat_file_test.cc \
runtime/oat_file_assistant_test.cc \
runtime/parsed_options_test.cc \
runtime/reference_table_test.cc \
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 4a35e9f..8babc28 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -39,6 +39,22 @@
return soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader());
}
+inline mirror::Class* CompilerDriver::ResolveClass(
+ const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader, uint16_t cls_index,
+ const DexCompilationUnit* mUnit) {
+ DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
+ DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
+ mirror::Class* cls = mUnit->GetClassLinker()->ResolveType(
+ *mUnit->GetDexFile(), cls_index, dex_cache, class_loader);
+ DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending());
+ if (UNLIKELY(cls == nullptr)) {
+ // Clean up any exception left by type resolution.
+ soa.Self()->ClearException();
+ }
+ return cls;
+}
+
inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass(
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) {
@@ -46,14 +62,7 @@
DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
const DexFile::MethodId& referrer_method_id =
mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex());
- mirror::Class* referrer_class = mUnit->GetClassLinker()->ResolveType(
- *mUnit->GetDexFile(), referrer_method_id.class_idx_, dex_cache, class_loader);
- DCHECK_EQ(referrer_class == nullptr, soa.Self()->IsExceptionPending());
- if (UNLIKELY(referrer_class == nullptr)) {
- // Clean up any exception left by type resolution.
- soa.Self()->ClearException();
- }
- return referrer_class;
+ return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit);
}
inline mirror::ArtField* CompilerDriver::ResolveFieldWithDexFile(
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 9463c2c..b825293 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -228,6 +228,12 @@
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Class* ResolveClass(
+ const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader, uint16_t type_index,
+ const DexCompilationUnit* mUnit)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Resolve a field. Returns nullptr on failure, including incompatible class change.
// NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static.
mirror::ArtField* ResolveField(
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index d238b2c..c1555aa 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -133,7 +133,7 @@
return false;
}
std::string error_msg;
- oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
+ oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, nullptr, &error_msg);
if (oat_file_ == nullptr) {
PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
<< ": " << error_msg;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 46aed60..c426625 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -122,7 +122,7 @@
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
- nullptr, false, &error_msg));
+ nullptr, false, nullptr, &error_msg));
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index 0ecc0d7..e9ca042 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -65,10 +65,10 @@
} else if (cond->IsIntConstant()) {
HIntConstant* int_const = cond->AsIntConstant();
if (int_const->IsZero()) {
- return graph->GetIntConstant1();
+ return graph->GetIntConstant(1);
} else {
DCHECK(int_const->IsOne());
- return graph->GetIntConstant0();
+ return graph->GetIntConstant(0);
}
}
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 24fa583..b3653fe 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -52,12 +52,11 @@
HParameterValue(0, Primitive::kPrimNot); // array
HInstruction* parameter2 = new (&allocator)
HParameterValue(0, Primitive::kPrimInt); // i
- HInstruction* constant_1 = new (&allocator) HIntConstant(1);
- HInstruction* constant_0 = new (&allocator) HIntConstant(0);
entry->AddInstruction(parameter1);
entry->AddInstruction(parameter2);
- entry->AddInstruction(constant_1);
- entry->AddInstruction(constant_0);
+
+ HInstruction* constant_1 = graph->GetIntConstant(1);
+ HInstruction* constant_0 = graph->GetIntConstant(0);
HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
graph->AddBlock(block1);
@@ -158,14 +157,12 @@
HParameterValue(0, Primitive::kPrimNot); // array
HInstruction* parameter2 = new (&allocator)
HParameterValue(0, Primitive::kPrimInt); // i
- HInstruction* constant_1 = new (&allocator) HIntConstant(1);
- HInstruction* constant_0 = new (&allocator) HIntConstant(0);
- HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX);
entry->AddInstruction(parameter1);
entry->AddInstruction(parameter2);
- entry->AddInstruction(constant_1);
- entry->AddInstruction(constant_0);
- entry->AddInstruction(constant_max_int);
+
+ HInstruction* constant_1 = graph->GetIntConstant(1);
+ HInstruction* constant_0 = graph->GetIntConstant(0);
+ HInstruction* constant_max_int = graph->GetIntConstant(INT_MAX);
HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
graph->AddBlock(block1);
@@ -232,14 +229,12 @@
HParameterValue(0, Primitive::kPrimNot); // array
HInstruction* parameter2 = new (&allocator)
HParameterValue(0, Primitive::kPrimInt); // i
- HInstruction* constant_1 = new (&allocator) HIntConstant(1);
- HInstruction* constant_0 = new (&allocator) HIntConstant(0);
- HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX);
entry->AddInstruction(parameter1);
entry->AddInstruction(parameter2);
- entry->AddInstruction(constant_1);
- entry->AddInstruction(constant_0);
- entry->AddInstruction(constant_max_int);
+
+ HInstruction* constant_1 = graph->GetIntConstant(1);
+ HInstruction* constant_0 = graph->GetIntConstant(0);
+ HInstruction* constant_max_int = graph->GetIntConstant(INT_MAX);
HBasicBlock* block1 = new (&allocator) HBasicBlock(graph);
graph->AddBlock(block1);
@@ -303,15 +298,12 @@
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
- HInstruction* constant_5 = new (&allocator) HIntConstant(5);
- HInstruction* constant_4 = new (&allocator) HIntConstant(4);
- HInstruction* constant_6 = new (&allocator) HIntConstant(6);
- HInstruction* constant_1 = new (&allocator) HIntConstant(1);
entry->AddInstruction(parameter);
- entry->AddInstruction(constant_5);
- entry->AddInstruction(constant_4);
- entry->AddInstruction(constant_6);
- entry->AddInstruction(constant_1);
+
+ HInstruction* constant_5 = graph->GetIntConstant(5);
+ HInstruction* constant_4 = graph->GetIntConstant(4);
+ HInstruction* constant_6 = graph->GetIntConstant(6);
+ HInstruction* constant_1 = graph->GetIntConstant(1);
HBasicBlock* block = new (&allocator) HBasicBlock(graph);
graph->AddBlock(block);
@@ -379,13 +371,11 @@
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
- HInstruction* constant_initial = new (allocator) HIntConstant(initial);
- HInstruction* constant_increment = new (allocator) HIntConstant(increment);
- HInstruction* constant_10 = new (allocator) HIntConstant(10);
entry->AddInstruction(parameter);
- entry->AddInstruction(constant_initial);
- entry->AddInstruction(constant_increment);
- entry->AddInstruction(constant_10);
+
+ HInstruction* constant_initial = graph->GetIntConstant(initial);
+ HInstruction* constant_increment = graph->GetIntConstant(increment);
+ HInstruction* constant_10 = graph->GetIntConstant(10);
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
@@ -518,15 +508,12 @@
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
- HInstruction* constant_initial = new (allocator) HIntConstant(initial);
- HInstruction* constant_increment = new (allocator) HIntConstant(increment);
- HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1);
- HInstruction* constant_10 = new (allocator) HIntConstant(10);
entry->AddInstruction(parameter);
- entry->AddInstruction(constant_initial);
- entry->AddInstruction(constant_increment);
- entry->AddInstruction(constant_minus_1);
- entry->AddInstruction(constant_10);
+
+ HInstruction* constant_initial = graph->GetIntConstant(initial);
+ HInstruction* constant_increment = graph->GetIntConstant(increment);
+ HInstruction* constant_minus_1 = graph->GetIntConstant(-1);
+ HInstruction* constant_10 = graph->GetIntConstant(10);
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
@@ -651,12 +638,10 @@
HBasicBlock* entry = new (allocator) HBasicBlock(graph);
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
- HInstruction* constant_10 = new (allocator) HIntConstant(10);
- HInstruction* constant_initial = new (allocator) HIntConstant(initial);
- HInstruction* constant_increment = new (allocator) HIntConstant(increment);
- entry->AddInstruction(constant_10);
- entry->AddInstruction(constant_initial);
- entry->AddInstruction(constant_increment);
+
+ HInstruction* constant_10 = graph->GetIntConstant(10);
+ HInstruction* constant_initial = graph->GetIntConstant(initial);
+ HInstruction* constant_increment = graph->GetIntConstant(increment);
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
@@ -765,15 +750,12 @@
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
- HInstruction* constant_initial = new (allocator) HIntConstant(initial);
- HInstruction* constant_1 = new (allocator) HIntConstant(1);
- HInstruction* constant_10 = new (allocator) HIntConstant(10);
- HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1);
entry->AddInstruction(parameter);
- entry->AddInstruction(constant_initial);
- entry->AddInstruction(constant_1);
- entry->AddInstruction(constant_10);
- entry->AddInstruction(constant_minus_1);
+
+ HInstruction* constant_initial = graph->GetIntConstant(initial);
+ HInstruction* constant_1 = graph->GetIntConstant(1);
+ HInstruction* constant_10 = graph->GetIntConstant(10);
+ HInstruction* constant_minus_1 = graph->GetIntConstant(-1);
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
@@ -893,13 +875,11 @@
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot);
- HInstruction* constant_0 = new (&allocator) HIntConstant(0);
- HInstruction* constant_minus_1 = new (&allocator) HIntConstant(-1);
- HInstruction* constant_1 = new (&allocator) HIntConstant(1);
entry->AddInstruction(parameter);
- entry->AddInstruction(constant_0);
- entry->AddInstruction(constant_minus_1);
- entry->AddInstruction(constant_1);
+
+ HInstruction* constant_0 = graph->GetIntConstant(0);
+ HInstruction* constant_minus_1 = graph->GetIntConstant(-1);
+ HInstruction* constant_1 = graph->GetIntConstant(1);
HBasicBlock* block = new (&allocator) HBasicBlock(graph);
graph->AddBlock(block);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index a21c311..2cdd5af 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -215,7 +215,7 @@
DCHECK(fallthrough_target != nullptr);
PotentiallyAddSuspendCheck(branch_target, dex_pc);
HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt);
- T* comparison = new (arena_) T(value, GetIntConstant(0));
+ T* comparison = new (arena_) T(value, graph_->GetIntConstant(0));
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison);
current_block_->AddInstruction(ifinst);
@@ -515,7 +515,7 @@
template<typename T>
void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse) {
HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- HInstruction* second = GetIntConstant(instruction.VRegC_22s());
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s());
if (reverse) {
std::swap(first, second);
}
@@ -526,7 +526,7 @@
template<typename T>
void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse) {
HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt);
- HInstruction* second = GetIntConstant(instruction.VRegC_22b());
+ HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b());
if (reverse) {
std::swap(first, second);
}
@@ -616,11 +616,11 @@
DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
|| compiler_driver_->GetCompilerOptions().GetCompilePic());
bool is_recursive =
- (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex());
- DCHECK(!is_recursive || (target_method.dex_file == outer_compilation_unit_->GetDexFile()));
+ (target_method.dex_method_index == dex_compilation_unit_->GetDexMethodIndex());
+ DCHECK(!is_recursive || (target_method.dex_file == dex_compilation_unit_->GetDexFile()));
invoke = new (arena_) HInvokeStaticOrDirect(
arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
- is_recursive, optimized_invoke_type);
+ is_recursive, invoke_type, optimized_invoke_type);
}
size_t start_index = 0;
@@ -704,6 +704,34 @@
return true;
}
+mirror::Class* HGraphBuilder::GetOutermostCompilingClass() const {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file)));
+
+ return compiler_driver_->ResolveCompilingMethodsClass(
+ soa, outer_dex_cache, class_loader, outer_compilation_unit_);
+}
+
+bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+ dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+ soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
+ soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
+ Handle<mirror::Class> compiling_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+ return compiling_class.Get() == cls.Get();
+}
+
+
bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
uint32_t dex_pc,
bool is_put) {
@@ -711,7 +739,7 @@
uint16_t field_index = instruction.VRegB_21c();
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<4> hs(soa.Self());
+ StackHandleScope<5> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(
dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
@@ -724,23 +752,36 @@
return false;
}
- Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass(
- soa, dex_cache, class_loader, outer_compilation_unit_)));
+ const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+ Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+ outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file)));
+ Handle<mirror::Class> referrer_class(hs.NewHandle(GetOutermostCompilingClass()));
// The index at which the field's class is stored in the DexCache's type array.
uint32_t storage_index;
- std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
- dex_cache.Get(), referrer_class.Get(), resolved_field.Get(), field_index, &storage_index);
- bool can_easily_access = is_put ? pair.second : pair.first;
- if (!can_easily_access) {
+ bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
+ if (is_referrer_class) {
+ storage_index = referrer_class->GetDexTypeIndex();
+ } else if (outer_dex_cache.Get() != dex_cache.Get()) {
+ // The compiler driver cannot currently understand multple dex caches involved. Just bailout.
return false;
+ } else {
+ std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
+ outer_dex_cache.Get(),
+ referrer_class.Get(),
+ resolved_field.Get(),
+ field_index,
+ &storage_index);
+ bool can_easily_access = is_put ? pair.second : pair.first;
+ if (!can_easily_access) {
+ return false;
+ }
}
// TODO: find out why this check is needed.
bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
*outer_compilation_unit_->GetDexFile(), storage_index);
bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
- bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
HLoadClass* constant = new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
current_block_->AddInstruction(constant);
@@ -783,9 +824,9 @@
HInstruction* second = nullptr;
if (second_is_constant) {
if (type == Primitive::kPrimInt) {
- second = GetIntConstant(second_vreg_or_constant);
+ second = graph_->GetIntConstant(second_vreg_or_constant);
} else {
- second = GetLongConstant(second_vreg_or_constant);
+ second = graph_->GetLongConstant(second_vreg_or_constant);
}
} else {
second = LoadLocal(second_vreg_or_constant, type);
@@ -849,7 +890,7 @@
bool is_range,
uint32_t* args,
uint32_t register_index) {
- HInstruction* length = GetIntConstant(number_of_vreg_arguments);
+ HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments);
QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index)
? kQuickAllocArrayWithAccessCheck
: kQuickAllocArray;
@@ -869,7 +910,7 @@
temps.Add(object);
for (size_t i = 0; i < number_of_vreg_arguments; ++i) {
HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type);
- HInstruction* index = GetIntConstant(i);
+ HInstruction* index = graph_->GetIntConstant(i);
current_block_->AddInstruction(
new (arena_) HArraySet(object, index, value, type, dex_pc));
}
@@ -883,8 +924,8 @@
Primitive::Type anticipated_type,
uint32_t dex_pc) {
for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = GetIntConstant(i);
- HInstruction* value = GetIntConstant(data[i]);
+ HInstruction* index = graph_->GetIntConstant(i);
+ HInstruction* value = graph_->GetIntConstant(data[i]);
current_block_->AddInstruction(new (arena_) HArraySet(
object, index, value, anticipated_type, dex_pc));
}
@@ -908,7 +949,7 @@
// Implementation of this DEX instruction seems to be that the bounds check is
// done before doing any stores.
- HInstruction* last_index = GetIntConstant(payload->element_count - 1);
+ HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1);
current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc));
switch (payload->element_width) {
@@ -949,8 +990,8 @@
uint32_t element_count,
uint32_t dex_pc) {
for (uint32_t i = 0; i < element_count; ++i) {
- HInstruction* index = GetIntConstant(i);
- HInstruction* value = GetLongConstant(data[i]);
+ HInstruction* index = graph_->GetIntConstant(i);
+ HInstruction* value = graph_->GetLongConstant(data[i]);
current_block_->AddInstruction(new (arena_) HArraySet(
object, index, value, Primitive::kPrimLong, dex_pc));
}
@@ -966,7 +1007,7 @@
// `CanAccessTypeWithoutChecks` will tell whether the method being
// built is trying to access its own class, so that the generated
// code can optimize for this case. However, the optimization does not
- // work for inlining, so we use `IsCompilingClass` instead.
+ // work for inlining, so we use `IsOutermostCompilingClass` instead.
bool dont_use_is_referrers_class;
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
@@ -976,7 +1017,8 @@
return false;
}
HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
- HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc);
+ HLoadClass* cls = new (arena_) HLoadClass(
+ type_index, IsOutermostCompilingClass(type_index), dex_pc);
current_block_->AddInstruction(cls);
// The class needs a temporary before being used by the type check.
Temporaries temps(graph_);
@@ -1040,7 +1082,7 @@
PotentiallyAddSuspendCheck(case_target, dex_pc);
// The current case's value.
- HInstruction* this_case_value = GetIntConstant(case_value_int);
+ HInstruction* this_case_value = graph_->GetIntConstant(case_value_int);
// Compare value and this_case_value.
HEqual* comparison = new (arena_) HEqual(value, this_case_value);
@@ -1098,28 +1140,28 @@
switch (instruction.Opcode()) {
case Instruction::CONST_4: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = GetIntConstant(instruction.VRegB_11n());
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n());
UpdateLocal(register_index, constant);
break;
}
case Instruction::CONST_16: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = GetIntConstant(instruction.VRegB_21s());
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s());
UpdateLocal(register_index, constant);
break;
}
case Instruction::CONST: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = GetIntConstant(instruction.VRegB_31i());
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i());
UpdateLocal(register_index, constant);
break;
}
case Instruction::CONST_HIGH16: {
int32_t register_index = instruction.VRegA();
- HIntConstant* constant = GetIntConstant(instruction.VRegB_21h() << 16);
+ HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16);
UpdateLocal(register_index, constant);
break;
}
@@ -1130,7 +1172,7 @@
int64_t value = instruction.VRegB_21s();
value <<= 48;
value >>= 48;
- HLongConstant* constant = GetLongConstant(value);
+ HLongConstant* constant = graph_->GetLongConstant(value);
UpdateLocal(register_index, constant);
break;
}
@@ -1141,14 +1183,14 @@
int64_t value = instruction.VRegB_31i();
value <<= 32;
value >>= 32;
- HLongConstant* constant = GetLongConstant(value);
+ HLongConstant* constant = graph_->GetLongConstant(value);
UpdateLocal(register_index, constant);
break;
}
case Instruction::CONST_WIDE: {
int32_t register_index = instruction.VRegA();
- HLongConstant* constant = GetLongConstant(instruction.VRegB_51l());
+ HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l());
UpdateLocal(register_index, constant);
break;
}
@@ -1156,7 +1198,7 @@
case Instruction::CONST_WIDE_HIGH16: {
int32_t register_index = instruction.VRegA();
int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48;
- HLongConstant* constant = GetLongConstant(value);
+ HLongConstant* constant = graph_->GetLongConstant(value);
UpdateLocal(register_index, constant);
break;
}
@@ -1971,7 +2013,7 @@
// `CanAccessTypeWithoutChecks` will tell whether the method being
// built is trying to access its own class, so that the generated
// code can optimize for this case. However, the optimization does not
- // work for inlining, so we use `IsCompilingClass` instead.
+ // work for inlining, so we use `IsOutermostCompilingClass` instead.
bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
&type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
@@ -1980,7 +2022,7 @@
return false;
}
current_block_->AddInstruction(
- new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc));
+ new (arena_) HLoadClass(type_index, IsOutermostCompilingClass(type_index), dex_pc));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
@@ -2058,24 +2100,6 @@
return true;
} // NOLINT(readability/fn_size)
-HIntConstant* HGraphBuilder::GetIntConstant(int32_t constant) {
- switch (constant) {
- case 0: return graph_->GetIntConstant0();
- case 1: return graph_->GetIntConstant1();
- default: {
- HIntConstant* instruction = new (arena_) HIntConstant(constant);
- graph_->AddConstant(instruction);
- return instruction;
- }
- }
-}
-
-HLongConstant* HGraphBuilder::GetLongConstant(int64_t constant) {
- HLongConstant* instruction = new (arena_) HLongConstant(constant);
- graph_->AddConstant(instruction);
- return instruction;
-}
-
HLocal* HGraphBuilder::GetLocalAt(int register_index) const {
return locals_.Get(register_index);
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index c70170b..6a0738a 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -96,8 +96,6 @@
void MaybeUpdateCurrentBlock(size_t index);
HBasicBlock* FindBlockStartingAt(int32_t index) const;
- HIntConstant* GetIntConstant(int32_t constant);
- HLongConstant* GetLongConstant(int64_t constant);
void InitializeLocals(uint16_t count);
HLocal* GetLocalAt(int register_index) const;
void UpdateLocal(int register_index, HInstruction* instruction) const;
@@ -225,13 +223,10 @@
void MaybeRecordStat(MethodCompilationStat compilation_stat);
+ mirror::Class* GetOutermostCompilingClass() const;
+
// Returns whether `type_index` points to the outer-most compiling method's class.
- bool IsCompilingClass(uint16_t type_index) const {
- uint32_t referrer_index = outer_compilation_unit_->GetDexMethodIndex();
- const DexFile::MethodId& method_id =
- outer_compilation_unit_->GetDexFile()->GetMethodId(referrer_index);
- return method_id.class_idx_ == type_index;
- }
+ bool IsOutermostCompilingClass(uint16_t type_index) const;
ArenaAllocator* const arena_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 5a79a69..97c470b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2326,10 +2326,8 @@
void LocationsBuilderARM::HandleShift(HBinaryOperation* op) {
DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
- LocationSummary::CallKind call_kind = op->GetResultType() == Primitive::kPrimLong
- ? LocationSummary::kCall
- : LocationSummary::kNoCall;
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(op, call_kind);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
switch (op->GetResultType()) {
case Primitive::kPrimInt: {
@@ -2339,12 +2337,10 @@
break;
}
case Primitive::kPrimLong: {
- InvokeRuntimeCallingConvention calling_convention;
- locations->SetInAt(0, Location::RegisterPairLocation(
- calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
- // The runtime helper puts the output in R0,R1.
- locations->SetOut(Location::RegisterPairLocation(R0, R1));
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
break;
}
default:
@@ -2392,24 +2388,56 @@
break;
}
case Primitive::kPrimLong: {
- // TODO: Inline the assembly instead of calling the runtime.
- InvokeRuntimeCallingConvention calling_convention;
- DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
- DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegister<Register>());
- DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
- DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
+ Register o_h = out.AsRegisterPairHigh<Register>();
+ Register o_l = out.AsRegisterPairLow<Register>();
- int32_t entry_point_offset;
+ Register temp = locations->GetTemp(0).AsRegister<Register>();
+
+ Register high = first.AsRegisterPairHigh<Register>();
+ Register low = first.AsRegisterPairLow<Register>();
+
+ Register second_reg = second.AsRegister<Register>();
+
if (op->IsShl()) {
- entry_point_offset = QUICK_ENTRY_POINT(pShlLong);
+ // Shift the high part
+ __ and_(second_reg, second_reg, ShifterOperand(63));
+ __ Lsl(o_h, high, second_reg);
+ // Shift the low part and `or` what overflew on the high part
+ __ rsb(temp, second_reg, ShifterOperand(32));
+ __ Lsr(temp, low, temp);
+ __ orr(o_h, o_h, ShifterOperand(temp));
+ // If the shift is > 32 bits, override the high part
+ __ subs(temp, second_reg, ShifterOperand(32));
+ __ it(PL);
+ __ Lsl(o_h, low, temp, false, PL);
+ // Shift the low part
+ __ Lsl(o_l, low, second_reg);
} else if (op->IsShr()) {
- entry_point_offset = QUICK_ENTRY_POINT(pShrLong);
+ // Shift the low part
+ __ and_(second_reg, second_reg, ShifterOperand(63));
+ __ Lsr(o_l, low, second_reg);
+ // Shift the high part and `or` what underflew on the low part
+ __ rsb(temp, second_reg, ShifterOperand(32));
+ __ Lsl(temp, high, temp);
+ __ orr(o_l, o_l, ShifterOperand(temp));
+ // If the shift is > 32 bits, override the low part
+ __ subs(temp, second_reg, ShifterOperand(32));
+ __ it(PL);
+ __ Asr(o_l, high, temp, false, PL);
+ // Shift the high part
+ __ Asr(o_h, high, second_reg);
} else {
- entry_point_offset = QUICK_ENTRY_POINT(pUshrLong);
+ // same as Shr except we use `Lsr`s and not `Asr`s
+ __ and_(second_reg, second_reg, ShifterOperand(63));
+ __ Lsr(o_l, low, second_reg);
+ __ rsb(temp, second_reg, ShifterOperand(32));
+ __ Lsl(temp, high, temp);
+ __ orr(o_l, o_l, ShifterOperand(temp));
+ __ subs(temp, second_reg, ShifterOperand(32));
+ __ it(PL);
+ __ Lsr(o_l, high, temp, false, PL);
+ __ Lsr(o_h, high, second_reg);
}
- __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
- __ blx(LR);
break;
}
default:
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 40f0adc..6053ad5 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -474,10 +474,8 @@
HBasicBlock* first_block = new (&allocator) HBasicBlock(graph);
graph->AddBlock(first_block);
entry->AddSuccessor(first_block);
- HIntConstant* constant0 = new (&allocator) HIntConstant(0);
- entry->AddInstruction(constant0);
- HIntConstant* constant1 = new (&allocator) HIntConstant(1);
- entry->AddInstruction(constant1);
+ HIntConstant* constant0 = graph->GetIntConstant(0);
+ HIntConstant* constant1 = graph->GetIntConstant(1);
HEqual* equal = new (&allocator) HEqual(constant0, constant0);
first_block->AddInstruction(equal);
first_block->AddInstruction(new (&allocator) HIf(equal));
@@ -582,11 +580,9 @@
code_block->AddSuccessor(exit_block);
graph->SetExitBlock(exit_block);
- HIntConstant cst_lhs(lhs[i]);
- code_block->AddInstruction(&cst_lhs);
- HIntConstant cst_rhs(rhs[i]);
- code_block->AddInstruction(&cst_rhs);
- HLessThan cmp_lt(&cst_lhs, &cst_rhs);
+ HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
+ HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
+ HLessThan cmp_lt(cst_lhs, cst_rhs);
code_block->AddInstruction(&cmp_lt);
HReturn ret(&cmp_lt);
code_block->AddInstruction(&ret);
@@ -639,11 +635,9 @@
if_false_block->AddSuccessor(exit_block);
graph->SetExitBlock(exit_block);
- HIntConstant cst_lhs(lhs[i]);
- if_block->AddInstruction(&cst_lhs);
- HIntConstant cst_rhs(rhs[i]);
- if_block->AddInstruction(&cst_rhs);
- HLessThan cmp_lt(&cst_lhs, &cst_rhs);
+ HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
+ HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
+ HLessThan cmp_lt(cst_lhs, cst_rhs);
if_block->AddInstruction(&cmp_lt);
// We insert a temporary to separate the HIf from the HLessThan and force
// the materialization of the condition.
@@ -652,13 +646,11 @@
HIf if_lt(&cmp_lt);
if_block->AddInstruction(&if_lt);
- HIntConstant cst_lt(1);
- if_true_block->AddInstruction(&cst_lt);
- HReturn ret_lt(&cst_lt);
+ HIntConstant* cst_lt = graph->GetIntConstant(1);
+ HReturn ret_lt(cst_lt);
if_true_block->AddInstruction(&ret_lt);
- HIntConstant cst_ge(0);
- if_false_block->AddInstruction(&cst_ge);
- HReturn ret_ge(&cst_ge);
+ HIntConstant* cst_ge = graph->GetIntConstant(0);
+ HReturn ret_ge(cst_ge);
if_false_block->AddInstruction(&ret_ge);
auto hook_before_codegen = [](HGraph* graph_in) {
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index ec0cc3e..b7a92b5 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -55,20 +55,20 @@
if (inst->IsBinaryOperation()) {
// Constant folding: replace `op(a, b)' with a constant at
// compile time if `a' and `b' are both constants.
- HConstant* constant =
- inst->AsBinaryOperation()->TryStaticEvaluation();
+ HConstant* constant = inst->AsBinaryOperation()->TryStaticEvaluation();
if (constant != nullptr) {
- inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant);
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
} else {
inst->Accept(&simplifier);
}
} else if (inst->IsUnaryOperation()) {
// Constant folding: replace `op(a)' with a constant at compile
// time if `a' is a constant.
- HConstant* constant =
- inst->AsUnaryOperation()->TryStaticEvaluation();
+ HConstant* constant = inst->AsUnaryOperation()->TryStaticEvaluation();
if (constant != nullptr) {
- inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant);
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
}
} else if (inst->IsDivZeroCheck()) {
// We can safely remove the check if the input is a non-null constant.
@@ -173,9 +173,8 @@
// REM dst, src, src
// with
// CONSTANT 0
- ArenaAllocator* allocator = GetGraph()->GetArena();
- block->ReplaceAndRemoveInstructionWith(instruction,
- HConstant::NewConstant(allocator, type, 0));
+ instruction->ReplaceWith(GetGraph()->GetConstant(type, 0));
+ block->RemoveInstruction(instruction);
}
}
@@ -195,7 +194,6 @@
}
HBasicBlock* block = instruction->GetBlock();
- ArenaAllocator* allocator = GetGraph()->GetArena();
// We assume that GVN has run before, so we only perform a pointer
// comparison. If for some reason the values are equal but the pointers are
@@ -208,8 +206,8 @@
// CONSTANT 0
// Note that we cannot optimise `x - x` to `0` for floating-point. It does
// not work when `x` is an infinity.
- block->ReplaceAndRemoveInstructionWith(instruction,
- HConstant::NewConstant(allocator, type, 0));
+ instruction->ReplaceWith(GetGraph()->GetConstant(type, 0));
+ block->RemoveInstruction(instruction);
}
}
@@ -225,10 +223,8 @@
// CONSTANT 0
Primitive::Type type = instruction->GetType();
HBasicBlock* block = instruction->GetBlock();
- ArenaAllocator* allocator = GetGraph()->GetArena();
-
- block->ReplaceAndRemoveInstructionWith(instruction,
- HConstant::NewConstant(allocator, type, 0));
+ instruction->ReplaceWith(GetGraph()->GetConstant(type, 0));
+ block->RemoveInstruction(instruction);
}
}
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 6ceccfb..6853d54 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -101,14 +101,16 @@
// Expected difference after constant folding.
diff_t expected_cf_diff = {
{ " 2: IntConstant [5]\n", " 2: IntConstant\n" },
- { " 5: Neg(2) [8]\n", " 12: IntConstant [8]\n" },
+ { " 10: SuspendCheck\n", " 10: SuspendCheck\n"
+ " 12: IntConstant [8]\n" },
+ { " 5: Neg(2) [8]\n", removed },
{ " 8: Return(5)\n", " 8: Return(12)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the value of the computed constant.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst->IsIntConstant());
ASSERT_EQ(inst->AsIntConstant()->GetValue(), -1);
};
@@ -160,14 +162,16 @@
diff_t expected_cf_diff = {
{ " 3: IntConstant [9]\n", " 3: IntConstant\n" },
{ " 5: IntConstant [9]\n", " 5: IntConstant\n" },
- { " 9: Add(3, 5) [12]\n", " 16: IntConstant [12]\n" },
+ { " 14: SuspendCheck\n", " 14: SuspendCheck\n"
+ " 16: IntConstant [12]\n" },
+ { " 9: Add(3, 5) [12]\n", removed },
{ " 12: Return(9)\n", " 12: Return(16)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the value of the computed constant.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst->IsIntConstant());
ASSERT_EQ(inst->AsIntConstant()->GetValue(), 3);
};
@@ -195,8 +199,8 @@
* v0 <- 1 0. const/4 v0, #+1
* v1 <- 2 1. const/4 v1, #+2
* v0 <- v0 + v1 2. add-int/2addr v0, v1
- * v1 <- 3 3. const/4 v1, #+3
- * v2 <- 4 4. const/4 v2, #+4
+ * v1 <- 4 3. const/4 v1, #+4
+ * v2 <- 5 4. const/4 v2, #+5
* v1 <- v1 + v2 5. add-int/2addr v1, v2
* v2 <- v0 + v1 6. add-int v2, v0, v1
* return v2 8. return v2
@@ -206,8 +210,8 @@
Instruction::CONST_4 | 0 << 8 | 1 << 12,
Instruction::CONST_4 | 1 << 8 | 2 << 12,
Instruction::ADD_INT_2ADDR | 0 << 8 | 1 << 12,
- Instruction::CONST_4 | 1 << 8 | 3 << 12,
- Instruction::CONST_4 | 2 << 8 | 4 << 12,
+ Instruction::CONST_4 | 1 << 8 | 4 << 12,
+ Instruction::CONST_4 | 2 << 8 | 5 << 12,
Instruction::ADD_INT_2ADDR | 1 << 8 | 2 << 12,
Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
Instruction::RETURN | 2 << 8);
@@ -234,24 +238,28 @@
{ " 5: IntConstant [9]\n", " 5: IntConstant\n" },
{ " 11: IntConstant [17]\n", " 11: IntConstant\n" },
{ " 13: IntConstant [17]\n", " 13: IntConstant\n" },
- { " 9: Add(3, 5) [21]\n", " 28: IntConstant\n" },
- { " 17: Add(11, 13) [21]\n", " 29: IntConstant\n" },
- { " 21: Add(9, 17) [24]\n", " 30: IntConstant [24]\n" },
+ { " 26: SuspendCheck\n", " 26: SuspendCheck\n"
+ " 28: IntConstant\n"
+ " 29: IntConstant\n"
+ " 30: IntConstant [24]\n" },
+ { " 9: Add(3, 5) [21]\n", removed },
+ { " 17: Add(11, 13) [21]\n", removed },
+ { " 21: Add(9, 17) [24]\n", removed },
{ " 24: Return(21)\n", " 24: Return(30)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the values of the computed constants.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst1->IsIntConstant());
- ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 3);
- HInstruction* inst2 = inst1->GetNext();
+ ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 12);
+ HInstruction* inst2 = inst1->GetPrevious();
ASSERT_TRUE(inst2->IsIntConstant());
- ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 7);
- HInstruction* inst3 = inst2->GetNext();
+ ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 9);
+ HInstruction* inst3 = inst2->GetPrevious();
ASSERT_TRUE(inst3->IsIntConstant());
- ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 10);
+ ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 3);
};
// Expected difference after dead code elimination.
@@ -306,14 +314,16 @@
diff_t expected_cf_diff = {
{ " 3: IntConstant [9]\n", " 3: IntConstant\n" },
{ " 5: IntConstant [9]\n", " 5: IntConstant\n" },
- { " 9: Sub(3, 5) [12]\n", " 16: IntConstant [12]\n" },
+ { " 14: SuspendCheck\n", " 14: SuspendCheck\n"
+ " 16: IntConstant [12]\n" },
+ { " 9: Sub(3, 5) [12]\n", removed },
{ " 12: Return(9)\n", " 12: Return(16)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the value of the computed constant.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst->IsIntConstant());
ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1);
};
@@ -368,14 +378,16 @@
diff_t expected_cf_diff = {
{ " 6: LongConstant [12]\n", " 6: LongConstant\n" },
{ " 8: LongConstant [12]\n", " 8: LongConstant\n" },
- { " 12: Add(6, 8) [15]\n", " 19: LongConstant [15]\n" },
+ { " 17: SuspendCheck\n", " 17: SuspendCheck\n"
+ " 19: LongConstant [15]\n" },
+ { " 12: Add(6, 8) [15]\n", removed },
{ " 15: Return(12)\n", " 15: Return(19)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the value of the computed constant.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst->IsLongConstant());
ASSERT_EQ(inst->AsLongConstant()->GetValue(), 3);
};
@@ -431,14 +443,16 @@
diff_t expected_cf_diff = {
{ " 6: LongConstant [12]\n", " 6: LongConstant\n" },
{ " 8: LongConstant [12]\n", " 8: LongConstant\n" },
- { " 12: Sub(6, 8) [15]\n", " 19: LongConstant [15]\n" },
+ { " 17: SuspendCheck\n", " 17: SuspendCheck\n"
+ " 19: LongConstant [15]\n" },
+ { " 12: Sub(6, 8) [15]\n", removed },
{ " 15: Return(12)\n", " 15: Return(19)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the value of the computed constant.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst->IsLongConstant());
ASSERT_EQ(inst->AsLongConstant()->GetValue(), 1);
};
@@ -469,51 +483,51 @@
* 16-bit
* offset
* ------
- * v0 <- 0 0. const/4 v0, #+0
- * v1 <- 1 1. const/4 v1, #+1
+ * v0 <- 1 0. const/4 v0, #+1
+ * v1 <- 2 1. const/4 v1, #+2
* v2 <- v0 + v1 2. add-int v2, v0, v1
* goto L2 4. goto +4
- * L1: v1 <- v0 + 3 5. add-int/lit16 v1, v0, #+3
+ * L1: v1 <- v0 + 5 5. add-int/lit16 v1, v0, #+5
* goto L3 7. goto +4
- * L2: v0 <- v2 + 2 8. add-int/lit16 v0, v2, #+2
+ * L2: v0 <- v2 + 4 8. add-int/lit16 v0, v2, #+4
* goto L1 10. goto +(-5)
- * L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4
+ * L3: v2 <- v1 + 8 11. add-int/lit16 v2, v1, #+8
* return v2 13. return v2
*/
TEST(ConstantFolding, IntConstantFoldingAndJumps) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
- Instruction::CONST_4 | 0 << 8 | 0 << 12,
- Instruction::CONST_4 | 1 << 8 | 1 << 12,
+ Instruction::CONST_4 | 0 << 8 | 1 << 12,
+ Instruction::CONST_4 | 1 << 8 | 2 << 12,
Instruction::ADD_INT | 2 << 8, 0 | 1 << 8,
Instruction::GOTO | 4 << 8,
- Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 3,
+ Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 5,
Instruction::GOTO | 4 << 8,
- Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 2,
+ Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 4,
static_cast<uint16_t>(Instruction::GOTO | -5 << 8),
- Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 4,
+ Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 8,
Instruction::RETURN | 2 << 8);
std::string expected_before =
"BasicBlock 0, succ: 1\n"
- " 3: IntConstant [9]\n" // v0 <- 0
- " 5: IntConstant [9]\n" // v1 <- 1
- " 13: IntConstant [14]\n" // const 3
- " 18: IntConstant [19]\n" // const 2
- " 24: IntConstant [25]\n" // const 4
+ " 3: IntConstant [9]\n" // v0 <- 1
+ " 5: IntConstant [9]\n" // v1 <- 2
+ " 13: IntConstant [14]\n" // const 5
+ " 18: IntConstant [19]\n" // const 4
+ " 24: IntConstant [25]\n" // const 8
" 30: SuspendCheck\n"
" 31: Goto 1\n"
"BasicBlock 1, pred: 0, succ: 3\n"
- " 9: Add(3, 5) [19]\n" // v2 <- v0 + v1 = 0 + 1 = 1
+ " 9: Add(3, 5) [19]\n" // v2 <- v0 + v1 = 1 + 2 = 3
" 11: Goto 3\n" // goto L2
"BasicBlock 2, pred: 3, succ: 4\n" // L1:
- " 14: Add(19, 13) [25]\n" // v1 <- v0 + 3 = 3 + 3 = 6
+ " 14: Add(19, 13) [25]\n" // v1 <- v0 + 3 = 7 + 5 = 12
" 16: Goto 4\n" // goto L3
"BasicBlock 3, pred: 1, succ: 2\n" // L2:
- " 19: Add(9, 18) [14]\n" // v0 <- v2 + 2 = 1 + 2 = 3
+ " 19: Add(9, 18) [14]\n" // v0 <- v2 + 2 = 3 + 4 = 7
" 21: SuspendCheck\n"
" 22: Goto 2\n" // goto L1
"BasicBlock 4, pred: 2, succ: 5\n" // L3:
- " 25: Add(14, 24) [28]\n" // v2 <- v1 + 4 = 6 + 4 = 10
+ " 25: Add(14, 24) [28]\n" // v2 <- v1 + 4 = 12 + 8 = 20
" 28: Return(25)\n" // return v2
"BasicBlock 5, pred: 4\n"
" 29: Exit\n";
@@ -525,28 +539,33 @@
{ " 13: IntConstant [14]\n", " 13: IntConstant\n" },
{ " 18: IntConstant [19]\n", " 18: IntConstant\n" },
{ " 24: IntConstant [25]\n", " 24: IntConstant\n" },
- { " 9: Add(3, 5) [19]\n", " 32: IntConstant []\n" },
- { " 14: Add(19, 13) [25]\n", " 34: IntConstant\n" },
- { " 19: Add(9, 18) [14]\n", " 33: IntConstant []\n" },
- { " 25: Add(14, 24) [28]\n", " 35: IntConstant [28]\n" },
+ { " 30: SuspendCheck\n", " 30: SuspendCheck\n"
+ " 32: IntConstant []\n"
+ " 33: IntConstant []\n"
+ " 34: IntConstant\n"
+ " 35: IntConstant [28]\n" },
+ { " 9: Add(3, 5) [19]\n", removed },
+ { " 14: Add(19, 13) [25]\n", removed },
+ { " 19: Add(9, 18) [14]\n", removed },
+ { " 25: Add(14, 24) [28]\n", removed },
{ " 28: Return(25)\n", " 28: Return(35)\n"}
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the values of the computed constants.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst1 = graph->GetBlock(4)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst1->IsIntConstant());
- ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 1);
- HInstruction* inst2 = graph->GetBlock(2)->GetFirstInstruction();
+ ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 20);
+ HInstruction* inst2 = inst1->GetPrevious();
ASSERT_TRUE(inst2->IsIntConstant());
- ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 6);
- HInstruction* inst3 = graph->GetBlock(3)->GetFirstInstruction();
+ ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 12);
+ HInstruction* inst3 = inst2->GetPrevious();
ASSERT_TRUE(inst3->IsIntConstant());
- ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 3);
- HInstruction* inst4 = graph->GetBlock(4)->GetFirstInstruction();
+ ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 7);
+ HInstruction* inst4 = inst3->GetPrevious();
ASSERT_TRUE(inst4->IsIntConstant());
- ASSERT_EQ(inst4->AsIntConstant()->GetValue(), 10);
+ ASSERT_EQ(inst4->AsIntConstant()->GetValue(), 3);
};
// Expected difference after dead code elimination.
@@ -611,25 +630,25 @@
// Expected difference after constant folding.
diff_t expected_cf_diff = {
- { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [15, 22]\n" },
+ { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [9, 15, 22]\n" },
{ " 5: IntConstant [22, 8]\n", " 5: IntConstant [22]\n" },
- { " 8: GreaterThanOrEqual(3, 5) [9]\n", " 23: IntConstant [9]\n" },
- { " 9: If(8)\n", " 9: If(23)\n" }
+ { " 8: GreaterThanOrEqual(3, 5) [9]\n", removed },
+ { " 9: If(8)\n", " 9: If(3)\n" }
};
std::string expected_after_cf = Patch(expected_before, expected_cf_diff);
// Check the values of the computed constants.
auto check_after_cf = [](HGraph* graph) {
- HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction();
+ HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0);
ASSERT_TRUE(inst->IsIntConstant());
ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1);
};
// Expected difference after dead code elimination.
diff_t expected_dce_diff = {
- { " 3: IntConstant [15, 22]\n", " 3: IntConstant [22]\n" },
- { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" },
- { " 15: Add(22, 3)\n", removed }
+ { " 3: IntConstant [9, 15, 22]\n", " 3: IntConstant [9, 22]\n" },
+ { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" },
+ { " 15: Add(22, 3)\n", removed }
};
std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 09a3ae4..7c3c2bf 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -80,8 +80,7 @@
}
// Ensure `block` ends with a branch instruction.
- HInstruction* last_inst = block->GetLastInstruction();
- if (last_inst == nullptr || !last_inst->IsControlFlow()) {
+ if (!block->EndsWithControlFlowInstruction()) {
AddError(StringPrintf("Block %d does not end with a branch instruction.",
block->GetBlockId()));
}
@@ -476,4 +475,15 @@
}
}
+void SSAChecker::VisitConstant(HConstant* instruction) {
+ HBasicBlock* block = instruction->GetBlock();
+ if (!block->IsEntryBlock()) {
+ AddError(StringPrintf(
+ "%s %d should be in the entry block but is in block %d.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ block->GetBlockId()));
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 5ec3003..89fea0a 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -107,6 +107,7 @@
void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE;
void VisitCondition(HCondition* op) OVERRIDE;
void VisitIf(HIf* instruction) OVERRIDE;
+ void VisitConstant(HConstant* instruction) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(SSAChecker);
diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc
index 4742e4d..50398b4 100644
--- a/compiler/optimizing/graph_test.cc
+++ b/compiler/optimizing/graph_test.cc
@@ -28,8 +28,7 @@
static HBasicBlock* createIfBlock(HGraph* graph, ArenaAllocator* allocator) {
HBasicBlock* if_block = new (allocator) HBasicBlock(graph);
graph->AddBlock(if_block);
- HInstruction* instr = new (allocator) HIntConstant(4);
- if_block->AddInstruction(instr);
+ HInstruction* instr = graph->GetIntConstant(4);
HInstruction* equal = new (allocator) HEqual(instr, instr);
if_block->AddInstruction(equal);
instr = new (allocator) HIf(equal);
@@ -45,6 +44,12 @@
return block;
}
+static HBasicBlock* createEntryBlock(HGraph* graph, ArenaAllocator* allocator) {
+ HBasicBlock* block = createGotoBlock(graph, allocator);
+ graph->SetEntryBlock(block);
+ return block;
+}
+
static HBasicBlock* createReturnBlock(HGraph* graph, ArenaAllocator* allocator) {
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
@@ -69,7 +74,7 @@
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- HBasicBlock* entry_block = createGotoBlock(graph, &allocator);
+ HBasicBlock* entry_block = createEntryBlock(graph, &allocator);
HBasicBlock* if_block = createIfBlock(graph, &allocator);
HBasicBlock* if_true = createGotoBlock(graph, &allocator);
HBasicBlock* return_block = createReturnBlock(graph, &allocator);
@@ -104,7 +109,7 @@
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- HBasicBlock* entry_block = createGotoBlock(graph, &allocator);
+ HBasicBlock* entry_block = createEntryBlock(graph, &allocator);
HBasicBlock* if_block = createIfBlock(graph, &allocator);
HBasicBlock* if_false = createGotoBlock(graph, &allocator);
HBasicBlock* return_block = createReturnBlock(graph, &allocator);
@@ -139,12 +144,11 @@
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- HBasicBlock* entry_block = createGotoBlock(graph, &allocator);
+ HBasicBlock* entry_block = createEntryBlock(graph, &allocator);
HBasicBlock* if_block = createIfBlock(graph, &allocator);
HBasicBlock* return_block = createReturnBlock(graph, &allocator);
HBasicBlock* exit_block = createExitBlock(graph, &allocator);
- graph->SetEntryBlock(entry_block);
entry_block->AddSuccessor(if_block);
if_block->AddSuccessor(if_block);
if_block->AddSuccessor(return_block);
@@ -175,12 +179,11 @@
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- HBasicBlock* entry_block = createGotoBlock(graph, &allocator);
+ HBasicBlock* entry_block = createEntryBlock(graph, &allocator);
HBasicBlock* if_block = createIfBlock(graph, &allocator);
HBasicBlock* return_block = createReturnBlock(graph, &allocator);
HBasicBlock* exit_block = createExitBlock(graph, &allocator);
- graph->SetEntryBlock(entry_block);
entry_block->AddSuccessor(if_block);
if_block->AddSuccessor(return_block);
if_block->AddSuccessor(if_block);
@@ -211,13 +214,12 @@
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- HBasicBlock* entry_block = createGotoBlock(graph, &allocator);
+ HBasicBlock* entry_block = createEntryBlock(graph, &allocator);
HBasicBlock* first_if_block = createIfBlock(graph, &allocator);
HBasicBlock* if_block = createIfBlock(graph, &allocator);
HBasicBlock* loop_block = createGotoBlock(graph, &allocator);
HBasicBlock* return_block = createReturnBlock(graph, &allocator);
- graph->SetEntryBlock(entry_block);
entry_block->AddSuccessor(first_if_block);
first_if_block->AddSuccessor(if_block);
first_if_block->AddSuccessor(loop_block);
@@ -251,13 +253,12 @@
ArenaAllocator allocator(&pool);
HGraph* graph = new (&allocator) HGraph(&allocator);
- HBasicBlock* entry_block = createGotoBlock(graph, &allocator);
+ HBasicBlock* entry_block = createEntryBlock(graph, &allocator);
HBasicBlock* first_if_block = createIfBlock(graph, &allocator);
HBasicBlock* if_block = createIfBlock(graph, &allocator);
HBasicBlock* loop_block = createGotoBlock(graph, &allocator);
HBasicBlock* return_block = createReturnBlock(graph, &allocator);
- graph->SetEntryBlock(entry_block);
entry_block->AddSuccessor(first_if_block);
first_if_block->AddSuccessor(if_block);
first_if_block->AddSuccessor(loop_block);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 968fe3e..4b990f1 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -50,7 +50,9 @@
HInstruction* next = instruction->GetNext();
HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect();
if (call != nullptr) {
- if (!TryInline(call, call->GetDexMethodIndex(), call->GetInvokeType())) {
+ // We use the original invoke type to ensure the resolution of the called method
+ // works properly.
+ if (!TryInline(call, call->GetDexMethodIndex(), call->GetOriginalInvokeType())) {
if (kIsDebugBuild) {
std::string callee_name =
PrettyMethod(call->GetDexMethodIndex(), *outer_compilation_unit_.GetDexFile());
@@ -68,87 +70,87 @@
uint32_t method_index,
InvokeType invoke_type) const {
ScopedObjectAccess soa(Thread::Current());
- const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
- VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, outer_dex_file);
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+ VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, caller_dex_file);
StackHandleScope<3> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file)));
+ hs.NewHandle(caller_compilation_unit_.GetClassLinker()->FindDexCache(caller_dex_file)));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader*>(outer_compilation_unit_.GetClassLoader())));
+ soa.Decode<mirror::ClassLoader*>(caller_compilation_unit_.GetClassLoader())));
Handle<mirror::ArtMethod> resolved_method(hs.NewHandle(
compiler_driver_->ResolveMethod(
- soa, dex_cache, class_loader, &outer_compilation_unit_, method_index, invoke_type)));
+ soa, dex_cache, class_loader, &caller_compilation_unit_, method_index, invoke_type)));
if (resolved_method.Get() == nullptr) {
- VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file);
+ VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, caller_dex_file);
return false;
}
+ bool can_use_dex_cache = true;
+ const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
- VLOG(compiler) << "Did not inline "
- << PrettyMethod(method_index, outer_dex_file)
- << " because it is in a different dex file";
- return false;
+ can_use_dex_cache = false;
}
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
if (code_item == nullptr) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " is not inlined because it is native";
return false;
}
if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " is too big to inline";
return false;
}
if (code_item->tries_size_ != 0) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " is not inlined because of try block";
return false;
}
if (!resolved_method->GetDeclaringClass()->IsVerified()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " is not inlined because its class could not be verified";
return false;
}
if (resolved_method->ShouldNotInline()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " was already flagged as non inlineable";
return false;
}
- if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index)) {
+ if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, can_use_dex_cache)) {
resolved_method->SetShouldNotInline();
return false;
}
- VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file);
+ VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, caller_dex_file);
MaybeRecordStat(kInlinedInvoke);
return true;
}
bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,
HInvoke* invoke_instruction,
- uint32_t method_index) const {
+ uint32_t method_index,
+ bool can_use_dex_cache) const {
ScopedObjectAccess soa(Thread::Current());
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
- const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
DexCompilationUnit dex_compilation_unit(
nullptr,
- outer_compilation_unit_.GetClassLoader(),
- outer_compilation_unit_.GetClassLinker(),
- outer_dex_file,
+ caller_compilation_unit_.GetClassLoader(),
+ caller_compilation_unit_.GetClassLinker(),
+ *resolved_method->GetDexFile(),
code_item,
resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
- method_index,
+ resolved_method->GetDexMethodIndex(),
resolved_method->GetAccessFlags(),
nullptr);
@@ -159,25 +161,25 @@
HGraphBuilder builder(callee_graph,
&dex_compilation_unit,
&outer_compilation_unit_,
- &outer_dex_file,
+ resolved_method->GetDexFile(),
compiler_driver_,
&inline_stats);
if (!builder.BuildGraph(*code_item)) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " could not be built, so cannot be inlined";
return false;
}
if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
compiler_driver_->GetInstructionSet())) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " cannot be inlined because of the register allocator";
return false;
}
if (!callee_graph->TryBuildingSsa()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " could not be transformed to SSA";
return false;
}
@@ -199,8 +201,12 @@
}
if (depth_ + 1 < kDepthLimit) {
- HInliner inliner(
- callee_graph, outer_compilation_unit_, compiler_driver_, stats_, depth_ + 1);
+ HInliner inliner(callee_graph,
+ outer_compilation_unit_,
+ dex_compilation_unit,
+ compiler_driver_,
+ stats_,
+ depth_ + 1);
inliner.Run();
}
@@ -209,7 +215,7 @@
for (; !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (block->IsLoopHeader()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " could not be inlined because it contains a loop";
return false;
}
@@ -223,18 +229,25 @@
}
if (current->CanThrow()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " could not be inlined because " << current->DebugName()
<< " can throw";
return false;
}
if (current->NeedsEnvironment()) {
- VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " could not be inlined because " << current->DebugName()
<< " needs an environment";
return false;
}
+
+ if (!can_use_dex_cache && current->NeedsDexCache()) {
+ VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+ << " could not be inlined because " << current->DebugName()
+ << " it is in a different dex file and requires access to the dex cache";
+ return false;
+ }
}
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 1251977..1dbc7d3 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -32,11 +32,13 @@
public:
HInliner(HGraph* outer_graph,
const DexCompilationUnit& outer_compilation_unit,
+ const DexCompilationUnit& caller_compilation_unit,
CompilerDriver* compiler_driver,
OptimizingCompilerStats* stats,
size_t depth = 0)
: HOptimization(outer_graph, true, kInlinerPassName, stats),
outer_compilation_unit_(outer_compilation_unit),
+ caller_compilation_unit_(caller_compilation_unit),
compiler_driver_(compiler_driver),
depth_(depth) {}
@@ -48,9 +50,11 @@
bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
bool TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,
HInvoke* invoke_instruction,
- uint32_t method_index) const;
+ uint32_t method_index,
+ bool can_use_dex_cache) const;
const DexCompilationUnit& outer_compilation_unit_;
+ const DexCompilationUnit& caller_compilation_unit_;
CompilerDriver* const compiler_driver_;
const size_t depth_;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 2ef19b9..56ec8a7 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -292,8 +292,7 @@
// MUL dst, src, pow_of_2
// with
// SHL dst, src, log2(pow_of_2)
- HIntConstant* shift = new (allocator) HIntConstant(WhichPowerOf2(factor));
- block->InsertInstructionBefore(shift, instruction);
+ HIntConstant* shift = GetGraph()->GetIntConstant(WhichPowerOf2(factor));
HShl* shl = new(allocator) HShl(type, input_other, shift);
block->ReplaceAndRemoveInstructionWith(instruction, shl);
}
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 4f6565d..dca612e 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -287,39 +287,62 @@
return true;
}
-void HGraph::AddConstant(HConstant* instruction) {
- HInstruction* last_instruction = entry_block_->GetLastInstruction();
- if (last_instruction == nullptr || !last_instruction->IsControlFlow()) {
- // Called from the builder. Insert at the end of the block.
- entry_block_->AddInstruction(instruction);
+void HGraph::InsertConstant(HConstant* constant) {
+ // New constants are inserted before the final control-flow instruction
+ // of the graph, or at its end if called from the graph builder.
+ if (entry_block_->EndsWithControlFlowInstruction()) {
+ entry_block_->InsertInstructionBefore(constant, entry_block_->GetLastInstruction());
} else {
- // Entry block ends with control-flow. Insert before the last instruction.
- entry_block_->InsertInstructionBefore(instruction, last_instruction);
+ entry_block_->AddInstruction(constant);
}
}
HNullConstant* HGraph::GetNullConstant() {
if (cached_null_constant_ == nullptr) {
cached_null_constant_ = new (arena_) HNullConstant();
- AddConstant(cached_null_constant_);
+ InsertConstant(cached_null_constant_);
}
return cached_null_constant_;
}
-HIntConstant* HGraph::GetIntConstant0() {
- if (cached_int_constant0_ == nullptr) {
- cached_int_constant0_ = new (arena_) HIntConstant(0);
- AddConstant(cached_int_constant0_);
+template <class InstructionType, typename ValueType>
+InstructionType* HGraph::CreateConstant(ValueType value,
+ ArenaSafeMap<ValueType, InstructionType*>* cache) {
+ // Try to find an existing constant of the given value.
+ InstructionType* constant = nullptr;
+ auto cached_constant = cache->find(value);
+ if (cached_constant != cache->end()) {
+ constant = cached_constant->second;
}
- return cached_int_constant0_;
+
+ // If not found or previously deleted, create and cache a new instruction.
+ if (constant == nullptr || constant->GetBlock() == nullptr) {
+ constant = new (arena_) InstructionType(value);
+ cache->Overwrite(value, constant);
+ InsertConstant(constant);
+ }
+ return constant;
}
-HIntConstant* HGraph::GetIntConstant1() {
- if (cached_int_constant1_ == nullptr) {
- cached_int_constant1_ = new (arena_) HIntConstant(1);
- AddConstant(cached_int_constant1_);
+HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value) {
+ switch (type) {
+ case Primitive::Type::kPrimBoolean:
+ DCHECK(IsUint<1>(value));
+ FALLTHROUGH_INTENDED;
+ case Primitive::Type::kPrimByte:
+ case Primitive::Type::kPrimChar:
+ case Primitive::Type::kPrimShort:
+ case Primitive::Type::kPrimInt:
+ DCHECK(IsInt(Primitive::ComponentSize(type) * kBitsPerByte, value));
+ return GetIntConstant(static_cast<int32_t>(value));
+
+ case Primitive::Type::kPrimLong:
+ return GetLongConstant(value);
+
+ default:
+ LOG(FATAL) << "Unsupported constant type";
+ UNREACHABLE();
}
- return cached_int_constant1_;
}
void HLoopInformation::Add(HBasicBlock* block) {
@@ -676,7 +699,7 @@
HConstant* HUnaryOperation::TryStaticEvaluation() const {
if (GetInput()->IsIntConstant()) {
int32_t value = Evaluate(GetInput()->AsIntConstant()->GetValue());
- return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+ return GetBlock()->GetGraph()->GetIntConstant(value);
} else if (GetInput()->IsLongConstant()) {
// TODO: Implement static evaluation of long unary operations.
//
@@ -692,15 +715,15 @@
if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) {
int32_t value = Evaluate(GetLeft()->AsIntConstant()->GetValue(),
GetRight()->AsIntConstant()->GetValue());
- return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+ return GetBlock()->GetGraph()->GetIntConstant(value);
} else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
GetRight()->AsLongConstant()->GetValue());
if (GetResultType() == Primitive::kPrimLong) {
- return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+ return GetBlock()->GetGraph()->GetLongConstant(value);
} else {
DCHECK_EQ(GetResultType(), Primitive::kPrimInt);
- return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+ return GetBlock()->GetGraph()->GetIntConstant(static_cast<int32_t>(value));
}
}
return nullptr;
@@ -733,16 +756,6 @@
return this == if_->GetPreviousDisregardingMoves();
}
-HConstant* HConstant::NewConstant(ArenaAllocator* allocator, Primitive::Type type, int64_t val) {
- if (type == Primitive::kPrimInt) {
- DCHECK(IsInt<32>(val));
- return new (allocator) HIntConstant(val);
- } else {
- DCHECK_EQ(type, Primitive::kPrimLong);
- return new (allocator) HLongConstant(val);
- }
-}
-
bool HInstruction::Equals(HInstruction* other) const {
if (!InstructionTypeEquals(other)) return false;
DCHECK_EQ(GetKind(), other->GetKind());
@@ -832,6 +845,10 @@
&& (loop_info == nullptr || !loop_info->IsBackEdge(*this));
}
+bool HBasicBlock::EndsWithControlFlowInstruction() const {
+ return !GetInstructions().IsEmpty() && GetLastInstruction()->IsControlFlow();
+}
+
bool HBasicBlock::EndsWithIf() const {
return !GetInstructions().IsEmpty() && GetLastInstruction()->IsIf();
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 664cf18..c4e2798 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_NODES_H_
#define ART_COMPILER_OPTIMIZING_NODES_H_
+#include "base/arena_containers.h"
#include "base/arena_object.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
@@ -33,16 +34,20 @@
class GraphChecker;
class HBasicBlock;
+class HDoubleConstant;
class HEnvironment;
+class HFloatConstant;
+class HGraphVisitor;
class HInstruction;
class HIntConstant;
class HInvoke;
-class HGraphVisitor;
+class HLongConstant;
class HNullConstant;
class HPhi;
class HSuspendCheck;
class LiveInterval;
class LocationSummary;
+class SsaBuilder;
static const int kDefaultNumberOfBlocks = 8;
static const int kDefaultNumberOfSuccessors = 2;
@@ -115,7 +120,10 @@
temporaries_vreg_slots_(0),
has_array_accesses_(false),
debuggable_(debuggable),
- current_instruction_id_(start_instruction_id) {}
+ current_instruction_id_(start_instruction_id),
+ cached_null_constant_(nullptr),
+ cached_int_constants_(std::less<int32_t>(), arena->Adapter()),
+ cached_long_constants_(std::less<int64_t>(), arena->Adapter()) {}
ArenaAllocator* GetArena() const { return arena_; }
const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; }
@@ -128,7 +136,6 @@
void SetExitBlock(HBasicBlock* block) { exit_block_ = block; }
void AddBlock(HBasicBlock* block);
- void AddConstant(HConstant* instruction);
// Try building the SSA form of this graph, with dominance computation and loop
// recognition. Returns whether it was successful in doing all these steps.
@@ -219,9 +226,17 @@
bool IsDebuggable() const { return debuggable_; }
+ // Returns a constant of the given type and value. If it does not exist
+ // already, it is created and inserted into the graph. Only integral types
+ // are currently supported.
+ HConstant* GetConstant(Primitive::Type type, int64_t value);
HNullConstant* GetNullConstant();
- HIntConstant* GetIntConstant0();
- HIntConstant* GetIntConstant1();
+ HIntConstant* GetIntConstant(int32_t value) {
+ return CreateConstant(value, &cached_int_constants_);
+ }
+ HLongConstant* GetLongConstant(int64_t value) {
+ return CreateConstant(value, &cached_long_constants_);
+ }
private:
HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const;
@@ -235,6 +250,10 @@
void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited) const;
+ template <class InstType, typename ValueType>
+ InstType* CreateConstant(ValueType value, ArenaSafeMap<ValueType, InstType*>* cache);
+ void InsertConstant(HConstant* instruction);
+
ArenaAllocator* const arena_;
// List of blocks in insertion order.
@@ -269,12 +288,10 @@
// The current id to assign to a newly added instruction. See HInstruction.id_.
int32_t current_instruction_id_;
- // Cached null constant that might be created when building SSA form.
- HNullConstant* cached_null_constant_;
-
// Cached common constants often needed by optimization passes.
- HIntConstant* cached_int_constant0_;
- HIntConstant* cached_int_constant1_;
+ HNullConstant* cached_null_constant_;
+ ArenaSafeMap<int32_t, HIntConstant*> cached_int_constants_;
+ ArenaSafeMap<int64_t, HLongConstant*> cached_long_constants_;
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
DISALLOW_COPY_AND_ASSIGN(HGraph);
@@ -602,6 +619,7 @@
bool IsCatchBlock() const { return is_catch_block_; }
void SetIsCatchBlock() { is_catch_block_ = true; }
+ bool EndsWithControlFlowInstruction() const;
bool EndsWithIf() const;
bool HasSinglePhi() const;
@@ -1248,6 +1266,8 @@
return NeedsEnvironment() || IsLoadClass() || IsLoadString();
}
+ virtual bool NeedsDexCache() const { return false; }
+
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
@@ -1906,8 +1926,6 @@
virtual bool IsZero() const { return false; }
virtual bool IsOne() const { return false; }
- static HConstant* NewConstant(ArenaAllocator* allocator, Primitive::Type type, int64_t val);
-
DECLARE_INSTRUCTION(Constant);
private:
@@ -1916,8 +1934,6 @@
class HFloatConstant : public HConstant {
public:
- explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {}
-
float GetValue() const { return value_; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -1942,15 +1958,19 @@
DECLARE_INSTRUCTION(FloatConstant);
private:
+ explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {}
+
const float value_;
+ // Only the SsaBuilder can currently create floating-point constants. If we
+ // ever need to create them later in the pipeline, we will have to handle them
+ // the same way as integral constants.
+ friend class SsaBuilder;
DISALLOW_COPY_AND_ASSIGN(HFloatConstant);
};
class HDoubleConstant : public HConstant {
public:
- explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {}
-
double GetValue() const { return value_; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -1975,15 +1995,19 @@
DECLARE_INSTRUCTION(DoubleConstant);
private:
+ explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {}
+
const double value_;
+ // Only the SsaBuilder can currently create floating-point constants. If we
+ // ever need to create them later in the pipeline, we will have to handle them
+ // the same way as integral constants.
+ friend class SsaBuilder;
DISALLOW_COPY_AND_ASSIGN(HDoubleConstant);
};
class HNullConstant : public HConstant {
public:
- HNullConstant() : HConstant(Primitive::kPrimNot) {}
-
bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
return true;
}
@@ -1995,6 +2019,9 @@
DECLARE_INSTRUCTION(NullConstant);
private:
+ HNullConstant() : HConstant(Primitive::kPrimNot) {}
+
+ friend class HGraph;
DISALLOW_COPY_AND_ASSIGN(HNullConstant);
};
@@ -2002,8 +2029,6 @@
// synthesized (for example with the if-eqz instruction).
class HIntConstant : public HConstant {
public:
- explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {}
-
int32_t GetValue() const { return value_; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -2024,15 +2049,18 @@
DECLARE_INSTRUCTION(IntConstant);
private:
+ explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {}
+
const int32_t value_;
+ friend class HGraph;
+ ART_FRIEND_TEST(GraphTest, InsertInstructionBefore);
+ ART_FRIEND_TEST(ParallelMoveTest, ConstantLast);
DISALLOW_COPY_AND_ASSIGN(HIntConstant);
};
class HLongConstant : public HConstant {
public:
- explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {}
-
int64_t GetValue() const { return value_; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -2048,8 +2076,11 @@
DECLARE_INSTRUCTION(LongConstant);
private:
+ explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {}
+
const int64_t value_;
+ friend class HGraph;
DISALLOW_COPY_AND_ASSIGN(HLongConstant);
};
@@ -2129,8 +2160,10 @@
uint32_t dex_pc,
uint32_t dex_method_index,
bool is_recursive,
+ InvokeType original_invoke_type,
InvokeType invoke_type)
: HInvoke(arena, number_of_arguments, return_type, dex_pc, dex_method_index),
+ original_invoke_type_(original_invoke_type),
invoke_type_(invoke_type),
is_recursive_(is_recursive) {}
@@ -2140,12 +2173,15 @@
return false;
}
+ InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
InvokeType GetInvokeType() const { return invoke_type_; }
bool IsRecursive() const { return is_recursive_; }
+ bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); }
DECLARE_INSTRUCTION(InvokeStaticOrDirect);
private:
+ const InvokeType original_invoke_type_;
const InvokeType invoke_type_;
const bool is_recursive_;
@@ -3024,6 +3060,8 @@
return loaded_class_rti_.IsExact();
}
+ bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; }
+
DECLARE_INSTRUCTION(LoadClass);
private:
@@ -3059,6 +3097,7 @@
// TODO: Can we deopt or debug when we resolve a string?
bool NeedsEnvironment() const OVERRIDE { return false; }
+ bool NeedsDexCache() const OVERRIDE { return true; }
DECLARE_INSTRUCTION(LoadString);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index eaa30df..5ce73ba 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -316,7 +316,7 @@
InstructionSimplifier simplify1(graph, stats);
HBooleanSimplifier boolean_not(graph);
- HInliner inliner(graph, dex_compilation_unit, driver, stats);
+ HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, stats);
HConstantFolding fold2(graph);
SideEffectsAnalysis side_effects(graph);
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 7a2d84b..7c3a035 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -644,11 +644,10 @@
graph->AddBlock(entry);
graph->SetEntryBlock(entry);
HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimInt);
- HInstruction* constant1 = new (allocator) HIntConstant(0);
- HInstruction* constant2 = new (allocator) HIntConstant(0);
entry->AddInstruction(parameter);
- entry->AddInstruction(constant1);
- entry->AddInstruction(constant2);
+
+ HInstruction* constant1 = graph->GetIntConstant(1);
+ HInstruction* constant2 = graph->GetIntConstant(2);
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index ae6bf16..fcc4e69 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -353,7 +353,7 @@
* is used for floating point operations. We create a floating-point equivalent
* constant to make the operations correctly typed.
*/
-static HFloatConstant* GetFloatEquivalent(HIntConstant* constant) {
+HFloatConstant* SsaBuilder::GetFloatEquivalent(HIntConstant* constant) {
// We place the floating point constant next to this constant.
HFloatConstant* result = constant->GetNext()->AsFloatConstant();
if (result == nullptr) {
@@ -375,7 +375,7 @@
* is used for floating point operations. We create a floating-point equivalent
* constant to make the operations correctly typed.
*/
-static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) {
+HDoubleConstant* SsaBuilder::GetDoubleEquivalent(HLongConstant* constant) {
// We place the floating point constant next to this constant.
HDoubleConstant* result = constant->GetNext()->AsDoubleConstant();
if (result == nullptr) {
@@ -398,7 +398,7 @@
* floating point registers and core registers), we need to create a copy of the
* phi with a floating point / reference type.
*/
-static HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type) {
+HPhi* SsaBuilder::GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type) {
// We place the floating point /reference phi next to this phi.
HInstruction* next = phi->GetNext();
if (next != nullptr
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 24dc449..569b3e2 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -85,6 +85,10 @@
static constexpr const char* kSsaBuilderPassName = "ssa_builder";
private:
+ static HFloatConstant* GetFloatEquivalent(HIntConstant* constant);
+ static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant);
+ static HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type);
+
// Locals for the current block being visited.
HEnvironment* current_locals_;
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 9914ef4..5818a37 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -17,7 +17,8 @@
#ifndef ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
#define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
-#include "base/bit_vector.h"
+#include "base/arena_containers.h"
+#include "base/bit_vector-inl.h"
#include "base/value_object.h"
#include "memory_region.h"
#include "nodes.h"
@@ -40,7 +41,8 @@
stack_mask_max_(-1),
dex_pc_max_(0),
native_pc_offset_max_(0),
- number_of_stack_maps_with_inline_info_(0) {}
+ number_of_stack_maps_with_inline_info_(0),
+ dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()) {}
// Compute bytes needed to encode a mask with the given maximum element.
static uint32_t StackMaskEncodingSize(int max_element) {
@@ -59,6 +61,7 @@
size_t dex_register_locations_start_index;
size_t inline_infos_start_index;
BitVector* live_dex_registers_mask;
+ uint32_t dex_register_map_hash;
};
struct InlineInfoEntry {
@@ -80,6 +83,7 @@
entry.inlining_depth = inlining_depth;
entry.dex_register_locations_start_index = dex_register_locations_.Size();
entry.inline_infos_start_index = inline_infos_.Size();
+ entry.dex_register_map_hash = 0;
if (num_dex_registers != 0) {
entry.live_dex_registers_mask =
new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true);
@@ -105,7 +109,7 @@
inline_infos_.Add(entry);
}
- size_t ComputeNeededSize() const {
+ size_t ComputeNeededSize() {
size_t size = CodeInfo::kFixedSize
+ ComputeStackMapsSize()
+ ComputeDexRegisterMapsSize()
@@ -118,7 +122,7 @@
return StackMaskEncodingSize(stack_mask_max_);
}
- size_t ComputeStackMapsSize() const {
+ size_t ComputeStackMapsSize() {
return stack_maps_.Size() * StackMap::ComputeStackMapSize(
ComputeStackMaskSize(),
ComputeInlineInfoSize(),
@@ -146,10 +150,13 @@
}
// Compute the size of all the Dex register maps.
- size_t ComputeDexRegisterMapsSize() const {
+ size_t ComputeDexRegisterMapsSize() {
size_t size = 0;
for (size_t i = 0; i < stack_maps_.Size(); ++i) {
- size += ComputeDexRegisterMapSize(stack_maps_.Get(i));
+ if (FindEntryWithTheSameDexMap(i) == kNoSameDexMapFound) {
+ // Entries with the same dex map will have the same offset.
+ size += ComputeDexRegisterMapSize(stack_maps_.Get(i));
+ }
}
return size;
}
@@ -161,11 +168,11 @@
+ (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize);
}
- size_t ComputeDexRegisterMapsStart() const {
+ size_t ComputeDexRegisterMapsStart() {
return CodeInfo::kFixedSize + ComputeStackMapsSize();
}
- size_t ComputeInlineInfoStart() const {
+ size_t ComputeInlineInfoStart() {
return ComputeDexRegisterMapsStart() + ComputeDexRegisterMapsSize();
}
@@ -206,38 +213,47 @@
stack_map.SetStackMask(code_info, *entry.sp_mask);
}
- if (entry.num_dex_registers != 0) {
- // Set the Dex register map.
- MemoryRegion register_region =
- dex_register_locations_region.Subregion(
- next_dex_register_map_offset,
- ComputeDexRegisterMapSize(entry));
- next_dex_register_map_offset += register_region.size();
- DexRegisterMap dex_register_map(register_region);
- stack_map.SetDexRegisterMapOffset(
+ if (entry.num_dex_registers == 0) {
+ // No dex map available.
+ stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
+ } else {
+ // Search for an entry with the same dex map.
+ size_t entry_with_same_map = FindEntryWithTheSameDexMap(i);
+ if (entry_with_same_map != kNoSameDexMapFound) {
+ // If we have a hit reuse the offset.
+ stack_map.SetDexRegisterMapOffset(code_info,
+ code_info.GetStackMapAt(entry_with_same_map).GetDexRegisterMapOffset(code_info));
+ } else {
+ // New dex registers maps should be added to the stack map.
+ MemoryRegion register_region =
+ dex_register_locations_region.Subregion(
+ next_dex_register_map_offset,
+ ComputeDexRegisterMapSize(entry));
+ next_dex_register_map_offset += register_region.size();
+ DexRegisterMap dex_register_map(register_region);
+ stack_map.SetDexRegisterMapOffset(
code_info, register_region.start() - dex_register_locations_region.start());
- // Offset in `dex_register_map` where to store the next register entry.
- size_t offset = DexRegisterMap::kFixedSize;
- dex_register_map.SetLiveBitMask(offset,
- entry.num_dex_registers,
- *entry.live_dex_registers_mask);
- offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
- for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
- dex_register_number < entry.num_dex_registers;
- ++dex_register_number) {
- if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
- DexRegisterLocation dex_register_location = dex_register_locations_.Get(
- entry.dex_register_locations_start_index + index_in_dex_register_locations);
- dex_register_map.SetRegisterInfo(offset, dex_register_location);
- offset += DexRegisterMap::EntrySize(dex_register_location);
- ++index_in_dex_register_locations;
+ // Offset in `dex_register_map` where to store the next register entry.
+ size_t offset = DexRegisterMap::kFixedSize;
+ dex_register_map.SetLiveBitMask(offset,
+ entry.num_dex_registers,
+ *entry.live_dex_registers_mask);
+ offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
+ for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
+ dex_register_number < entry.num_dex_registers;
+ ++dex_register_number) {
+ if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
+ DexRegisterLocation dex_register_location = dex_register_locations_.Get(
+ entry.dex_register_locations_start_index + index_in_dex_register_locations);
+ dex_register_map.SetRegisterInfo(offset, dex_register_location);
+ offset += DexRegisterMap::EntrySize(dex_register_location);
+ ++index_in_dex_register_locations;
+ }
}
+ // Ensure we reached the end of the Dex registers region.
+ DCHECK_EQ(offset, register_region.size());
}
- // Ensure we reached the end of the Dex registers region.
- DCHECK_EQ(offset, register_region.size());
- } else {
- stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
}
// Set the inlining info.
@@ -271,11 +287,86 @@
DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
<< DexRegisterLocation::PrettyDescriptor(kind);
dex_register_locations_.Add(DexRegisterLocation(kind, value));
- stack_maps_.Get(stack_maps_.Size() - 1).live_dex_registers_mask->SetBit(dex_register);
+ StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1);
+ entry.live_dex_registers_mask->SetBit(dex_register);
+ entry.dex_register_map_hash += (1 << dex_register);
+ entry.dex_register_map_hash += static_cast<uint32_t>(value);
+ entry.dex_register_map_hash += static_cast<uint32_t>(kind);
+ stack_maps_.Put(stack_maps_.Size() - 1, entry);
}
}
private:
+ // Returns the index of an entry with the same dex register map
+ // or kNoSameDexMapFound if no such entry exists.
+ size_t FindEntryWithTheSameDexMap(size_t entry_index) {
+ StackMapEntry entry = stack_maps_.Get(entry_index);
+ auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.dex_register_map_hash);
+ if (entries_it == dex_map_hash_to_stack_map_indices_.end()) {
+ // We don't have a perfect hash functions so we need a list to collect all stack maps
+ // which might have the same dex register map.
+ GrowableArray<uint32_t> stack_map_indices(allocator_, 1);
+ stack_map_indices.Add(entry_index);
+ dex_map_hash_to_stack_map_indices_.Put(entry.dex_register_map_hash, stack_map_indices);
+ return kNoSameDexMapFound;
+ }
+
+ // TODO: We don't need to add ourselves to the map if we can guarantee that
+ // FindEntryWithTheSameDexMap is called just once per stack map entry.
+ // A good way to do this is to cache the offset in the stack map entry. This
+ // is easier to do if we add markers when the stack map constructions begins
+ // and when it ends.
+
+ // We might have collisions, so we need to check whether or not we should
+ // add the entry to the map. `needs_to_be_added` keeps track of this.
+ bool needs_to_be_added = true;
+ size_t result = kNoSameDexMapFound;
+ for (size_t i = 0; i < entries_it->second.Size(); i++) {
+ size_t test_entry_index = entries_it->second.Get(i);
+ if (test_entry_index == entry_index) {
+ needs_to_be_added = false;
+ } else if (HaveTheSameDexMaps(stack_maps_.Get(test_entry_index), entry)) {
+ result = test_entry_index;
+ needs_to_be_added = false;
+ break;
+ }
+ }
+ if (needs_to_be_added) {
+ entries_it->second.Add(entry_index);
+ }
+ return result;
+ }
+
+ bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const {
+ if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) {
+ return true;
+ }
+ if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) {
+ return false;
+ }
+ if (a.num_dex_registers != b.num_dex_registers) {
+ return false;
+ }
+
+ int index_in_dex_register_locations = 0;
+ for (uint32_t i = 0; i < a.num_dex_registers; i++) {
+ if (a.live_dex_registers_mask->IsBitSet(i) != b.live_dex_registers_mask->IsBitSet(i)) {
+ return false;
+ }
+ if (a.live_dex_registers_mask->IsBitSet(i)) {
+ DexRegisterLocation a_loc = dex_register_locations_.Get(
+ a.dex_register_locations_start_index + index_in_dex_register_locations);
+ DexRegisterLocation b_loc = dex_register_locations_.Get(
+ b.dex_register_locations_start_index + index_in_dex_register_locations);
+ if (a_loc != b_loc) {
+ return false;
+ }
+ ++index_in_dex_register_locations;
+ }
+ }
+ return true;
+ }
+
ArenaAllocator* allocator_;
GrowableArray<StackMapEntry> stack_maps_;
GrowableArray<DexRegisterLocation> dex_register_locations_;
@@ -285,6 +376,10 @@
uint32_t native_pc_offset_max_;
size_t number_of_stack_maps_with_inline_info_;
+ ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_;
+
+ static constexpr uint32_t kNoSameDexMapFound = -1;
+
ART_FRIEND_TEST(StackMapTest, Test1);
ART_FRIEND_TEST(StackMapTest, Test2);
ART_FRIEND_TEST(StackMapTest, TestNonLiveDexRegisters);
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index e7075c0..e5a9790 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -231,4 +231,54 @@
ASSERT_EQ(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMapSmallEncoding);
}
+TEST(StackMapTest, TestShareDexRegisterMap) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ StackMapStream stream(&arena);
+
+ ArenaBitVector sp_mask(&arena, 0, false);
+ uint32_t number_of_dex_registers = 2;
+ // First stack map.
+ stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
+ stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+ // Second stack map, which should share the same dex register map.
+ stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
+ stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+ // Third stack map (doesn't share the dex register map).
+ stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+ stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 2);
+ stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+
+ size_t size = stream.ComputeNeededSize();
+ void* memory = arena.Alloc(size, kArenaAllocMisc);
+ MemoryRegion region(memory, size);
+ stream.FillIn(region);
+
+ CodeInfo ci(region);
+ // Verify first stack map.
+ StackMap sm0 = ci.GetStackMapAt(0);
+ DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers);
+ ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers));
+ ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers));
+
+ // Verify second stack map.
+ StackMap sm1 = ci.GetStackMapAt(1);
+ DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers);
+ ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers));
+ ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers));
+
+ // Verify third stack map.
+ StackMap sm2 = ci.GetStackMapAt(2);
+ DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers);
+ ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers));
+ ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers));
+
+ // Verify dex register map offsets.
+ ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci));
+ ASSERT_NE(sm0.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
+ ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 8730f52..dd0dba2 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -100,6 +100,10 @@
return rm_;
}
+ Register GetSecondRegister() const {
+ return rs_;
+ }
+
enum Type {
kUnknown = -1,
kRegister,
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 6d0571e..a894319 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -825,10 +825,12 @@
if (so.IsImmediate()) {
// Check special cases.
if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12))) {
- if (opcode == SUB) {
- thumb_opcode = 5U /* 0b0101 */;
- } else {
- thumb_opcode = 0;
+ if (!set_cc) {
+ if (opcode == SUB) {
+ thumb_opcode = 5U;
+ } else if (opcode == ADD) {
+ thumb_opcode = 0U;
+ }
}
uint32_t imm = so.GetImmediate();
@@ -836,13 +838,14 @@
uint32_t imm3 = (imm >> 8) & 7U /* 0b111 */;
uint32_t imm8 = imm & 0xff;
- encoding = B31 | B30 | B29 | B28 | B25 |
- thumb_opcode << 21 |
- rn << 16 |
- rd << 8 |
- i << 26 |
- imm3 << 12 |
- imm8;
+ encoding = B31 | B30 | B29 | B28 |
+ (set_cc ? B20 : B25) |
+ thumb_opcode << 21 |
+ rn << 16 |
+ rd << 8 |
+ i << 26 |
+ imm3 << 12 |
+ imm8;
} else {
// Modified immediate.
uint32_t imm = ModifiedImmediate(so.encodingThumb());
@@ -852,19 +855,19 @@
}
encoding = B31 | B30 | B29 | B28 |
thumb_opcode << 21 |
- (set_cc ? 1 : 0) << 20 |
+ (set_cc ? B20 : 0) |
rn << 16 |
rd << 8 |
imm;
}
} else if (so.IsRegister()) {
- // Register (possibly shifted)
- encoding = B31 | B30 | B29 | B27 | B25 |
- thumb_opcode << 21 |
- (set_cc ? 1 : 0) << 20 |
- rn << 16 |
- rd << 8 |
- so.encodingThumb();
+ // Register (possibly shifted)
+ encoding = B31 | B30 | B29 | B27 | B25 |
+ thumb_opcode << 21 |
+ (set_cc ? B20 : 0) |
+ rn << 16 |
+ rd << 8 |
+ so.encodingThumb();
}
Emit32(encoding);
}
@@ -921,6 +924,8 @@
use_immediate = true;
immediate = so.GetImmediate();
} else {
+ CHECK(!(so.IsRegister() && so.IsShift() && so.GetSecondRegister() != kNoRegister))
+ << "No register-shifted register instruction available in thumb";
// Adjust rn and rd: only two registers will be emitted.
switch (opcode) {
case AND:
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index ebea9d4..813996b 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -227,4 +227,24 @@
DriverStr(expected, "abs");
}
+TEST_F(AssemblerThumb2Test, sub) {
+ __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
+ __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
+
+ const char* expected =
+ "subs r1, r0, #42\n"
+ "subw r1, r0, #42\n";
+ DriverStr(expected, "sub");
+}
+
+TEST_F(AssemblerThumb2Test, add) {
+ __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
+ __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
+
+ const char* expected =
+ "adds r1, r0, #42\n"
+ "addw r1, r0, #42\n";
+ DriverStr(expected, "add");
+}
+
} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 14bcd4b..daca971 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1453,7 +1453,9 @@
std::string error_msg;
const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
if (oat_file == nullptr) {
- oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
+ oat_file = OatFile::Open(oat_location, oat_location,
+ nullptr, nullptr, false, nullptr,
+ &error_msg);
if (oat_file == nullptr) {
os << "NOT FOUND: " << error_msg << "\n";
return false;
@@ -2193,7 +2195,7 @@
std::ostream* os) {
std::string error_msg;
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- &error_msg);
+ nullptr, &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2209,7 +2211,7 @@
static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
std::string error_msg;
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- &error_msg);
+ nullptr, &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h
index ceff6e8..e6fe6c0 100644
--- a/runtime/base/arena_containers.h
+++ b/runtime/base/arena_containers.h
@@ -85,8 +85,7 @@
typedef ArenaAllocatorAdapterKindImpl<kArenaAllocatorCountAllocations> ArenaAllocatorAdapterKind;
template <>
-class ArenaAllocatorAdapter<void>
- : private DebugStackReference, private ArenaAllocatorAdapterKind {
+class ArenaAllocatorAdapter<void> : private ArenaAllocatorAdapterKind {
public:
typedef void value_type;
typedef void* pointer;
@@ -99,14 +98,12 @@
explicit ArenaAllocatorAdapter(ArenaAllocator* arena_allocator,
ArenaAllocKind kind = kArenaAllocSTL)
- : DebugStackReference(arena_allocator),
- ArenaAllocatorAdapterKind(kind),
+ : ArenaAllocatorAdapterKind(kind),
arena_allocator_(arena_allocator) {
}
template <typename U>
ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other)
- : DebugStackReference(other),
- ArenaAllocatorAdapterKind(other),
+ : ArenaAllocatorAdapterKind(other),
arena_allocator_(other.arena_allocator_) {
}
ArenaAllocatorAdapter(const ArenaAllocatorAdapter&) = default;
@@ -121,7 +118,7 @@
};
template <typename T>
-class ArenaAllocatorAdapter : private DebugStackReference, private ArenaAllocatorAdapterKind {
+class ArenaAllocatorAdapter : private ArenaAllocatorAdapterKind {
public:
typedef T value_type;
typedef T* pointer;
@@ -137,14 +134,12 @@
};
explicit ArenaAllocatorAdapter(ArenaAllocator* arena_allocator, ArenaAllocKind kind)
- : DebugStackReference(arena_allocator),
- ArenaAllocatorAdapterKind(kind),
+ : ArenaAllocatorAdapterKind(kind),
arena_allocator_(arena_allocator) {
}
template <typename U>
ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other)
- : DebugStackReference(other),
- ArenaAllocatorAdapterKind(other),
+ : ArenaAllocatorAdapterKind(other),
arena_allocator_(other.arena_allocator_) {
}
ArenaAllocatorAdapter(const ArenaAllocatorAdapter&) = default;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index c8ede48..da39573 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -421,15 +421,26 @@
}
}
- std::string GetBaseLocation() const {
- size_t pos = location_.rfind(kMultiDexSeparator);
+ static std::string GetBaseLocation(const std::string& location) {
+ return GetBaseLocation(location.c_str());
+ }
+
+ // Returns the ':classes*.dex' part of the dex location. Returns an empty
+ // string if there is no multidex suffix for the given location.
+ // The kMultiDexSeparator is included in the returned suffix.
+ static std::string GetMultiDexSuffix(const std::string& location) {
+ size_t pos = location.rfind(kMultiDexSeparator);
if (pos == std::string::npos) {
- return location_;
+ return "";
} else {
- return location_.substr(0, pos);
+ return location.substr(pos);
}
}
+ std::string GetBaseLocation() const {
+ return GetBaseLocation(location_);
+ }
+
// For DexFiles directly from .dex files, this is the checksum from the DexFile::Header.
// For DexFiles opened from a zip files, this will be the ZipEntry CRC32 of classes.dex.
uint32_t GetLocationChecksum() const {
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 7f5a181..09ef3ee 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -377,4 +377,13 @@
ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
}
+TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) {
+ EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes2.dex"));
+ EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes8.dex"));
+ EXPECT_EQ("", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar"));
+ EXPECT_EQ(":classes2.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes2.dex"));
+ EXPECT_EQ(":classes8.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes8.dex"));
+}
+
} // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 14f770d..1fb3252 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -788,7 +788,8 @@
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
image_header.GetOatFileBegin(),
- !Runtime::Current()->IsAotCompiler(), error_msg);
+ !Runtime::Current()->IsAotCompiler(),
+ nullptr, error_msg);
if (oat_file == NULL) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
oat_filename.c_str(), GetName(), error_msg->c_str());
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 356e3d2..69cb22d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -17,10 +17,11 @@
#include "oat_file.h"
#include <dlfcn.h>
-#include <sstream>
#include <string.h>
#include <unistd.h>
+#include <sstream>
+
#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
@@ -38,12 +39,33 @@
namespace art {
+std::string OatFile::ResolveRelativeEncodedDexLocation(
+ const char* abs_dex_location, const std::string& rel_dex_location) {
+ if (abs_dex_location != nullptr && rel_dex_location[0] != '/') {
+ // Strip :classes<N>.dex used for secondary multidex files.
+ std::string base = DexFile::GetBaseLocation(rel_dex_location);
+ std::string multidex_suffix = DexFile::GetMultiDexSuffix(rel_dex_location);
+
+ // Check if the base is a suffix of the provided abs_dex_location.
+ std::string target_suffix = "/" + base;
+ std::string abs_location(abs_dex_location);
+ if (abs_location.size() > target_suffix.size()) {
+ size_t pos = abs_location.size() - target_suffix.size();
+ if (abs_location.compare(pos, std::string::npos, target_suffix) == 0) {
+ return abs_location + multidex_suffix;
+ }
+ }
+ }
+ return rel_dex_location;
+}
+
void OatFile::CheckLocation(const std::string& location) {
CHECK(!location.empty());
}
OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file,
const std::string& location,
+ const char* abs_dex_location,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(new OatFile(location, false));
oat_file->elf_file_.reset(elf_file);
@@ -53,7 +75,7 @@
oat_file->begin_ = elf_file->Begin() + offset;
oat_file->end_ = elf_file->Begin() + size + offset;
// Ignore the optional .bss section when opening non-executable.
- return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
+ return oat_file->Setup(abs_dex_location, error_msg) ? oat_file.release() : nullptr;
}
OatFile* OatFile::Open(const std::string& filename,
@@ -61,6 +83,7 @@
uint8_t* requested_base,
uint8_t* oat_file_begin,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg) {
CHECK(!filename.empty()) << location;
CheckLocation(location);
@@ -80,7 +103,7 @@
return nullptr;
}
ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
- error_msg));
+ abs_dex_location, error_msg));
// It would be nice to unlink here. But we might have opened the file created by the
// ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
@@ -88,14 +111,18 @@
return ret.release();
}
-OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
+OatFile* OatFile::OpenWritable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, true, false, abs_dex_location, error_msg);
}
-OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
+OatFile* OatFile::OpenReadable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, false, false, abs_dex_location, error_msg);
}
OatFile* OatFile::OpenElfFile(File* file,
@@ -104,10 +131,11 @@
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
- error_msg);
+ abs_dex_location, error_msg);
if (!success) {
CHECK(!error_msg->empty());
return nullptr;
@@ -131,6 +159,7 @@
bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin,
bool writable, bool executable,
+ const char* abs_dex_location,
std::string* error_msg) {
// TODO: rename requested_base to oat_data_begin
elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
@@ -180,10 +209,10 @@
bss_end_ += sizeof(uint32_t);
}
- return Setup(error_msg);
+ return Setup(abs_dex_location, error_msg);
}
-bool OatFile::Setup(std::string* error_msg) {
+bool OatFile::Setup(const char* abs_dex_location, std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
std::string cause = GetOatHeader().GetValidationErrorMessage();
*error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),
@@ -230,7 +259,9 @@
return false;
}
- std::string dex_file_location(dex_file_location_data, dex_file_location_size);
+ std::string dex_file_location = ResolveRelativeEncodedDexLocation(
+ abs_dex_location,
+ std::string(dex_file_location_data, dex_file_location_size));
uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
oat += sizeof(dex_file_checksum);
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 564185c..51952f3 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -43,14 +43,18 @@
// Opens an oat file contained within the given elf file. This is always opened as
// non-executable at the moment.
static OatFile* OpenWithElfFile(ElfFile* elf_file, const std::string& location,
+ const char* abs_dex_location,
std::string* error_msg);
// Open an oat file. Returns NULL on failure. Requested base can
// optionally be used to request where the file should be loaded.
+ // See the ResolveRelativeEncodedDexLocation for a description of how the
+ // abs_dex_location argument is used.
static OatFile* Open(const std::string& filename,
const std::string& location,
uint8_t* requested_base,
uint8_t* oat_file_begin,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg);
// Open an oat file from an already opened File.
@@ -58,9 +62,13 @@
// where relocations may be required. Currently used from
// ImageWriter which wants to open a writable version from an existing
// file descriptor for patching.
- static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
+ static OatFile* OpenWritable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg);
// Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
- static OatFile* OpenReadable(File* file, const std::string& location, std::string* error_msg);
+ static OatFile* OpenReadable(File* file, const std::string& location,
+ const char* abs_dex_location,
+ std::string* error_msg);
~OatFile();
@@ -279,6 +287,18 @@
const uint8_t* BssBegin() const;
const uint8_t* BssEnd() const;
+ // Returns the absolute dex location for the encoded relative dex location.
+ //
+ // If not nullptr, abs_dex_location is used to resolve the absolute dex
+ // location of relative dex locations encoded in the oat file.
+ // For example, given absolute location "/data/app/foo/base.apk", encoded
+ // dex locations "base.apk", "base.apk:classes2.dex", etc. would be resolved
+ // to "/data/app/foo/base.apk", "/data/app/foo/base.apk:classes2.dex", etc.
+ // Relative encoded dex locations that don't match the given abs_dex_location
+ // are left unchanged.
+ static std::string ResolveRelativeEncodedDexLocation(
+ const char* abs_dex_location, const std::string& rel_dex_location);
+
private:
static void CheckLocation(const std::string& location);
@@ -288,14 +308,17 @@
uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
+ const char* abs_dex_location,
std::string* error_msg);
explicit OatFile(const std::string& filename, bool executable);
bool ElfFileOpen(File* file, uint8_t* requested_base,
uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable, bool executable,
+ const char* abs_dex_location,
std::string* error_msg);
- bool Setup(std::string* error_msg);
+
+ bool Setup(const char* abs_dex_location, std::string* error_msg);
// The oat file name.
//
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f87fa4f..d92f59b 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -737,18 +737,19 @@
CHECK(error_msg != nullptr);
// The odex file name is formed by replacing the dex_location extension with
- // .odex and inserting an isa directory. For example:
+ // .odex and inserting an oat/<isa> directory. For example:
// location = /foo/bar/baz.jar
- // odex_location = /foo/bar/<isa>/baz.odex
+ // odex_location = /foo/bar/oat/<isa>/baz.odex
- // Find the directory portion of the dex location and add the isa directory.
+ // Find the directory portion of the dex location and add the oat/<isa>
+ // directory.
size_t pos = location.rfind('/');
if (pos == std::string::npos) {
*error_msg = "Dex location " + location + " has no directory.";
return false;
}
std::string dir = location.substr(0, pos+1);
- dir += std::string(GetInstructionSetString(isa));
+ dir += "oat/" + std::string(GetInstructionSetString(isa));
// Find the file portion of the dex location.
std::string file;
@@ -850,7 +851,7 @@
std::string error_msg;
cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
odex_file_name.c_str(), nullptr, nullptr, load_executable_,
- &error_msg));
+ dex_location_, &error_msg));
if (cached_odex_file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
<< odex_file_name << ": " << error_msg;
@@ -875,7 +876,8 @@
const std::string& oat_file_name = *OatFileName();
std::string error_msg;
cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
- oat_file_name.c_str(), nullptr, nullptr, load_executable_, &error_msg));
+ oat_file_name.c_str(), nullptr, nullptr, load_executable_,
+ dex_location_, &error_msg));
if (cached_oat_file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing oat file "
<< oat_file_name << ": " << error_msg;
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 958b440..f2abcf9 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -151,11 +151,12 @@
static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
const OatFile& oat_file, const char* dex_location);
- // If the dex file has been pre-compiled on the host, the compiled oat file
- // will have the extension .odex, and is referred to as the odex file.
- // It is called odex for legacy reasons; the file is really an oat file. The
- // odex file will typically have a patch delta of 0 and need to be relocated
- // before use for the purposes of ASLR.
+ // If the dex file has been installed with a compiled oat file alongside
+ // it, the compiled oat file will have the extension .odex, and is referred
+ // to as the odex file. It is called odex for legacy reasons; the file is
+ // really an oat file. The odex file will often, but not always, have a
+ // patch delta of 0 and need to be relocated before use for the purposes of
+ // ASLR. The odex file is treated as if it were read-only.
// These methods return the location and status of the odex file for the dex
// location.
// Notes:
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index be8652c..b2798d3 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -44,10 +44,13 @@
scratch_dir_ = android_data_ + "/OatFileAssistantTest";
ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
- // Create a subdirectory in scratch for the current isa.
- // This is the location that will be used for odex files in the tests.
- isa_dir_ = scratch_dir_ + "/" + GetInstructionSetString(kRuntimeISA);
- ASSERT_EQ(0, mkdir(isa_dir_.c_str(), 0700));
+ // Create a subdirectory in scratch for odex files.
+ odex_oat_dir_ = scratch_dir_ + "/oat";
+ ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700));
+
+ odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
+ ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
+
// Verify the environment is as we expect
uint32_t checksum;
@@ -90,8 +93,11 @@
}
virtual void TearDown() {
- ClearDirectory(isa_dir_.c_str());
- ASSERT_EQ(0, rmdir(isa_dir_.c_str()));
+ ClearDirectory(odex_dir_.c_str());
+ ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
+
+ ClearDirectory(odex_oat_dir_.c_str());
+ ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
ClearDirectory(scratch_dir_.c_str());
ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
@@ -153,10 +159,10 @@
return scratch_dir_;
}
- // ISA directory is the subdirectory in the scratch directory where odex
+ // Odex directory is the subdirectory in the scratch directory where odex
// files should be located.
- std::string GetISADir() {
- return isa_dir_;
+ std::string GetOdexDir() {
+ return odex_dir_;
}
// Generate an odex file for the purposes of test.
@@ -241,7 +247,8 @@
}
std::string scratch_dir_;
- std::string isa_dir_;
+ std::string odex_oat_dir_;
+ std::string odex_dir_;
std::vector<std::unique_ptr<MemMap>> image_reservation_;
};
@@ -326,12 +333,43 @@
GenerateOatForTest(dex_location.c_str());
// Verify we can load both dex files.
- OatFileAssistant executable_oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- std::unique_ptr<OatFile> oat_file = executable_oat_file_assistant.GetBestOatFile();
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
EXPECT_TRUE(oat_file->IsExecutable());
std::vector<std::unique_ptr<const DexFile>> dex_files;
- dex_files = executable_oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(2u, dex_files.size());
+}
+
+// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
+// encoded dex locations.
+// Expect: The oat file status is kUpToDate.
+TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
+ std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
+ std::string oat_location = GetOdexDir() + "/RelativeEncodedDexLocation.oat";
+
+ // Create the dex file
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ // Create the oat file with relative encoded dex location.
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar"));
+ args.push_back("--oat-file=" + oat_location);
+
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Verify we can load both dex files.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ oat_location.c_str(),
+ kRuntimeISA, true);
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
}
@@ -362,7 +400,7 @@
// Expect: The oat file status is kNeedsRelocation.
TEST_F(OatFileAssistantTest, DexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
- std::string odex_location = GetISADir() + "/DexOdexNoOat.odex";
+ std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
@@ -388,7 +426,7 @@
// Expect: The oat file status is kNeedsRelocation.
TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
- std::string odex_location = GetISADir() + "/StrippedDexOdexNoOat.odex";
+ std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
@@ -437,7 +475,7 @@
// Expect: The oat file status is kNeedsRelocation.
TEST_F(OatFileAssistantTest, StrippedDexOdexOat) {
std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
- std::string odex_location = GetISADir() + "/StrippedDexOdexOat.odex";
+ std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex";
// Create the oat file from a different dex file so it looks out of date.
Copy(GetDexSrc2(), dex_location);
@@ -494,8 +532,8 @@
// Expect: It shouldn't crash.
TEST_F(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
- std::string odex_location = GetISADir() + "/OdexOatOverlap.odex";
- std::string oat_location = GetISADir() + "/OdexOatOverlap.oat";
+ std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
+ std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
@@ -532,7 +570,7 @@
// Expect: The oat file status is kUpToDate, because PIC needs no relocation.
TEST_F(OatFileAssistantTest, DexPicOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar";
- std::string odex_location = GetISADir() + "/DexPicOdexNoOat.odex";
+ std::string odex_location = GetOdexDir() + "/DexPicOdexNoOat.odex";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
@@ -772,7 +810,7 @@
// avoid using up the virtual memory address space.
TEST_F(OatFileAssistantTest, RaceToGenerate) {
std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
- std::string oat_location = GetISADir() + "/RaceToGenerate.oat";
+ std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
// We use the lib core dex file, because it's large, and hopefully should
// take a while to generate.
@@ -802,7 +840,7 @@
// Expect: We should load the odex file non-executable.
TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar";
- std::string odex_location = GetISADir() + "/LoadDexOdexNoOat.odex";
+ std::string odex_location = GetOdexDir() + "/LoadDexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
@@ -824,7 +862,7 @@
// Expect: We should load the odex file non-executable.
TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar";
- std::string odex_location = GetISADir() + "/LoadMultiDexOdexNoOat.odex";
+ std::string odex_location = GetOdexDir() + "/LoadMultiDexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetMultiDexSrc1(), dex_location);
@@ -847,11 +885,11 @@
EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename(
"/foo/bar/baz.jar", kArm, &odex_file, &error_msg)) << error_msg;
- EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file);
+ EXPECT_EQ("/foo/bar/oat/arm/baz.odex", odex_file);
EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename(
"/foo/bar/baz.funnyext", kArm, &odex_file, &error_msg)) << error_msg;
- EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file);
+ EXPECT_EQ("/foo/bar/oat/arm/baz.odex", odex_file);
EXPECT_FALSE(OatFileAssistant::DexFilenameToOdexFilename(
"nopath.jar", kArm, &odex_file, &error_msg));
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
new file mode 100644
index 0000000..f2213e9
--- /dev/null
+++ b/runtime/oat_file_test.cc
@@ -0,0 +1,59 @@
+/*
+ * 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 "oat_file.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+TEST(OatFileTest, ResolveRelativeEncodedDexLocation) {
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ nullptr, "/data/app/foo/base.apk"));
+
+ EXPECT_EQ(std::string("/system/framework/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "/system/framework/base.apk"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "base.apk"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "foo/base.apk"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk:classes2.dex"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "base.apk:classes2.dex"));
+
+ EXPECT_EQ(std::string("/data/app/foo/base.apk:classes11.dex"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "base.apk:classes11.dex"));
+
+ EXPECT_EQ(std::string("base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/sludge.apk", "base.apk"));
+
+ EXPECT_EQ(std::string("o/base.apk"),
+ OatFile::ResolveRelativeEncodedDexLocation(
+ "/data/app/foo/base.apk", "o/base.apk"));
+}
+
+} // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2dacfe2..9ca00b1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -688,7 +688,7 @@
return false;
}
std::unique_ptr<OatFile> oat_file(OatFile::OpenWithElfFile(elf_file.release(), oat_location,
- &error_msg));
+ nullptr, &error_msg));
if (oat_file.get() == nullptr) {
LOG(INFO) << "Unable to use '" << oat_filename << "' because " << error_msg;
return false;
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 629fc9a..6ec7cc8 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -212,6 +212,14 @@
// Get the actual kind of the location.
Kind GetInternalKind() const { return kind_; }
+ bool operator==(DexRegisterLocation other) const {
+ return kind_ == other.kind_ && value_ == other.value_;
+ }
+
+ bool operator!=(DexRegisterLocation other) const {
+ return !(*this == other);
+ }
+
private:
Kind kind_;
int32_t value_;
diff --git a/runtime/utils.h b/runtime/utils.h
index e20412e..1a7fdfb 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -110,6 +110,7 @@
// Check whether an N-bit two's-complement representation can hold value.
static inline bool IsInt(int N, intptr_t value) {
+ if (N == kBitsPerIntPtrT) return true;
CHECK_LT(0, N);
CHECK_LT(N, kBitsPerIntPtrT);
intptr_t limit = static_cast<intptr_t>(1) << (N - 1);
diff --git a/test/462-checker-inlining-across-dex-files/expected.txt b/test/462-checker-inlining-across-dex-files/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/expected.txt
diff --git a/test/462-checker-inlining-across-dex-files/info.txt b/test/462-checker-inlining-across-dex-files/info.txt
new file mode 100644
index 0000000..57008c3
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/info.txt
@@ -0,0 +1 @@
+Check our inlining heuristics across dex files in optimizing.
diff --git a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
new file mode 100644
index 0000000..cee8e0f
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
@@ -0,0 +1,64 @@
+/*
+* 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.
+*/
+
+public class OtherDex {
+ public static void emptyMethod() {
+ }
+
+ public static int returnIntMethod() {
+ return 38;
+ }
+
+ public static int returnOtherDexStatic() {
+ return myStatic;
+ }
+
+ public static int returnMainStatic() {
+ return Main.myStatic;
+ }
+
+ public static int recursiveCall() {
+ return recursiveCall();
+ }
+
+ public static String returnString() {
+ return "OtherDex";
+ }
+
+ public static Class returnOtherDexClass() {
+ return OtherDex.class;
+ }
+
+ public static Class returnMainClass() {
+ return Main.class;
+ }
+
+ private static Class returnOtherDexClass2() {
+ return OtherDex.class;
+ }
+
+ public static Class returnOtherDexClassStaticCall() {
+ // Do not call returnOtherDexClass, as it may have been flagged
+ // as non-inlineable.
+ return returnOtherDexClass2();
+ }
+
+ public static Class returnOtherDexCallingMain() {
+ return Main.getOtherClass();
+ }
+
+ static int myStatic = 1;
+}
diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java
new file mode 100644
index 0000000..d5563b8
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/src/Main.java
@@ -0,0 +1,202 @@
+/*
+* 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.
+*/
+
+// Add a class that will be the first entry in the dex cache, to
+// avoid having the OtherDex and Main classes share the same cache index.
+class AAA {
+}
+
+public class Main {
+
+ // CHECK-START: void Main.inlineEmptyMethod() inliner (before)
+ // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: ReturnVoid
+
+ // CHECK-START: void Main.inlineEmptyMethod() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static void inlineEmptyMethod() {
+ OtherDex.emptyMethod();
+ }
+
+ // CHECK-START: int Main.inlineReturnIntMethod() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
+ // CHECK-DAG: [[Const38:i\d+]] IntConstant 38
+ // CHECK-DAG: Return [ [[Const38]] ]
+
+ public static int inlineReturnIntMethod() {
+ return OtherDex.returnIntMethod();
+ }
+
+ // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (after)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ public static int dontInlineOtherDexStatic() {
+ return OtherDex.returnOtherDexStatic();
+ }
+
+ // CHECK-START: int Main.inlineMainStatic() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineMainStatic() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineMainStatic() inliner (after)
+ // CHECK-DAG: [[Static:i\d+]] StaticFieldGet
+ // CHECK-DAG: Return [ [[Static]] ]
+
+ public static int inlineMainStatic() {
+ return OtherDex.returnMainStatic();
+ }
+
+ // CHECK-START: int Main.dontInlineRecursiveCall() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.dontInlineRecursiveCall() inliner (after)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ public static int dontInlineRecursiveCall() {
+ return OtherDex.recursiveCall();
+ }
+
+ // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (after)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ public static String dontInlineReturnString() {
+ return OtherDex.returnString();
+ }
+
+ // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (after)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ public static Class dontInlineOtherDexClass() {
+ return OtherDex.returnOtherDexClass();
+ }
+
+ // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
+ // CHECK-DAG: [[Class:l\d+]] LoadClass
+ // CHECK-DAG: Return [ [[Class]] ]
+
+ public static Class inlineMainClass() {
+ return OtherDex.returnMainClass();
+ }
+
+ // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (after)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ public static Class dontInlineOtherDexClassStaticCall() {
+ return OtherDex.returnOtherDexClassStaticCall();
+ }
+
+ // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (before)
+ // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
+ // CHECK-DAG: [[Class:l\d+]] LoadClass
+ // CHECK-DAG: Return [ [[Class]] ]
+
+ public static Class inlineOtherDexCallingMain() {
+ return OtherDex.returnOtherDexCallingMain();
+ }
+
+ public static Class getOtherClass() {
+ return Main.class;
+ }
+
+ public static void main(String[] args) {
+ inlineEmptyMethod();
+ if (inlineReturnIntMethod() != 38) {
+ throw new Error("Expected 38");
+ }
+
+ if (dontInlineOtherDexStatic() != 1) {
+ throw new Error("Expected 1");
+ }
+
+ if (inlineMainStatic() != 42) {
+ throw new Error("Expected 42");
+ }
+
+ if (dontInlineReturnString() != "OtherDex") {
+ throw new Error("Expected OtherDex");
+ }
+
+ if (dontInlineOtherDexClass() != OtherDex.class) {
+ throw new Error("Expected " + OtherDex.class);
+ }
+
+ if (dontInlineOtherDexClassStaticCall() != OtherDex.class) {
+ throw new Error("Expected " + OtherDex.class);
+ }
+
+ if (inlineMainClass() != Main.class) {
+ throw new Error("Expected " + Main.class);
+ }
+
+ if (inlineOtherDexCallingMain() != Main.class) {
+ throw new Error("Expected " + Main.class);
+ }
+ }
+
+ // Reference the AAA class to ensure it is in the dex cache.
+ public static Class<?> cls = AAA.class;
+
+ // Add a field that will be the first entry in the dex cache, to
+ // avoid having the OtherDex.myStatic and Main.myStatic fields
+ // share the same cache index.
+ public static int aaa = 32;
+ public static int myStatic = 42;
+}
diff --git a/test/464-checker-inline-sharpen-calls/expected.txt b/test/464-checker-inline-sharpen-calls/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/464-checker-inline-sharpen-calls/expected.txt
diff --git a/test/464-checker-inline-sharpen-calls/info.txt b/test/464-checker-inline-sharpen-calls/info.txt
new file mode 100644
index 0000000..9e56030
--- /dev/null
+++ b/test/464-checker-inline-sharpen-calls/info.txt
@@ -0,0 +1 @@
+Check that we inline sharpen calls.
diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java
new file mode 100644
index 0000000..1b25b42
--- /dev/null
+++ b/test/464-checker-inline-sharpen-calls/src/Main.java
@@ -0,0 +1,54 @@
+/*
+* 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.
+*/
+
+public final class Main {
+
+ public void invokeVirtual() {
+ }
+
+ // CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (before)
+ // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: ReturnVoid
+
+ // CHECK-START: void Main.inlineSharpenInvokeVirtual(Main) inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ public static void inlineSharpenInvokeVirtual(Main m) {
+ m.invokeVirtual();
+ }
+
+ // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (before)
+ // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect
+ // CHECK-DAG: Return [ [[Invoke]] ]
+
+ // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
+ // CHECK-NOT: InvokeStaticOrDirect
+
+ // CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after)
+ // CHECK-DAG: [[Field:i\d+]] InstanceFieldGet
+ // CHECK-DAG: Return [ [[Field]] ]
+
+ public static int inlineSharpenStringInvoke() {
+ return "Foo".length();
+ }
+
+ public static void main(String[] args) {
+ inlineSharpenInvokeVirtual(new Main());
+ if (inlineSharpenStringInvoke() != 3) {
+ throw new Error("Expected 3");
+ }
+ }
+}
diff --git a/test/etc/default-build b/test/etc/default-build
index 58c9564..fbe97f9 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -39,7 +39,7 @@
fi
mkdir classes
-${JAVAC} -d classes `find src -name '*.java'`
+${JAVAC} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
if [ -d src2 ]; then
${JAVAC} -d classes `find src2 -name '*.java'`
@@ -72,6 +72,15 @@
fi
fi
-if [ ${NEED_DEX} = "true" ]; then
+# Create a single jar with two dex files for multidex.
+if [ -d src-multidex ]; then
+ mkdir classes2
+ ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
+ --dump-width=1000 ${DX_FLAGS} classes2
+ zip $TEST_NAME.jar classes.dex classes2.dex
+ fi
+elif [ ${NEED_DEX} = "true" ]; then
zip $TEST_NAME.jar classes.dex
fi
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index b87b2ff..30d99bb 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -74,7 +74,11 @@
description: "Failing due to missing localhost on hammerhead and volantis.",
result: EXEC_FAILED,
modes: [device],
- names: ["libcore.javax.crypto.CipherTest#testCipherInitWithCertificate"]
+ names: ["libcore.javax.crypto.CipherTest#testCipherInitWithCertificate",
+ "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithFtpURLConnection",
+ "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarFtpURLConnection",
+ "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithLoggingSocketHandler"
+ ]
},
{
description: "Test timeouts",
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index fc396b6..7faf86e 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -24,5 +24,9 @@
adb shell setenforce 0
adb shell getenforce
+echo -e "${green}Setting local loopback${nc}"
+adb shell ifconfig lo up
+adb shell ifconfig
+
echo -e "${green}List properties${nc}"
adb shell getprop