summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/Android.mk5
-rw-r--r--compiler/debug/elf_debug_loc_writer.h27
-rw-r--r--compiler/dex/mir_optimization.cc45
-rw-r--r--compiler/dex/quick/quick_compiler.cc12
-rw-r--r--compiler/dex/quick/quick_compiler.h2
-rw-r--r--compiler/dex/verified_method.cc10
-rw-r--r--compiler/dex/verified_method.h12
-rw-r--r--compiler/driver/compiled_method_storage.cc3
-rw-r--r--compiler/driver/compiler_driver-inl.h8
-rw-r--r--compiler/driver/compiler_driver.cc24
-rw-r--r--compiler/driver/compiler_driver.h1
-rw-r--r--compiler/image_writer.cc149
-rw-r--r--compiler/image_writer.h16
-rw-r--r--compiler/jit/jit_compiler.cc17
-rw-r--r--compiler/oat_test.cc4
-rw-r--r--compiler/optimizing/bounds_check_elimination.cc24
-rw-r--r--compiler/optimizing/code_generator_mips.cc30
-rw-r--r--compiler/optimizing/code_generator_mips.h5
-rw-r--r--compiler/optimizing/code_generator_mips64.cc6
-rw-r--r--compiler/optimizing/code_generator_mips64.h2
-rw-r--r--compiler/optimizing/code_generator_x86.cc163
-rw-r--r--compiler/optimizing/code_generator_x86.h1
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc4
-rw-r--r--compiler/optimizing/constant_folding.cc126
-rw-r--r--compiler/optimizing/constant_folding.h9
-rw-r--r--compiler/optimizing/graph_checker.cc5
-rw-r--r--compiler/optimizing/graph_visualizer.cc7
-rw-r--r--compiler/optimizing/inliner.cc352
-rw-r--r--compiler/optimizing/inliner.h85
-rw-r--r--compiler/optimizing/instruction_simplifier.cc14
-rw-r--r--compiler/optimizing/instruction_simplifier.h7
-rw-r--r--compiler/optimizing/intrinsics.cc1
-rw-r--r--compiler/optimizing/intrinsics.h4
-rw-r--r--compiler/optimizing/intrinsics_arm.cc4
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc4
-rw-r--r--compiler/optimizing/intrinsics_mips.cc232
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc4
-rw-r--r--compiler/optimizing/intrinsics_x86.cc14
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc4
-rw-r--r--compiler/optimizing/licm.cc1
-rw-r--r--compiler/optimizing/licm.h5
-rw-r--r--compiler/optimizing/licm_test.cc2
-rw-r--r--compiler/optimizing/nodes.cc106
-rw-r--r--compiler/optimizing/nodes.h654
-rw-r--r--compiler/optimizing/optimizing_compiler.cc8
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h10
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.cc10
-rw-r--r--compiler/optimizing/select_generator.cc2
-rw-r--r--compiler/optimizing/select_generator.h4
-rw-r--r--compiler/optimizing/ssa_builder.cc30
-rw-r--r--compiler/profile_assistant.cc169
-rw-r--r--compiler/profile_assistant.h72
-rw-r--r--compiler/profile_assistant_test.cc279
-rw-r--r--compiler/utils/mips/assembler_mips.cc20
-rw-r--r--compiler/utils/mips/assembler_mips.h4
-rw-r--r--compiler/utils/mips/assembler_mips_test.cc40
56 files changed, 1685 insertions, 1173 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk
index b16494248f..3f61e8eb1b 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -90,7 +90,6 @@ LIBART_COMPILER_SRC_FILES := \
optimizing/optimization.cc \
optimizing/optimizing_compiler.cc \
optimizing/parallel_move_resolver.cc \
- optimizing/pc_relative_fixups_x86.cc \
optimizing/prepare_for_register_allocation.cc \
optimizing/reference_type_propagation.cc \
optimizing/register_allocator.cc \
@@ -108,8 +107,7 @@ LIBART_COMPILER_SRC_FILES := \
elf_writer.cc \
elf_writer_quick.cc \
image_writer.cc \
- oat_writer.cc \
- profile_assistant.cc
+ oat_writer.cc
LIBART_COMPILER_SRC_FILES_arm := \
dex/quick/arm/assemble_arm.cc \
@@ -182,6 +180,7 @@ LIBART_COMPILER_SRC_FILES_x86 := \
linker/x86/relative_patcher_x86_base.cc \
optimizing/code_generator_x86.cc \
optimizing/intrinsics_x86.cc \
+ optimizing/pc_relative_fixups_x86.cc \
utils/x86/assembler_x86.cc \
utils/x86/managed_register_x86.cc \
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index a19b36f9cc..8fd20aa428 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
#define ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+#include <cstring>
#include <map>
#include "arch/instruction_set.h"
@@ -172,11 +173,6 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
return;
}
- dwarf::Writer<> debug_loc(debug_loc_buffer);
- dwarf::Writer<> debug_ranges(debug_ranges_buffer);
- debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc.size());
- debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges.size());
-
std::vector<VariableLocation> variable_locations = GetVariableLocations(
method_info,
vreg,
@@ -185,6 +181,8 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
dex_pc_high);
// Write .debug_loc entries.
+ dwarf::Writer<> debug_loc(debug_loc_buffer);
+ const size_t debug_loc_offset = debug_loc.size();
const bool is64bit = Is64BitInstructionSet(isa);
std::vector<uint8_t> expr_buffer;
for (const VariableLocation& variable_location : variable_locations) {
@@ -271,6 +269,8 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
// Write .debug_ranges entries.
// This includes ranges where the variable is in scope but the location is not known.
+ dwarf::Writer<> debug_ranges(debug_ranges_buffer);
+ size_t debug_ranges_offset = debug_ranges.size();
for (size_t i = 0; i < variable_locations.size(); i++) {
uint32_t low_pc = variable_locations[i].low_pc;
uint32_t high_pc = variable_locations[i].high_pc;
@@ -294,6 +294,23 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
debug_ranges.PushUint32(0);
debug_ranges.PushUint32(0);
}
+
+ // Simple de-duplication - check whether this entry is same as the last one (or tail of it).
+ size_t debug_ranges_entry_size = debug_ranges.size() - debug_ranges_offset;
+ if (debug_ranges_offset >= debug_ranges_entry_size) {
+ size_t previous_offset = debug_ranges_offset - debug_ranges_entry_size;
+ if (memcmp(debug_ranges_buffer->data() + previous_offset,
+ debug_ranges_buffer->data() + debug_ranges_offset,
+ debug_ranges_entry_size) == 0) {
+ // Remove what we have just written and use the last entry instead.
+ debug_ranges_buffer->resize(debug_ranges_offset);
+ debug_ranges_offset = previous_offset;
+ }
+ }
+
+ // Write attributes to .debug_info.
+ debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc_offset);
+ debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges_offset);
}
} // namespace debug
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index eb4915b821..6f9dd6d268 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -1679,9 +1679,7 @@ void MIRGraph::StringChange() {
if (opcode == Instruction::NEW_INSTANCE) {
uint32_t type_idx = mir->dalvikInsn.vB;
if (cu_->compiler_driver->IsStringTypeIndex(type_idx, cu_->dex_file)) {
- // Change NEW_INSTANCE into CONST_4 of 0
- mir->dalvikInsn.opcode = Instruction::CONST_4;
- mir->dalvikInsn.vB = 0;
+ LOG(FATAL) << "Quick cannot compile String allocations";
}
} else if ((opcode == Instruction::INVOKE_DIRECT) ||
(opcode == Instruction::INVOKE_DIRECT_RANGE)) {
@@ -1689,52 +1687,13 @@ void MIRGraph::StringChange() {
DexFileMethodInliner* inliner =
cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
if (inliner->IsStringInitMethodIndex(method_idx)) {
- bool is_range = (opcode == Instruction::INVOKE_DIRECT_RANGE);
- uint32_t orig_this_reg = is_range ? mir->dalvikInsn.vC : mir->dalvikInsn.arg[0];
- // Remove this pointer from string init and change to static call.
- mir->dalvikInsn.vA--;
- if (!is_range) {
- mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC;
- for (uint32_t i = 0; i < mir->dalvikInsn.vA; i++) {
- mir->dalvikInsn.arg[i] = mir->dalvikInsn.arg[i + 1];
- }
- } else {
- mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC_RANGE;
- mir->dalvikInsn.vC++;
- }
- // Insert a move-result instruction to the original this pointer reg.
- MIR* move_result_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR));
- move_result_mir->dalvikInsn.opcode = Instruction::MOVE_RESULT_OBJECT;
- move_result_mir->dalvikInsn.vA = orig_this_reg;
- move_result_mir->offset = mir->offset;
- move_result_mir->m_unit_index = mir->m_unit_index;
- bb->InsertMIRAfter(mir, move_result_mir);
- // Add additional moves if this pointer was copied to other registers.
- const VerifiedMethod* verified_method =
- cu_->compiler_driver->GetVerifiedMethod(cu_->dex_file, cu_->method_idx);
- DCHECK(verified_method != nullptr);
- const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map =
- verified_method->GetStringInitPcRegMap();
- auto map_it = string_init_map.find(mir->offset);
- if (map_it != string_init_map.end()) {
- const std::set<uint32_t>& reg_set = map_it->second;
- for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
- MIR* move_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR));
- move_mir->dalvikInsn.opcode = Instruction::MOVE_OBJECT;
- move_mir->dalvikInsn.vA = *set_it;
- move_mir->dalvikInsn.vB = orig_this_reg;
- move_mir->offset = mir->offset;
- move_mir->m_unit_index = mir->m_unit_index;
- bb->InsertMIRAfter(move_result_mir, move_mir);
- }
- }
+ LOG(FATAL) << "Quick cannot compile String allocations";
}
}
}
}
}
-
bool MIRGraph::EliminateSuspendChecksGate() {
if (kLeafOptimization || // Incompatible (could create loops without suspend checks).
(cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled.
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 027290f9b1..49768ded46 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -509,7 +509,8 @@ static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set)
}
bool QuickCompiler::CanCompileInstruction(const MIR* mir,
- const DexFile& dex_file) const {
+ const DexFile& dex_file,
+ CompilationUnit* cu) const {
switch (mir->dalvikInsn.opcode) {
// Quick compiler won't support new instruction semantics to invoke-super into an interface
// method
@@ -522,6 +523,13 @@ bool QuickCompiler::CanCompileInstruction(const MIR* mir,
// False if we are an interface i.e. !(java_access_flags & kAccInterface)
return class_def != nullptr && ((class_def->GetJavaAccessFlags() & kAccInterface) == 0);
}
+ case Instruction::NEW_INSTANCE: {
+ uint32_t type_idx = mir->dalvikInsn.vB;
+ if (cu->compiler_driver->IsStringTypeIndex(type_idx, cu->dex_file)) {
+ return false;
+ }
+ return true;
+ }
default:
return true;
}
@@ -567,7 +575,7 @@ bool QuickCompiler::CanCompileMethod(uint32_t method_idx,
<< MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst];
}
return false;
- } else if (!CanCompileInstruction(mir, dex_file)) {
+ } else if (!CanCompileInstruction(mir, dex_file, cu)) {
VLOG(compiler) << "Cannot compile dalvik opcode : " << mir->dalvikInsn.opcode;
return false;
}
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
index 55f45f1ab0..f32cf866ca 100644
--- a/compiler/dex/quick/quick_compiler.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -75,7 +75,7 @@ class QuickCompiler : public Compiler {
explicit QuickCompiler(CompilerDriver* driver);
private:
- bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file) const;
+ bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file, CompilationUnit* cu) const;
std::unique_ptr<PassManager> pre_opt_pass_manager_;
std::unique_ptr<PassManager> post_opt_pass_manager_;
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 0355f116f1..9ae21648bf 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -37,20 +37,16 @@
namespace art {
-VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types,
- bool has_runtime_throw,
- const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map)
+VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw)
: encountered_error_types_(encountered_error_types),
- has_runtime_throw_(has_runtime_throw),
- string_init_pc_reg_map_(string_init_pc_reg_map) {
+ has_runtime_throw_(has_runtime_throw) {
}
const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier,
bool compile) {
std::unique_ptr<VerifiedMethod> verified_method(
new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(),
- method_verifier->HasInstructionThatWillThrow(),
- method_verifier->GetStringInitPcRegMap()));
+ method_verifier->HasInstructionThatWillThrow()));
if (compile) {
/* Generate a register map. */
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 74fcb07d27..12d0219058 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -83,14 +83,8 @@ class VerifiedMethod {
return has_runtime_throw_;
}
- const SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() const {
- return string_init_pc_reg_map_;
- }
-
private:
- VerifiedMethod(uint32_t encountered_error_types,
- bool has_runtime_throw,
- const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map);
+ VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw);
/*
* Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
@@ -129,10 +123,6 @@ class VerifiedMethod {
const uint32_t encountered_error_types_;
const bool has_runtime_throw_;
-
- // Copy of mapping generated by verifier of dex PCs of string init invocations
- // to the set of other registers that the receiver has been copied into.
- const SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_;
};
} // namespace art
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc
index bc5c6cab87..510613ecf4 100644
--- a/compiler/driver/compiled_method_storage.cc
+++ b/compiler/driver/compiled_method_storage.cc
@@ -190,7 +190,8 @@ CompiledMethodStorage::~CompiledMethodStorage() {
void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
if (swap_space_.get() != nullptr) {
- os << " swap=" << PrettySize(swap_space_->GetSize());
+ const size_t swap_size = swap_space_->GetSize();
+ os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
}
if (extended) {
Thread* self = Thread::Current();
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 0d65bc7405..3cb63e7082 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -186,13 +186,7 @@ inline std::pair<bool, bool> CompilerDriver::IsClassOfStaticMemberAvailableToRef
} else {
// Search dex file for localized ssb index, may fail if member's class is a parent
// of the class mentioned in the dex file and there is no dex cache entry.
- std::string temp;
- const DexFile::TypeId* type_id =
- dex_file->FindTypeId(resolved_member->GetDeclaringClass()->GetDescriptor(&temp));
- if (type_id != nullptr) {
- // medium path, needs check of static storage base being initialized
- storage_idx = dex_file->GetIndexForTypeId(*type_id);
- }
+ storage_idx = resolved_member->GetDeclaringClass()->FindTypeIndexInOtherDexFile(*dex_file);
}
if (storage_idx != DexFile::kDexNoIndex) {
*storage_index = storage_idx;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f078bf6507..8ef1f28130 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -378,7 +378,6 @@ CompilerDriver::CompilerDriver(
compiled_method_storage_(swap_fd),
profile_compilation_info_(profile_compilation_info) {
DCHECK(compiler_options_ != nullptr);
- DCHECK(verification_results_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
compiler_->Init();
@@ -1078,10 +1077,8 @@ static void MaybeAddToImageClasses(Handle<mirror::Class> c,
image_classes);
}
for (auto& m : c->GetVirtualMethods(pointer_size)) {
- if (m.IsMiranda() || (true)) {
- StackHandleScope<1> hs2(self);
- MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
- }
+ StackHandleScope<1> hs2(self);
+ MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
}
if (klass->IsArrayClass()) {
StackHandleScope<1> hs2(self);
@@ -2493,6 +2490,7 @@ void CompilerDriver::Compile(jobject class_loader,
parallel_thread_pool_.get(),
parallel_thread_count_,
timings);
+ Runtime::Current()->ReclaimArenaPoolMemory();
}
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
@@ -2732,16 +2730,18 @@ bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex
std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
std::ostringstream oss;
Runtime* const runtime = Runtime::Current();
- const ArenaPool* arena_pool = runtime->GetArenaPool();
- gc::Heap* const heap = runtime->GetHeap();
- oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated());
- oss << " java alloc=" << PrettySize(heap->GetBytesAllocated());
+ const ArenaPool* const arena_pool = runtime->GetArenaPool();
+ const gc::Heap* const heap = runtime->GetHeap();
+ const size_t arena_alloc = arena_pool->GetBytesAllocated();
+ const size_t java_alloc = heap->GetBytesAllocated();
+ oss << "arena alloc=" << PrettySize(arena_alloc) << " (" << arena_alloc << "B)";
+ oss << " java alloc=" << PrettySize(java_alloc) << " (" << java_alloc << "B)";
#if defined(__BIONIC__) || defined(__GLIBC__)
- struct mallinfo info = mallinfo();
+ const struct mallinfo info = mallinfo();
const size_t allocated_space = static_cast<size_t>(info.uordblks);
const size_t free_space = static_cast<size_t>(info.fordblks);
- oss << " native alloc=" << PrettySize(allocated_space) << " free="
- << PrettySize(free_space);
+ oss << " native alloc=" << PrettySize(allocated_space) << " (" << allocated_space << "B)"
+ << " free=" << PrettySize(free_space) << " (" << free_space << "B)";
#endif
compiled_method_storage_.DumpMemoryUsage(oss, extended);
return oss.str();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 5e35cbb309..d8f23f7a73 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -138,6 +138,7 @@ class CompilerDriver {
REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_);
VerificationResults* GetVerificationResults() const {
+ DCHECK(Runtime::Current()->IsAotCompiler());
return verification_results_;
}
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 73574ba673..3d3130962a 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -124,7 +124,10 @@ bool ImageWriter::PrepareImageAddressSpace() {
{
ScopedObjectAccess soa(Thread::Current());
PruneNonImageClasses(); // Remove junk
- ComputeLazyFieldsForImageClasses(); // Add useful information
+ if (!compile_app_image_) {
+ // Avoid for app image since this may increase RAM and image size.
+ ComputeLazyFieldsForImageClasses(); // Add useful information
+ }
}
heap->CollectGarbage(false); // Remove garbage.
@@ -735,20 +738,20 @@ bool ImageWriter::IsBootClassLoaderNonImageClass(mirror::Class* klass) {
return IsBootClassLoaderClass(klass) && !IsInBootImage(klass);
}
-bool ImageWriter::ContainsBootClassLoaderNonImageClass(mirror::Class* klass) {
+bool ImageWriter::PruneAppImageClass(mirror::Class* klass) {
bool early_exit = false;
std::unordered_set<mirror::Class*> visited;
- return ContainsBootClassLoaderNonImageClassInternal(klass, &early_exit, &visited);
+ return PruneAppImageClassInternal(klass, &early_exit, &visited);
}
-bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
+bool ImageWriter::PruneAppImageClassInternal(
mirror::Class* klass,
bool* early_exit,
std::unordered_set<mirror::Class*>* visited) {
DCHECK(early_exit != nullptr);
DCHECK(visited != nullptr);
DCHECK(compile_app_image_);
- if (klass == nullptr) {
+ if (klass == nullptr || IsInBootImage(klass)) {
return false;
}
auto found = prune_class_memo_.find(klass);
@@ -762,7 +765,11 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
return false;
}
visited->emplace(klass);
- bool result = IsBootClassLoaderNonImageClass(klass);
+ bool result = IsBootClassLoaderClass(klass);
+ std::string temp;
+ // Prune if not an image class, this handles any broken sets of image classes such as having a
+ // class in the set but not it's superclass.
+ result = result || !compiler_driver_.IsImageClass(klass->GetDescriptor(&temp));
bool my_early_exit = false; // Only for ourselves, ignore caller.
// Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the
// app image.
@@ -775,17 +782,15 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
// Check interfaces since these wont be visited through VisitReferences.)
mirror::IfTable* if_table = klass->GetIfTable();
for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
- result = result || ContainsBootClassLoaderNonImageClassInternal(
- if_table->GetInterface(i),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(if_table->GetInterface(i),
+ &my_early_exit,
+ visited);
}
}
if (klass->IsObjectArrayClass()) {
- result = result || ContainsBootClassLoaderNonImageClassInternal(
- klass->GetComponentType(),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(klass->GetComponentType(),
+ &my_early_exit,
+ visited);
}
// Check static fields and their classes.
size_t num_static_fields = klass->NumReferenceStaticFields();
@@ -798,27 +803,22 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
mirror::Object* ref = klass->GetFieldObject<mirror::Object>(field_offset);
if (ref != nullptr) {
if (ref->IsClass()) {
- result = result ||
- ContainsBootClassLoaderNonImageClassInternal(
- ref->AsClass(),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(ref->AsClass(),
+ &my_early_exit,
+ visited);
+ } else {
+ result = result || PruneAppImageClassInternal(ref->GetClass(),
+ &my_early_exit,
+ visited);
}
- result = result ||
- ContainsBootClassLoaderNonImageClassInternal(
- ref->GetClass(),
- &my_early_exit,
- visited);
}
field_offset = MemberOffset(field_offset.Uint32Value() +
sizeof(mirror::HeapReference<mirror::Object>));
}
}
- result = result ||
- ContainsBootClassLoaderNonImageClassInternal(
- klass->GetSuperClass(),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(klass->GetSuperClass(),
+ &my_early_exit,
+ visited);
// Erase the element we stored earlier since we are exiting the function.
auto it = visited->find(klass);
DCHECK(it != visited->end());
@@ -837,15 +837,21 @@ bool ImageWriter::KeepClass(Class* klass) {
if (klass == nullptr) {
return false;
}
+ if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ // Already in boot image, return true.
+ return true;
+ }
+ std::string temp;
+ if (!compiler_driver_.IsImageClass(klass->GetDescriptor(&temp))) {
+ return false;
+ }
if (compile_app_image_) {
// For app images, we need to prune boot loader classes that are not in the boot image since
// these may have already been loaded when the app image is loaded.
// Keep classes in the boot image space since we don't want to re-resolve these.
- return Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass) ||
- !ContainsBootClassLoaderNonImageClass(klass);
+ return !PruneAppImageClass(klass);
}
- std::string temp;
- return compiler_driver_.IsImageClass(klass->GetDescriptor(&temp));
+ return true;
}
class NonImageClassesVisitor : public ClassVisitor {
@@ -873,6 +879,7 @@ void ImageWriter::PruneNonImageClasses() {
class_linker->VisitClasses(&visitor);
// Remove the undesired classes from the class roots.
+ VLOG(compiler) << "Pruning " << visitor.classes_to_prune_.size() << " classes";
for (mirror::Class* klass : visitor.classes_to_prune_) {
std::string temp;
const char* name = klass->GetDescriptor(&temp);
@@ -891,10 +898,10 @@ void ImageWriter::PruneNonImageClasses() {
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); // For ClassInClassTable
ReaderMutexLock mu2(self, *class_linker->DexLock());
for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
- mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
- if (dex_cache == nullptr) {
+ if (self->IsJWeakCleared(data.weak_root)) {
continue;
}
+ mirror::DexCache* dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache();
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
Class* klass = dex_cache->GetResolvedType(i);
if (klass != nullptr && !KeepClass(klass)) {
@@ -907,10 +914,10 @@ void ImageWriter::PruneNonImageClasses() {
mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_);
DCHECK(method != nullptr) << "Expected resolution method instead of null method";
mirror::Class* declaring_class = method->GetDeclaringClass();
- // Miranda methods may be held live by a class which was not an image class but have a
+ // Copied methods may be held live by a class which was not an image class but have a
// declaring class which is an image class. Set it to the resolution method to be safe and
// prevent dangling pointers.
- if (method->IsMiranda() || !KeepClass(declaring_class)) {
+ if (method->IsCopied() || !KeepClass(declaring_class)) {
mirror::DexCache::SetElementPtrSize(resolved_methods,
i,
resolution_method,
@@ -1820,12 +1827,16 @@ uintptr_t ImageWriter::NativeOffsetInImage(void* obj) {
}
template <typename T>
-T* ImageWriter::NativeLocationInImage(T* obj, const char* oat_filename) {
+T* ImageWriter::NativeLocationInImage(T* obj) {
if (obj == nullptr || IsInBootImage(obj)) {
return obj;
} else {
- ImageInfo& image_info = GetImageInfo(oat_filename);
- return reinterpret_cast<T*>(image_info.image_begin_ + NativeOffsetInImage(obj));
+ auto it = native_object_relocations_.find(obj);
+ CHECK(it != native_object_relocations_.end()) << obj << " spaces "
+ << Runtime::Current()->GetHeap()->DumpSpaces();
+ const NativeObjectRelocation& relocation = it->second;
+ ImageInfo& image_info = GetImageInfo(relocation.oat_filename);
+ return reinterpret_cast<T*>(image_info.image_begin_ + relocation.offset);
}
}
@@ -1842,33 +1853,19 @@ T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) {
class NativeLocationVisitor {
public:
- explicit NativeLocationVisitor(ImageWriter* image_writer, const char* oat_filename)
- : image_writer_(image_writer), oat_filename_(oat_filename) {}
+ explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
template <typename T>
T* operator()(T* ptr) const SHARED_REQUIRES(Locks::mutator_lock_) {
- return image_writer_->NativeLocationInImage(ptr, oat_filename_);
- }
-
- ArtMethod* operator()(ArtMethod* method) const SHARED_REQUIRES(Locks::mutator_lock_) {
- const char* oat_filename = method->IsRuntimeMethod() ? image_writer_->GetDefaultOatFilename() :
- image_writer_->GetOatFilenameForDexCache(method->GetDexCache());
- return image_writer_->NativeLocationInImage(method, oat_filename);
- }
-
- ArtField* operator()(ArtField* field) const SHARED_REQUIRES(Locks::mutator_lock_) {
- const char* oat_filename = image_writer_->GetOatFilenameForDexCache(field->GetDexCache());
- return image_writer_->NativeLocationInImage(field, oat_filename);
+ return image_writer_->NativeLocationInImage(ptr);
}
private:
ImageWriter* const image_writer_;
- const char* oat_filename_;
};
void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
- const char* oat_filename = GetOatFilename(orig);
- orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename));
+ orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this));
FixupClassVisitor visitor(this, copy);
static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor);
@@ -1952,11 +1949,10 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
// 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
// done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
// static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
- const char* oat_filename = GetOatFilenameForDexCache(orig_dex_cache);
GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
if (orig_strings != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::StringsOffset(),
- NativeLocationInImage(orig_strings, oat_filename),
+ NativeLocationInImage(orig_strings),
/*pointer size*/8u);
orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache),
ImageAddressVisitor(this));
@@ -1964,7 +1960,7 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
if (orig_types != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
- NativeLocationInImage(orig_types, oat_filename),
+ NativeLocationInImage(orig_types),
/*pointer size*/8u);
orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache),
ImageAddressVisitor(this));
@@ -1972,32 +1968,25 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
if (orig_methods != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(),
- NativeLocationInImage(orig_methods, oat_filename),
+ NativeLocationInImage(orig_methods),
/*pointer size*/8u);
ArtMethod** copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_);
- const char* method_oat_filename;
- if (orig == nullptr || orig->IsRuntimeMethod()) {
- method_oat_filename = default_oat_filename_;
- } else {
- method_oat_filename = GetOatFilenameForDexCache(orig->GetDexCache());
- }
- ArtMethod* copy = NativeLocationInImage(orig, method_oat_filename);
+ // NativeLocationInImage also handles runtime methods since these have relocation info.
+ ArtMethod* copy = NativeLocationInImage(orig);
mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
}
}
ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
if (orig_fields != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(),
- NativeLocationInImage(orig_fields, oat_filename),
+ NativeLocationInImage(orig_fields),
/*pointer size*/8u);
ArtField** copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_);
- const char* field_oat_filename =
- orig == nullptr ? default_oat_filename_ : GetOatFilenameForDexCache(orig->GetDexCache());
- ArtField* copy = NativeLocationInImage(orig, field_oat_filename);
+ ArtField* copy = NativeLocationInImage(orig);
mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
}
}
@@ -2089,20 +2078,10 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
- const char* oat_filename;
- if (orig->IsRuntimeMethod() || compile_app_image_) {
- oat_filename = default_oat_filename_;
- } else {
- auto it = dex_file_oat_filename_map_.find(orig->GetDexFile());
- DCHECK(it != dex_file_oat_filename_map_.end()) << orig->GetDexFile()->GetLocation();
- oat_filename = it->second;
- }
ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
- copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods, oat_filename),
- target_ptr_size_);
+ copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_);
- copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types, oat_filename),
- target_ptr_size_);
+ copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_);
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
@@ -2324,6 +2303,8 @@ ImageWriter::ImageWriter(
image_info_map_.emplace(oat_filename, ImageInfo());
}
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
+ CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
+ << "Compiling a boot image should occur iff there are no boot image spaces loaded";
}
ImageWriter::ImageInfo::ImageInfo()
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 9371d9ffa9..ee204c5081 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -410,16 +410,18 @@ class ImageWriter FINAL {
// Return true if klass is loaded by the boot class loader but not in the boot image.
bool IsBootClassLoaderNonImageClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
- // Return true if klass depends on a boot class loader non image class live. We want to prune
- // these classes since we do not want any boot class loader classes in the image. This means that
+ // Return true if klass depends on a boot class loader non image class. We want to prune these
+ // classes since we do not want any boot class loader classes in the image. This means that
// we also cannot have any classes which refer to these boot class loader non image classes.
- bool ContainsBootClassLoaderNonImageClass(mirror::Class* klass)
+ // PruneAppImageClass also prunes if klass depends on a non-image class according to the compiler
+ // driver.
+ bool PruneAppImageClass(mirror::Class* klass)
SHARED_REQUIRES(Locks::mutator_lock_);
// early_exit is true if we had a cyclic dependency anywhere down the chain.
- bool ContainsBootClassLoaderNonImageClassInternal(mirror::Class* klass,
- bool* early_exit,
- std::unordered_set<mirror::Class*>* visited)
+ bool PruneAppImageClassInternal(mirror::Class* klass,
+ bool* early_exit,
+ std::unordered_set<mirror::Class*>* visited)
SHARED_REQUIRES(Locks::mutator_lock_);
static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
@@ -428,7 +430,7 @@ class ImageWriter FINAL {
// Location of where the object will be when the image is loaded at runtime.
template <typename T>
- T* NativeLocationInImage(T* obj, const char* oat_filename) SHARED_REQUIRES(Locks::mutator_lock_);
+ T* NativeLocationInImage(T* obj) SHARED_REQUIRES(Locks::mutator_lock_);
// Location of where the temporary copy of the object currently is.
template <typename T>
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 3fe786141e..909d6822a8 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -23,10 +23,7 @@
#include "base/time_utils.h"
#include "base/timing_logger.h"
#include "base/unix_file/fd_file.h"
-#include "compiler_callbacks.h"
#include "debug/elf_debug_writer.h"
-#include "dex/pass_manager.h"
-#include "dex/quick_compiler_callbacks.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "jit/debugger_interface.h"
@@ -36,7 +33,6 @@
#include "oat_quick_method_header.h"
#include "object_lock.h"
#include "thread_list.h"
-#include "verifier/method_verifier-inl.h"
namespace art {
namespace jit {
@@ -45,11 +41,10 @@ JitCompiler* JitCompiler::Create() {
return new JitCompiler();
}
-extern "C" void* jit_load(CompilerCallbacks** callbacks, bool* generate_debug_info) {
+extern "C" void* jit_load(bool* generate_debug_info) {
VLOG(jit) << "loading jit compiler";
auto* const jit_compiler = JitCompiler::Create();
CHECK(jit_compiler != nullptr);
- *callbacks = jit_compiler->GetCompilerCallbacks();
*generate_debug_info = jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo();
VLOG(jit) << "Done loading jit compiler";
return jit_compiler;
@@ -151,14 +146,10 @@ JitCompiler::JitCompiler() : total_time_(0) {
instruction_set_features_.reset(InstructionSetFeatures::FromCppDefines());
}
cumulative_logger_.reset(new CumulativeLogger("jit times"));
- verification_results_.reset(new VerificationResults(compiler_options_.get()));
method_inliner_map_.reset(new DexFileToMethodInlinerMap);
- callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(),
- method_inliner_map_.get(),
- CompilerCallbacks::CallbackMode::kCompileApp));
compiler_driver_.reset(new CompilerDriver(
compiler_options_.get(),
- verification_results_.get(),
+ /* verification_results */ nullptr,
method_inliner_map_.get(),
Compiler::kOptimizing,
instruction_set,
@@ -251,9 +242,5 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) {
return success;
}
-CompilerCallbacks* JitCompiler::GetCompilerCallbacks() const {
- return callbacks_.get();
-}
-
} // namespace jit
} // namespace art
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 894d29ee99..fead839263 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -415,7 +415,9 @@ TEST_F(OatTest, WriteRead) {
size_t visited_virtuals = 0;
// TODO We should also check copied methods in this test.
for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) {
- EXPECT_FALSE(m.IsMiranda());
+ if (!klass->IsInterface()) {
+ EXPECT_FALSE(m.IsCopied());
+ }
CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file);
++method_index;
++visited_virtuals;
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index ba1b1683d7..a7a1c0f2c4 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -67,20 +67,28 @@ class ValueBound : public ValueObject {
static bool IsAddOrSubAConstant(HInstruction* instruction,
/* out */ HInstruction** left_instruction,
/* out */ int32_t* right_constant) {
- if (instruction->IsAdd() || instruction->IsSub()) {
+ HInstruction* left_so_far = nullptr;
+ int32_t right_so_far = 0;
+ while (instruction->IsAdd() || instruction->IsSub()) {
HBinaryOperation* bin_op = instruction->AsBinaryOperation();
HInstruction* left = bin_op->GetLeft();
HInstruction* right = bin_op->GetRight();
if (right->IsIntConstant()) {
- *left_instruction = left;
- int32_t c = right->AsIntConstant()->GetValue();
- *right_constant = instruction->IsAdd() ? c : -c;
- return true;
+ int32_t v = right->AsIntConstant()->GetValue();
+ int32_t c = instruction->IsAdd() ? v : -v;
+ if (!WouldAddOverflowOrUnderflow(right_so_far, c)) {
+ instruction = left;
+ left_so_far = left;
+ right_so_far += c;
+ continue;
+ }
}
+ break;
}
- *left_instruction = nullptr;
- *right_constant = 0;
- return false;
+ // Return result: either false and "null+0" or true and "instr+constant".
+ *left_instruction = left_so_far;
+ *right_constant = right_so_far;
+ return left_so_far != nullptr;
}
// Expresses any instruction as a value bound.
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 23ca703b89..9dd7c519aa 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2107,7 +2107,6 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register res = locations->Out().AsRegister<Register>();
Primitive::Type in_type = instruction->InputAt(0)->GetType();
- bool gt_bias = instruction->IsGtBias();
bool isR6 = codegen_->GetInstructionSetFeatures().IsR6();
// 0 if: left == right
@@ -2141,6 +2140,7 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
}
case Primitive::kPrimFloat: {
+ bool gt_bias = instruction->IsGtBias();
FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
MipsLabel done;
@@ -2180,6 +2180,7 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
break;
}
case Primitive::kPrimDouble: {
+ bool gt_bias = instruction->IsGtBias();
FRegister lhs = locations->InAt(0).AsFpuRegister<FRegister>();
FRegister rhs = locations->InAt(1).AsFpuRegister<FRegister>();
MipsLabel done;
@@ -3953,28 +3954,19 @@ void InstructionCodeGeneratorMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDire
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
-void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
- if (TryGenerateIntrinsicCode(invoke, codegen_)) {
- return;
- }
-
+void CodeGeneratorMIPS::GenerateVirtualCall(HInvokeVirtual* invoke, Location temp_location) {
LocationSummary* locations = invoke->GetLocations();
Location receiver = locations->InAt(0);
- Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
+ Register temp = temp_location.AsRegister<Register>();
size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
invoke->GetVTableIndex(), kMipsPointerSize).SizeValue();
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kMipsWordSize);
// temp = object->GetClass();
- if (receiver.IsStackSlot()) {
- __ LoadFromOffset(kLoadWord, temp, SP, receiver.GetStackIndex());
- __ LoadFromOffset(kLoadWord, temp, temp, class_offset);
- } else {
- DCHECK(receiver.IsRegister());
- __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
- }
- codegen_->MaybeRecordImplicitNullCheck(invoke);
+ DCHECK(receiver.IsRegister());
+ __ LoadFromOffset(kLoadWord, temp, receiver.AsRegister<Register>(), class_offset);
+ MaybeRecordImplicitNullCheck(invoke);
// temp = temp->GetMethodAt(method_offset);
__ LoadFromOffset(kLoadWord, temp, temp, method_offset);
// T9 = temp->GetEntryPoint();
@@ -3982,6 +3974,14 @@ void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
// T9();
__ Jalr(T9);
__ Nop();
+}
+
+void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+
+ codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
}
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 752bf3b986..49c958335b 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -353,10 +353,7 @@ class CodeGeneratorMIPS : public CodeGenerator {
MethodReference target_method) OVERRIDE;
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
- void GenerateVirtualCall(HInvokeVirtual* invoke ATTRIBUTE_UNUSED,
- Location temp ATTRIBUTE_UNUSED) OVERRIDE {
- UNIMPLEMENTED(FATAL) << "Not implemented on MIPS";
- }
+ void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
void MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED,
Primitive::Type type ATTRIBUTE_UNUSED) OVERRIDE {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index db85dbeba6..2c0ae9ba90 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1727,7 +1727,6 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) {
LocationSummary* locations = instruction->GetLocations();
GpuRegister res = locations->Out().AsRegister<GpuRegister>();
Primitive::Type in_type = instruction->InputAt(0)->GetType();
- bool gt_bias = instruction->IsGtBias();
// 0 if: left == right
// 1 if: left > right
@@ -1769,7 +1768,7 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) {
__ CmpEqS(FTMP, lhs, rhs);
__ LoadConst32(res, 0);
__ Bc1nez(FTMP, &done);
- if (gt_bias) {
+ if (instruction->IsGtBias()) {
__ CmpLtS(FTMP, lhs, rhs);
__ LoadConst32(res, -1);
__ Bc1nez(FTMP, &done);
@@ -1791,7 +1790,7 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) {
__ CmpEqD(FTMP, lhs, rhs);
__ LoadConst32(res, 0);
__ Bc1nez(FTMP, &done);
- if (gt_bias) {
+ if (instruction->IsGtBias()) {
__ CmpLtD(FTMP, lhs, rhs);
__ LoadConst32(res, -1);
__ Bc1nez(FTMP, &done);
@@ -4258,4 +4257,3 @@ void InstructionCodeGeneratorMIPS64::VisitClassTableGet(HClassTableGet*) {
} // namespace mips64
} // namespace art
-
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 1ba44dfc54..c298097a46 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -349,7 +349,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
void MoveFromReturnRegister(Location trg ATTRIBUTE_UNUSED,
Primitive::Type type ATTRIBUTE_UNUSED) OVERRIDE {
- UNIMPLEMENTED(FATAL);
+ UNIMPLEMENTED(FATAL) << "Not implemented on MIPS64";
}
void GenerateNop();
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 143dad8085..236dea1bba 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -26,7 +26,6 @@
#include "intrinsics_x86.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
-#include "pc_relative_fixups_x86.h"
#include "thread.h"
#include "utils/assembler.h"
#include "utils/stack_checks.h"
@@ -1276,7 +1275,7 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond,
}
// Must be equal high, so compare the lows.
codegen_->Compare32BitValue(left_low, val_low);
- } else {
+ } else if (right.IsRegisterPair()) {
Register right_high = right.AsRegisterPairHigh<Register>();
Register right_low = right.AsRegisterPairLow<Register>();
@@ -1291,6 +1290,19 @@ void InstructionCodeGeneratorX86::GenerateLongComparesAndJumps(HCondition* cond,
}
// Must be equal high, so compare the lows.
__ cmpl(left_low, right_low);
+ } else {
+ DCHECK(right.IsDoubleStackSlot());
+ __ cmpl(left_high, Address(ESP, right.GetHighStackIndex(kX86WordSize)));
+ if (if_cond == kCondNE) {
+ __ j(X86Condition(true_high_cond), true_label);
+ } else if (if_cond == kCondEQ) {
+ __ j(X86Condition(false_high_cond), false_label);
+ } else {
+ __ j(X86Condition(true_high_cond), true_label);
+ __ j(X86Condition(false_high_cond), false_label);
+ }
+ // Must be equal high, so compare the lows.
+ __ cmpl(left_low, Address(ESP, right.GetStackIndex()));
}
// The last comparison might be unsigned.
__ j(final_condition, true_label);
@@ -1505,30 +1517,131 @@ void InstructionCodeGeneratorX86::VisitDeoptimize(HDeoptimize* deoptimize) {
/* false_target */ nullptr);
}
+static bool SelectCanUseCMOV(HSelect* select) {
+ // There are no conditional move instructions for XMMs.
+ if (Primitive::IsFloatingPointType(select->GetType())) {
+ return false;
+ }
+
+ // A FP condition doesn't generate the single CC that we need.
+ // In 32 bit mode, a long condition doesn't generate a single CC either.
+ HInstruction* condition = select->GetCondition();
+ if (condition->IsCondition()) {
+ Primitive::Type compare_type = condition->InputAt(0)->GetType();
+ if (compare_type == Primitive::kPrimLong ||
+ Primitive::IsFloatingPointType(compare_type)) {
+ return false;
+ }
+ }
+
+ // We can generate a CMOV for this Select.
+ return true;
+}
+
void LocationsBuilderX86::VisitSelect(HSelect* select) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select);
- Primitive::Type select_type = select->GetType();
- HInstruction* cond = select->GetCondition();
-
- if (Primitive::IsFloatingPointType(select_type)) {
+ if (Primitive::IsFloatingPointType(select->GetType())) {
locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::Any());
} else {
locations->SetInAt(0, Location::RequiresRegister());
+ if (SelectCanUseCMOV(select)) {
+ if (select->InputAt(1)->IsConstant()) {
+ // Cmov can't handle a constant value.
+ locations->SetInAt(1, Location::RequiresRegister());
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
+ } else {
+ locations->SetInAt(1, Location::Any());
+ }
}
- locations->SetInAt(1, Location::Any());
- if (IsBooleanValueOrMaterializedCondition(cond)) {
- locations->SetInAt(2, Location::Any());
+ if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
+ locations->SetInAt(2, Location::RequiresRegister());
}
locations->SetOut(Location::SameAsFirstInput());
}
+void InstructionCodeGeneratorX86::GenerateIntCompare(Location lhs, Location rhs) {
+ Register lhs_reg = lhs.AsRegister<Register>();
+ if (rhs.IsConstant()) {
+ int32_t value = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
+ codegen_->Compare32BitValue(lhs_reg, value);
+ } else if (rhs.IsStackSlot()) {
+ __ cmpl(lhs_reg, Address(ESP, rhs.GetStackIndex()));
+ } else {
+ __ cmpl(lhs_reg, rhs.AsRegister<Register>());
+ }
+}
+
void InstructionCodeGeneratorX86::VisitSelect(HSelect* select) {
LocationSummary* locations = select->GetLocations();
- NearLabel false_target;
- GenerateTestAndBranch<NearLabel>(
- select, /* condition_input_index */ 2, /* true_target */ nullptr, &false_target);
- codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
- __ Bind(&false_target);
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ if (SelectCanUseCMOV(select)) {
+ // If both the condition and the source types are integer, we can generate
+ // a CMOV to implement Select.
+
+ HInstruction* select_condition = select->GetCondition();
+ Condition cond = kNotEqual;
+
+ // Figure out how to test the 'condition'.
+ if (select_condition->IsCondition()) {
+ HCondition* condition = select_condition->AsCondition();
+ if (!condition->IsEmittedAtUseSite()) {
+ // This was a previously materialized condition.
+ // Can we use the existing condition code?
+ if (AreEflagsSetFrom(condition, select)) {
+ // Materialization was the previous instruction. Condition codes are right.
+ cond = X86Condition(condition->GetCondition());
+ } else {
+ // No, we have to recreate the condition code.
+ Register cond_reg = locations->InAt(2).AsRegister<Register>();
+ __ testl(cond_reg, cond_reg);
+ }
+ } else {
+ // We can't handle FP or long here.
+ DCHECK_NE(condition->InputAt(0)->GetType(), Primitive::kPrimLong);
+ DCHECK(!Primitive::IsFloatingPointType(condition->InputAt(0)->GetType()));
+ LocationSummary* cond_locations = condition->GetLocations();
+ GenerateIntCompare(cond_locations->InAt(0), cond_locations->InAt(1));
+ cond = X86Condition(condition->GetCondition());
+ }
+ } else {
+ // Must be a boolean condition, which needs to be compared to 0.
+ Register cond_reg = locations->InAt(2).AsRegister<Register>();
+ __ testl(cond_reg, cond_reg);
+ }
+
+ // If the condition is true, overwrite the output, which already contains false.
+ Location false_loc = locations->InAt(0);
+ Location true_loc = locations->InAt(1);
+ if (select->GetType() == Primitive::kPrimLong) {
+ // 64 bit conditional move.
+ Register false_high = false_loc.AsRegisterPairHigh<Register>();
+ Register false_low = false_loc.AsRegisterPairLow<Register>();
+ if (true_loc.IsRegisterPair()) {
+ __ cmovl(cond, false_high, true_loc.AsRegisterPairHigh<Register>());
+ __ cmovl(cond, false_low, true_loc.AsRegisterPairLow<Register>());
+ } else {
+ __ cmovl(cond, false_high, Address(ESP, true_loc.GetHighStackIndex(kX86WordSize)));
+ __ cmovl(cond, false_low, Address(ESP, true_loc.GetStackIndex()));
+ }
+ } else {
+ // 32 bit conditional move.
+ Register false_reg = false_loc.AsRegister<Register>();
+ if (true_loc.IsRegister()) {
+ __ cmovl(cond, false_reg, true_loc.AsRegister<Register>());
+ } else {
+ __ cmovl(cond, false_reg, Address(ESP, true_loc.GetStackIndex()));
+ }
+ }
+ } else {
+ NearLabel false_target;
+ GenerateTestAndBranch<NearLabel>(
+ select, /* condition_input_index */ 2, /* true_target */ nullptr, &false_target);
+ codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType());
+ __ Bind(&false_target);
+ }
}
void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
@@ -1593,7 +1706,7 @@ void LocationsBuilderX86::HandleCondition(HCondition* cond) {
switch (cond->InputAt(0)->GetType()) {
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
+ locations->SetInAt(1, Location::Any());
if (!cond->IsEmittedAtUseSite()) {
locations->SetOut(Location::RequiresRegister());
}
@@ -1642,15 +1755,7 @@ void InstructionCodeGeneratorX86::HandleCondition(HCondition* cond) {
// Clear output register: setb only sets the low byte.
__ xorl(reg, reg);
-
- if (rhs.IsRegister()) {
- __ cmpl(lhs.AsRegister<Register>(), rhs.AsRegister<Register>());
- } else if (rhs.IsConstant()) {
- int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
- codegen_->Compare32BitValue(lhs.AsRegister<Register>(), constant);
- } else {
- __ cmpl(lhs.AsRegister<Register>(), Address(ESP, rhs.GetStackIndex()));
- }
+ GenerateIntCompare(lhs, rhs);
__ setb(X86Condition(cond->GetCondition()), reg);
return;
}
@@ -4128,15 +4233,7 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
switch (compare->InputAt(0)->GetType()) {
case Primitive::kPrimInt: {
- Register left_reg = left.AsRegister<Register>();
- if (right.IsConstant()) {
- int32_t value = right.GetConstant()->AsIntConstant()->GetValue();
- codegen_->Compare32BitValue(left_reg, value);
- } else if (right.IsStackSlot()) {
- __ cmpl(left_reg, Address(ESP, right.GetStackIndex()));
- } else {
- __ cmpl(left_reg, right.AsRegister<Register>());
- }
+ GenerateIntCompare(left, right);
break;
}
case Primitive::kPrimLong: {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 858fe17e72..0795f3b530 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -297,6 +297,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator {
HBasicBlock* default_block);
void GenerateFPCompare(Location lhs, Location rhs, HInstruction* insn, bool is_double);
+ void GenerateIntCompare(Location lhs, Location rhs);
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e79c1fb227..8def1de14a 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -6511,8 +6511,8 @@ void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) {
if (value == 0) {
// Clears upper bits too.
__ xorl(dest, dest);
- } else if (value > 0 && IsInt<32>(value)) {
- // We can use a 32 bit move, as it will zero-extend and is one byte shorter.
+ } else if (IsUint<32>(value)) {
+ // We can use a 32 bit move, as it will zero-extend and is shorter.
__ movl(dest, Immediate(static_cast<int32_t>(value)));
} else {
__ movq(dest, Immediate(value));
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 57452cc076..7ddabdee78 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -18,8 +18,28 @@
namespace art {
-// This visitor tries to simplify operations that yield a constant. For example
-// `input * 0` is replaced by a null constant.
+// This visitor tries to simplify instructions that can be evaluated
+// as constants.
+class HConstantFoldingVisitor : public HGraphDelegateVisitor {
+ public:
+ explicit HConstantFoldingVisitor(HGraph* graph)
+ : HGraphDelegateVisitor(graph) {}
+
+ private:
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+ void VisitUnaryOperation(HUnaryOperation* inst) OVERRIDE;
+ void VisitBinaryOperation(HBinaryOperation* inst) OVERRIDE;
+
+ void VisitTypeConversion(HTypeConversion* inst) OVERRIDE;
+ void VisitDivZeroCheck(HDivZeroCheck* inst) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(HConstantFoldingVisitor);
+};
+
+// This visitor tries to simplify operations with an absorbing input,
+// yielding a constant. For example `input * 0` is replaced by a
+// null constant.
class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor {
public:
explicit InstructionWithAbsorbingInputSimplifier(HGraph* graph) : HGraphVisitor(graph) {}
@@ -44,59 +64,69 @@ class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor {
void VisitXor(HXor* instruction) OVERRIDE;
};
+
void HConstantFolding::Run() {
- InstructionWithAbsorbingInputSimplifier simplifier(graph_);
+ HConstantFoldingVisitor visitor(graph_);
// Process basic blocks in reverse post-order in the dominator tree,
// so that an instruction turned into a constant, used as input of
// another instruction, may possibly be used to turn that second
// instruction into a constant as well.
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
- // Traverse this block's instructions in (forward) order and
- // replace the ones that can be statically evaluated by a
- // compile-time counterpart.
- for (HInstructionIterator inst_it(block->GetInstructions());
- !inst_it.Done(); inst_it.Advance()) {
- HInstruction* inst = inst_it.Current();
- 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();
- if (constant != nullptr) {
- 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();
- if (constant != nullptr) {
- inst->ReplaceWith(constant);
- inst->GetBlock()->RemoveInstruction(inst);
- }
- } else if (inst->IsTypeConversion()) {
- // Constant folding: replace `TypeConversion(a)' with a constant at
- // compile time if `a' is a constant.
- HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation();
- if (constant != nullptr) {
- 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.
- HDivZeroCheck* check = inst->AsDivZeroCheck();
- HInstruction* check_input = check->InputAt(0);
- if (check_input->IsConstant() && !check_input->AsConstant()->IsZero()) {
- check->ReplaceWith(check_input);
- check->GetBlock()->RemoveInstruction(check);
- }
- }
- }
+ visitor.VisitReversePostOrder();
+}
+
+
+void HConstantFoldingVisitor::VisitBasicBlock(HBasicBlock* block) {
+ // Traverse this block's instructions (phis don't need to be
+ // processed) in (forward) order and replace the ones that can be
+ // statically evaluated by a compile-time counterpart.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ it.Current()->Accept(this);
}
}
+void HConstantFoldingVisitor::VisitUnaryOperation(HUnaryOperation* inst) {
+ // Constant folding: replace `op(a)' with a constant at compile
+ // time if `a' is a constant.
+ HConstant* constant = inst->TryStaticEvaluation();
+ if (constant != nullptr) {
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
+ }
+}
+
+void HConstantFoldingVisitor::VisitBinaryOperation(HBinaryOperation* inst) {
+ // Constant folding: replace `op(a, b)' with a constant at
+ // compile time if `a' and `b' are both constants.
+ HConstant* constant = inst->TryStaticEvaluation();
+ if (constant != nullptr) {
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
+ } else {
+ InstructionWithAbsorbingInputSimplifier simplifier(GetGraph());
+ inst->Accept(&simplifier);
+ }
+}
+
+void HConstantFoldingVisitor::VisitTypeConversion(HTypeConversion* inst) {
+ // Constant folding: replace `TypeConversion(a)' with a constant at
+ // compile time if `a' is a constant.
+ HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation();
+ if (constant != nullptr) {
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
+ }
+}
+
+void HConstantFoldingVisitor::VisitDivZeroCheck(HDivZeroCheck* inst) {
+ // We can safely remove the check if the input is a non-null constant.
+ HInstruction* check_input = inst->InputAt(0);
+ if (check_input->IsConstant() && !check_input->AsConstant()->IsZero()) {
+ inst->ReplaceWith(check_input);
+ inst->GetBlock()->RemoveInstruction(inst);
+ }
+}
+
+
void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instruction) {
DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
HInstruction* left = instruction->GetLeft();
@@ -178,7 +208,7 @@ void InstructionWithAbsorbingInputSimplifier::VisitCompare(HCompare* instruction
((input_cst->IsFloatConstant() && input_cst->AsFloatConstant()->IsNaN()) ||
(input_cst->IsDoubleConstant() && input_cst->AsDoubleConstant()->IsNaN()))) {
// Replace code looking like
- // CMP{G,L} dst, src, NaN
+ // CMP{G,L}-{FLOAT,DOUBLE} dst, src, NaN
// with
// CONSTANT +1 (gt bias)
// or
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
index 2698b2d690..e10b1d6b2e 100644
--- a/compiler/optimizing/constant_folding.h
+++ b/compiler/optimizing/constant_folding.h
@@ -26,13 +26,20 @@ namespace art {
* Optimization pass performing a simple constant-expression
* evaluation on the SSA form.
*
+ * Note that graph simplifications producing a constant should be
+ * implemented in art::HConstantFolding, while graph simplifications
+ * not producing constants should be implemented in
+ * art::InstructionSimplifier. (This convention is a choice that was
+ * made during the development of these parts of the compiler and is
+ * not bound by any technical requirement.)
+ *
* This class is named art::HConstantFolding to avoid name
* clashes with the art::ConstantPropagation class defined in
* compiler/dex/post_opt_passes.h.
*/
class HConstantFolding : public HOptimization {
public:
- explicit HConstantFolding(HGraph* graph, const char* name = kConstantFoldingPassName)
+ HConstantFolding(HGraph* graph, const char* name = kConstantFoldingPassName)
: HOptimization(graph, name) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index e6e9177841..4a49c83611 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -593,8 +593,9 @@ void GraphChecker::HandleLoop(HBasicBlock* loop_header) {
HBasicBlock* predecessor = loop_header->GetPredecessors()[i];
if (!loop_information->IsBackEdge(*predecessor)) {
AddError(StringPrintf(
- "Loop header %d has multiple incoming (non back edge) blocks.",
- id));
+ "Loop header %d has multiple incoming (non back edge) blocks: %d.",
+ id,
+ predecessor->GetBlockId()));
}
}
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4cf0eb1565..c0263e4e5b 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -384,6 +384,13 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
<< array_set->GetValueCanBeNull() << std::noboolalpha;
}
+ void VisitCompare(HCompare* compare) OVERRIDE {
+ ComparisonBias bias = compare->GetBias();
+ StartAttributeStream("bias") << (bias == ComparisonBias::kGtBias
+ ? "gt"
+ : (bias == ComparisonBias::kLtBias ? "lt" : "none"));
+ }
+
void VisitInvoke(HInvoke* invoke) OVERRIDE {
StartAttributeStream("dex_file_index") << invoke->GetDexMethodIndex();
StartAttributeStream("method_name") << PrettyMethod(
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 68e96fba74..02a1acc240 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -190,28 +190,34 @@ static uint32_t FindMethodIndexIn(ArtMethod* method,
}
}
-static uint32_t FindClassIndexIn(mirror::Class* cls, const DexFile& dex_file)
+static uint32_t FindClassIndexIn(mirror::Class* cls,
+ const DexFile& dex_file,
+ Handle<mirror::DexCache> dex_cache)
SHARED_REQUIRES(Locks::mutator_lock_) {
+ uint32_t index = DexFile::kDexNoIndex;
if (cls->GetDexCache() == nullptr) {
- DCHECK(cls->IsArrayClass());
- // TODO: find the class in `dex_file`.
- return DexFile::kDexNoIndex;
+ DCHECK(cls->IsArrayClass()) << PrettyClass(cls);
+ index = cls->FindTypeIndexInOtherDexFile(dex_file);
} else if (cls->GetDexTypeIndex() == DexFile::kDexNoIndex16) {
+ DCHECK(cls->IsProxyClass()) << PrettyClass(cls);
// TODO: deal with proxy classes.
- return DexFile::kDexNoIndex;
} else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
+ index = cls->GetDexTypeIndex();
+ } else {
+ index = cls->FindTypeIndexInOtherDexFile(dex_file);
+ }
+
+ if (index != DexFile::kDexNoIndex) {
// Update the dex cache to ensure the class is in. The generated code will
// consider it is. We make it safe by updating the dex cache, as other
// dex files might also load the class, and there is no guarantee the dex
// cache of the dex file of the class will be updated.
- if (cls->GetDexCache()->GetResolvedType(cls->GetDexTypeIndex()) == nullptr) {
- cls->GetDexCache()->SetResolvedType(cls->GetDexTypeIndex(), cls);
+ if (dex_cache->GetResolvedType(index) == nullptr) {
+ dex_cache->SetResolvedType(index, cls);
}
- return cls->GetDexTypeIndex();
- } else {
- // TODO: find the class in `dex_file`.
- return DexFile::kDexNoIndex;
}
+
+ return index;
}
bool HInliner::TryInline(HInvoke* invoke_instruction) {
@@ -258,8 +264,9 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
}
if (actual_method != nullptr) {
- return TryInline(invoke_instruction, actual_method);
+ return TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true);
}
+
DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
// Check if we can use an inline cache.
@@ -302,7 +309,7 @@ HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
uint32_t dex_pc) const {
ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
- return new (graph_->GetArena()) HInstanceFieldGet(
+ HInstanceFieldGet* result = new (graph_->GetArena()) HInstanceFieldGet(
receiver,
Primitive::kPrimNot,
field->GetOffset(),
@@ -312,6 +319,9 @@ HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
*field->GetDexFile(),
handles_->NewHandle(field->GetDexCache()),
dex_pc);
+ // The class of a field is effectively final, and does not have any memory dependencies.
+ result->SetSideEffects(SideEffects::None());
+ return result;
}
bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
@@ -321,7 +331,8 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
<< invoke_instruction->DebugName();
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
- uint32_t class_index = FindClassIndexIn(ic.GetMonomorphicType(), caller_dex_file);
+ uint32_t class_index = FindClassIndexIn(
+ ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache());
if (class_index == DexFile::kDexNoIndex) {
VLOG(compiler) << "Call to " << PrettyMethod(resolved_method)
<< " from inline cache is not inlined because its class is not"
@@ -344,16 +355,44 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- if (!TryInline(invoke_instruction, resolved_method, /* do_rtp */ false)) {
+ if (!TryInlineAndReplace(invoke_instruction, resolved_method, /* do_rtp */ false)) {
return false;
}
// We successfully inlined, now add a guard.
+ bool is_referrer =
+ (ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+ AddTypeGuard(receiver,
+ cursor,
+ bb_cursor,
+ class_index,
+ is_referrer,
+ invoke_instruction,
+ /* with_deoptimization */ true);
+
+ // Run type propagation to get the guard typed, and eventually propagate the
+ // type of the receiver.
+ ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ rtp_fixup.Run();
+
+ MaybeRecordStat(kInlinedMonomorphicCall);
+ return true;
+}
+
+HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
+ HInstruction* cursor,
+ HBasicBlock* bb_cursor,
+ uint32_t class_index,
+ bool is_referrer,
+ HInstruction* invoke_instruction,
+ bool with_deoptimization) {
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
class_linker, receiver, invoke_instruction->GetDexPc());
- bool is_referrer =
- (ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+ // Note that we will just compare the classes, so we don't need Java semantics access checks.
+ // Also, the caller of `AddTypeGuard` must have guaranteed that the class is in the dex cache.
HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
class_index,
caller_dex_file,
@@ -363,8 +402,6 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
/* is_in_dex_cache */ true);
HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
- HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
- compare, invoke_instruction->GetDexPc());
// TODO: Extend reference type propagation to understand the guard.
if (cursor != nullptr) {
bb_cursor->InsertInstructionAfter(receiver_class, cursor);
@@ -373,16 +410,13 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
}
bb_cursor->InsertInstructionAfter(load_class, receiver_class);
bb_cursor->InsertInstructionAfter(compare, load_class);
- bb_cursor->InsertInstructionAfter(deoptimize, compare);
- deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
-
- // Run type propagation to get the guard typed, and eventually propagate the
- // type of the receiver.
- ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
- rtp_fixup.Run();
-
- MaybeRecordStat(kInlinedMonomorphicCall);
- return true;
+ if (with_deoptimization) {
+ HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
+ compare, invoke_instruction->GetDexPc());
+ bb_cursor->InsertInstructionAfter(deoptimize, compare);
+ deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ }
+ return compare;
}
bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
@@ -390,6 +424,174 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
const InlineCache& ic) {
DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
<< invoke_instruction->DebugName();
+
+ if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, resolved_method, ic)) {
+ return true;
+ }
+
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+
+ bool all_targets_inlined = true;
+ bool one_target_inlined = false;
+ for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+ if (ic.GetTypeAt(i) == nullptr) {
+ break;
+ }
+ ArtMethod* method = nullptr;
+ if (invoke_instruction->IsInvokeInterface()) {
+ method = ic.GetTypeAt(i)->FindVirtualMethodForInterface(
+ resolved_method, pointer_size);
+ } else {
+ DCHECK(invoke_instruction->IsInvokeVirtual());
+ method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual(
+ resolved_method, pointer_size);
+ }
+
+ HInstruction* receiver = invoke_instruction->InputAt(0);
+ HInstruction* cursor = invoke_instruction->GetPrevious();
+ HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
+
+ uint32_t class_index = FindClassIndexIn(
+ ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
+ HInstruction* return_replacement = nullptr;
+ if (class_index == DexFile::kDexNoIndex ||
+ !TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+ all_targets_inlined = false;
+ } else {
+ one_target_inlined = true;
+ bool is_referrer = (ic.GetTypeAt(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+
+ // If we have inlined all targets before, and this receiver is the last seen,
+ // we deoptimize instead of keeping the original invoke instruction.
+ bool deoptimize = all_targets_inlined &&
+ (i != InlineCache::kIndividualCacheSize - 1) &&
+ (ic.GetTypeAt(i + 1) == nullptr);
+ HInstruction* compare = AddTypeGuard(
+ receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
+ if (deoptimize) {
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+ // Because the inline cache data can be populated concurrently, we force the end of the
+ // iteration. Otherhwise, we could see a new receiver type.
+ break;
+ } else {
+ CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
+ }
+ }
+ }
+
+ if (!one_target_inlined) {
+ VLOG(compiler) << "Call to " << PrettyMethod(resolved_method)
+ << " from inline cache is not inlined because none"
+ << " of its targets could be inlined";
+ return false;
+ }
+ MaybeRecordStat(kInlinedPolymorphicCall);
+
+ // Run type propagation to get the guards typed.
+ ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ rtp_fixup.Run();
+ return true;
+}
+
+void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
+ HInstruction* return_replacement,
+ HInstruction* invoke_instruction) {
+ uint32_t dex_pc = invoke_instruction->GetDexPc();
+ HBasicBlock* cursor_block = compare->GetBlock();
+ HBasicBlock* original_invoke_block = invoke_instruction->GetBlock();
+ ArenaAllocator* allocator = graph_->GetArena();
+
+ // Spit the block after the compare: `cursor_block` will now be the start of the diamond,
+ // and the returned block is the start of the then branch (that could contain multiple blocks).
+ HBasicBlock* then = cursor_block->SplitAfterForInlining(compare);
+
+ // Split the block containing the invoke before and after the invoke. The returned block
+ // of the split before will contain the invoke and will be the otherwise branch of
+ // the diamond. The returned block of the split after will be the merge block
+ // of the diamond.
+ HBasicBlock* end_then = invoke_instruction->GetBlock();
+ HBasicBlock* otherwise = end_then->SplitBeforeForInlining(invoke_instruction);
+ HBasicBlock* merge = otherwise->SplitAfterForInlining(invoke_instruction);
+
+ // If the methods we are inlining return a value, we create a phi in the merge block
+ // that will have the `invoke_instruction and the `return_replacement` as inputs.
+ if (return_replacement != nullptr) {
+ HPhi* phi = new (allocator) HPhi(
+ allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke_instruction->GetType()), dex_pc);
+ merge->AddPhi(phi);
+ invoke_instruction->ReplaceWith(phi);
+ phi->AddInput(return_replacement);
+ phi->AddInput(invoke_instruction);
+ }
+
+ // Add the control flow instructions.
+ otherwise->AddInstruction(new (allocator) HGoto(dex_pc));
+ end_then->AddInstruction(new (allocator) HGoto(dex_pc));
+ cursor_block->AddInstruction(new (allocator) HIf(compare, dex_pc));
+
+ // Add the newly created blocks to the graph.
+ graph_->AddBlock(then);
+ graph_->AddBlock(otherwise);
+ graph_->AddBlock(merge);
+
+ // Set up successor (and implictly predecessor) relations.
+ cursor_block->AddSuccessor(otherwise);
+ cursor_block->AddSuccessor(then);
+ end_then->AddSuccessor(merge);
+ otherwise->AddSuccessor(merge);
+
+ // Set up dominance information.
+ then->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(then);
+ otherwise->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(otherwise);
+ merge->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(merge);
+
+ // Update the revert post order.
+ size_t index = IndexOfElement(graph_->reverse_post_order_, cursor_block);
+ MakeRoomFor(&graph_->reverse_post_order_, 1, index);
+ graph_->reverse_post_order_[++index] = then;
+ index = IndexOfElement(graph_->reverse_post_order_, end_then);
+ MakeRoomFor(&graph_->reverse_post_order_, 2, index);
+ graph_->reverse_post_order_[++index] = otherwise;
+ graph_->reverse_post_order_[++index] = merge;
+
+ // Set the loop information of the newly created blocks.
+ HLoopInformation* loop_info = cursor_block->GetLoopInformation();
+ if (loop_info != nullptr) {
+ then->SetLoopInformation(cursor_block->GetLoopInformation());
+ merge->SetLoopInformation(cursor_block->GetLoopInformation());
+ otherwise->SetLoopInformation(cursor_block->GetLoopInformation());
+ for (HLoopInformationOutwardIterator loop_it(*cursor_block);
+ !loop_it.Done();
+ loop_it.Advance()) {
+ loop_it.Current()->Add(then);
+ loop_it.Current()->Add(merge);
+ loop_it.Current()->Add(otherwise);
+ }
+ // In case the original invoke location was a back edge, we need to update
+ // the loop to now have the merge block as a back edge.
+ if (loop_info->IsBackEdge(*original_invoke_block)) {
+ loop_info->RemoveBackEdge(original_invoke_block);
+ loop_info->AddBackEdge(merge);
+ }
+ }
+
+ // Set the try/catch information of the newly created blocks.
+ then->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+ merge->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+ otherwise->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+}
+
+bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ const InlineCache& ic) {
// This optimization only works under JIT for now.
DCHECK(Runtime::Current()->UseJit());
if (graph_->GetInstructionSet() == kMips64) {
@@ -431,7 +633,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- if (!TryInline(invoke_instruction, actual_method, /* do_rtp */ false)) {
+ if (!TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ false)) {
return false;
}
@@ -485,14 +687,29 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
return true;
}
-bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
+bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) {
+ HInstruction* return_replacement = nullptr;
+ if (!TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+ return false;
+ }
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+ FixUpReturnReferenceType(invoke_instruction, method, return_replacement, do_rtp);
+ return true;
+}
+
+bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
+ ArtMethod* method,
+ HInstruction** return_replacement) {
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
// Check whether we're allowed to inline. The outermost compilation unit is the relevant
// dex file here (though the transitivity of an inline chain would allow checking the calller).
if (!compiler_driver_->MayInline(method->GetDexFile(),
outer_compilation_unit_.GetDexFile())) {
- if (TryPatternSubstitution(invoke_instruction, method, do_rtp)) {
+ if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) {
VLOG(compiler) << "Successfully replaced pattern of invoke " << PrettyMethod(method);
MaybeRecordStat(kReplacedInvokeWithSimplePattern);
return true;
@@ -541,8 +758,9 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do
if (!method->GetDeclaringClass()->IsVerified()) {
uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
- if (!compiler_driver_->IsMethodVerifiedWithoutFailures(
- method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
+ if (Runtime::Current()->UseJit() ||
+ !compiler_driver_->IsMethodVerifiedWithoutFailures(
+ method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " couldn't be verified, so it cannot be inlined";
return false;
@@ -559,7 +777,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do
return false;
}
- if (!TryBuildAndInline(method, invoke_instruction, same_dex_file, do_rtp)) {
+ if (!TryBuildAndInlineHelper(invoke_instruction, method, same_dex_file, return_replacement)) {
return false;
}
@@ -586,27 +804,27 @@ static HInstruction* GetInvokeInputForArgVRegIndex(HInvoke* invoke_instruction,
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
- bool do_rtp) {
+ HInstruction** return_replacement) {
InlineMethod inline_method;
if (!InlineMethodAnalyser::AnalyseMethodCode(resolved_method, &inline_method)) {
return false;
}
- HInstruction* return_replacement = nullptr;
switch (inline_method.opcode) {
case kInlineOpNop:
DCHECK_EQ(invoke_instruction->GetType(), Primitive::kPrimVoid);
+ *return_replacement = nullptr;
break;
case kInlineOpReturnArg:
- return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
- inline_method.d.return_data.arg);
+ *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction,
+ inline_method.d.return_data.arg);
break;
case kInlineOpNonWideConst:
if (resolved_method->GetShorty()[0] == 'L') {
DCHECK_EQ(inline_method.d.data, 0u);
- return_replacement = graph_->GetNullConstant();
+ *return_replacement = graph_->GetNullConstant();
} else {
- return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
+ *return_replacement = graph_->GetIntConstant(static_cast<int32_t>(inline_method.d.data));
}
break;
case kInlineOpIGet: {
@@ -621,7 +839,7 @@ bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
DCHECK_EQ(iget->GetFieldOffset().Uint32Value(), data.field_offset);
DCHECK_EQ(iget->IsVolatile() ? 1u : 0u, data.is_volatile);
invoke_instruction->GetBlock()->InsertInstructionBefore(iget, invoke_instruction);
- return_replacement = iget;
+ *return_replacement = iget;
break;
}
case kInlineOpIPut: {
@@ -639,7 +857,7 @@ bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
invoke_instruction->GetBlock()->InsertInstructionBefore(iput, invoke_instruction);
if (data.return_arg_plus1 != 0u) {
size_t return_arg = data.return_arg_plus1 - 1u;
- return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
+ *return_replacement = GetInvokeInputForArgVRegIndex(invoke_instruction, return_arg);
}
break;
}
@@ -694,19 +912,13 @@ bool HInliner::TryPatternSubstitution(HInvoke* invoke_instruction,
HMemoryBarrier* barrier = new (graph_->GetArena()) HMemoryBarrier(kStoreStore, kNoDexPc);
invoke_instruction->GetBlock()->InsertInstructionBefore(barrier, invoke_instruction);
}
+ *return_replacement = nullptr;
break;
}
default:
LOG(FATAL) << "UNREACHABLE";
UNREACHABLE();
}
-
- if (return_replacement != nullptr) {
- invoke_instruction->ReplaceWith(return_replacement);
- }
- invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
-
- FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
return true;
}
@@ -760,10 +972,10 @@ HInstanceFieldSet* HInliner::CreateInstanceFieldSet(Handle<mirror::DexCache> dex
return iput;
}
-bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
- bool same_dex_file,
- bool do_rtp) {
+bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool same_dex_file,
+ HInstruction** return_replacement) {
ScopedObjectAccess soa(Thread::Current());
const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
@@ -771,16 +983,16 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
DexCompilationUnit dex_compilation_unit(
- nullptr,
- caller_compilation_unit_.GetClassLoader(),
- class_linker,
- callee_dex_file,
- code_item,
- resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
- method_index,
- resolved_method->GetAccessFlags(),
- compiler_driver_->GetVerifiedMethod(&callee_dex_file, method_index),
- dex_cache);
+ nullptr,
+ caller_compilation_unit_.GetClassLoader(),
+ class_linker,
+ callee_dex_file,
+ code_item,
+ resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
+ method_index,
+ resolved_method->GetAccessFlags(),
+ /* verified_method */ nullptr,
+ dex_cache);
bool requires_ctor_barrier = false;
@@ -873,7 +1085,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HConstantFolding fold(callee_graph);
HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
InstructionSimplifier simplify(callee_graph, stats_);
- IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_);
+ IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_, stats_);
HOptimization* optimizations[] = {
&intrinsics,
@@ -1016,16 +1228,12 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
}
number_of_inlined_instructions_ += number_of_instructions;
- HInstruction* return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
- if (return_replacement != nullptr) {
- DCHECK_EQ(graph_, return_replacement->GetBlock()->GetGraph());
- }
- FixUpReturnReferenceType(resolved_method, invoke_instruction, return_replacement, do_rtp);
+ *return_replacement = callee_graph->InlineInto(graph_, invoke_instruction);
return true;
}
-void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
+void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
HInstruction* return_replacement,
bool do_rtp) {
// Check the integrity of reference types and run another type propagation if needed.
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 7d343c62eb..cdb2167082 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -61,12 +61,25 @@ class HInliner : public HOptimization {
bool TryInline(HInvoke* invoke_instruction);
// Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether
- // reference type propagation can run after the inlining.
- bool TryInline(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp = true)
+ // reference type propagation can run after the inlining. If the inlining is successful, this
+ // method will replace and remove the `invoke_instruction`.
+ bool TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool TryBuildAndInline(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ HInstruction** return_replacement)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ bool TryBuildAndInlineHelper(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ bool same_dex_file,
+ HInstruction** return_replacement);
+
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
- bool TryPatternSubstitution(HInvoke* invoke_instruction, ArtMethod* resolved_method, bool do_rtp)
+ bool TryPatternSubstitution(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ HInstruction** return_replacement)
SHARED_REQUIRES(Locks::mutator_lock_);
// Create a new HInstanceFieldGet.
@@ -88,28 +101,80 @@ class HInliner : public HOptimization {
const InlineCache& ic)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Try to inline targets of a polymorphic call. Currently unimplemented.
+ // Try to inline targets of a polymorphic call.
bool TryInlinePolymorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
const InlineCache& ic)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool TryBuildAndInline(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
- bool same_dex_file,
- bool do_rtp = true);
+ bool TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ const InlineCache& ic)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker,
HInstruction* receiver,
uint32_t dex_pc) const
SHARED_REQUIRES(Locks::mutator_lock_);
- void FixUpReturnReferenceType(ArtMethod* resolved_method,
- HInvoke* invoke_instruction,
+ void FixUpReturnReferenceType(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
HInstruction* return_replacement,
bool do_rtp)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Add a type guard on the given `receiver`. This will add to the graph:
+ // i0 = HFieldGet(receiver, klass)
+ // i1 = HLoadClass(class_index, is_referrer)
+ // i2 = HNotEqual(i0, i1)
+ //
+ // And if `with_deoptimization` is true:
+ // HDeoptimize(i2)
+ //
+ // The method returns the `HNotEqual`, that will be used for polymorphic inlining.
+ HInstruction* AddTypeGuard(HInstruction* receiver,
+ HInstruction* cursor,
+ HBasicBlock* bb_cursor,
+ uint32_t class_index,
+ bool is_referrer,
+ HInstruction* invoke_instruction,
+ bool with_deoptimization)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ /*
+ * Ad-hoc implementation for implementing a diamond pattern in the graph for
+ * polymorphic inlining:
+ * 1) `compare` becomes the input of the new `HIf`.
+ * 2) Everything up until `invoke_instruction` is in the then branch (could
+ * contain multiple blocks).
+ * 3) `invoke_instruction` is moved to the otherwise block.
+ * 4) If `return_replacement` is not null, the merge block will have
+ * a phi whose inputs are `return_replacement` and `invoke_instruction`.
+ *
+ * Before:
+ * Block1
+ * compare
+ * ...
+ * invoke_instruction
+ *
+ * After:
+ * Block1
+ * compare
+ * if
+ * / \
+ * / \
+ * Then block Otherwise block
+ * ... invoke_instruction
+ * \ /
+ * \ /
+ * Merge block
+ * phi(return_replacement, invoke_instruction)
+ */
+ void CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
+ HInstruction* return_replacement,
+ HInstruction* invoke_instruction);
+
HGraph* const outermost_graph_;
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index a48d06f3d0..13d3f752c3 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -92,6 +92,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifySystemArrayCopy(HInvoke* invoke);
void SimplifyStringEquals(HInvoke* invoke);
void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
+ void SimplifyIsNaN(HInvoke* invoke);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -1551,6 +1552,16 @@ void InstructionSimplifierVisitor::SimplifyCompare(HInvoke* invoke, bool is_sign
invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, compare);
}
+void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) {
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ uint32_t dex_pc = invoke->GetDexPc();
+ // IsNaN(x) is the same as x != x.
+ HInstruction* x = invoke->InputAt(0);
+ HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc);
+ condition->SetBias(ComparisonBias::kLtBias);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition);
+}
+
void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) {
SimplifyStringEquals(instruction);
@@ -1568,6 +1579,9 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
} else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum ||
instruction->GetIntrinsic() == Intrinsics::kLongSignum) {
SimplifyCompare(instruction, /* is_signum */ true);
+ } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN ||
+ instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) {
+ SimplifyIsNaN(instruction);
}
}
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index cc4b6f6adc..7905104ed4 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -25,6 +25,13 @@ namespace art {
/**
* Implements optimizations specific to each instruction.
+ *
+ * Note that graph simplifications producing a constant should be
+ * implemented in art::HConstantFolding, while graph simplifications
+ * not producing constants should be implemented in
+ * art::InstructionSimplifier. (This convention is a choice that was
+ * made during the development of these parts of the compiler and is
+ * not bound by any technical requirement.)
*/
class InstructionSimplifier : public HOptimization {
public:
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index db39bc8eec..316e86b4c9 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -570,6 +570,7 @@ void IntrinsicsRecognizer::Run() {
NeedsEnvironmentOrCache(intrinsic),
GetSideEffects(intrinsic),
GetExceptions(intrinsic));
+ MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
}
}
}
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 3bf3f7ffae..2ab50bb436 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -33,8 +33,8 @@ static constexpr bool kRoundIsPlusPointFive = false;
// Recognize intrinsics from HInvoke nodes.
class IntrinsicsRecognizer : public HOptimization {
public:
- IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver)
- : HOptimization(graph, kIntrinsicsRecognizerPassName),
+ IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kIntrinsicsRecognizerPassName, stats),
driver_(driver) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 00a158b10a..ea8669fa18 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1858,8 +1858,6 @@ UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1867,6 +1865,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 4140d94e17..8741fd284f 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1618,8 +1618,6 @@ UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1627,6 +1625,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 2294713a3e..c8629644b6 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -113,11 +113,10 @@ class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS {
if (invoke_->IsInvokeStaticOrDirect()) {
codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
Location::RegisterLocation(A0));
- codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
} else {
- UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
- UNREACHABLE();
+ codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), Location::RegisterLocation(A0));
}
+ codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
// Copy the result back to the expected output.
Location out = invoke_->GetLocations()->Out();
@@ -825,6 +824,220 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) {
GetAssembler());
}
+// byte libcore.io.Memory.peekByte(long address)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekByte(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekByte(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register out = invoke->GetLocations()->Out().AsRegister<Register>();
+
+ __ Lb(out, adr, 0);
+}
+
+// short libcore.io.Memory.peekShort(long address)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekShortNative(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register out = invoke->GetLocations()->Out().AsRegister<Register>();
+
+ if (IsR6()) {
+ __ Lh(out, adr, 0);
+ } else if (IsR2OrNewer()) {
+ // Unlike for words, there are no lhl/lhr instructions to load
+ // unaligned halfwords so the code loads individual bytes, in case
+ // the address isn't halfword-aligned, and assembles them into a
+ // signed halfword.
+ __ Lb(AT, adr, 1); // This byte must be sign-extended.
+ __ Lb(out, adr, 0); // This byte can be either sign-extended, or
+ // zero-extended because the following
+ // instruction overwrites the sign bits.
+ __ Ins(out, AT, 8, 24);
+ } else {
+ __ Lbu(AT, adr, 0); // This byte must be zero-extended. If it's not
+ // the "or" instruction below will destroy the upper
+ // 24 bits of the final result.
+ __ Lb(out, adr, 1); // This byte must be sign-extended.
+ __ Sll(out, out, 8);
+ __ Or(out, out, AT);
+ }
+}
+
+// int libcore.io.Memory.peekInt(long address)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekIntNative(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register out = invoke->GetLocations()->Out().AsRegister<Register>();
+
+ if (IsR6()) {
+ __ Lw(out, adr, 0);
+ } else {
+ __ Lwr(out, adr, 0);
+ __ Lwl(out, adr, 3);
+ }
+}
+
+// long libcore.io.Memory.peekLong(long address)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPeekLongNative(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register out_lo = invoke->GetLocations()->Out().AsRegisterPairLow<Register>();
+ Register out_hi = invoke->GetLocations()->Out().AsRegisterPairHigh<Register>();
+
+ if (IsR6()) {
+ __ Lw(out_lo, adr, 0);
+ __ Lw(out_hi, adr, 4);
+ } else {
+ __ Lwr(out_lo, adr, 0);
+ __ Lwl(out_lo, adr, 3);
+ __ Lwr(out_hi, adr, 4);
+ __ Lwl(out_hi, adr, 7);
+ }
+}
+
+static void CreateIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+}
+
+// void libcore.io.Memory.pokeByte(long address, byte value)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeByte(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeByte(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
+
+ __ Sb(val, adr, 0);
+}
+
+// void libcore.io.Memory.pokeShort(long address, short value)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeShortNative(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
+
+ if (IsR6()) {
+ __ Sh(val, adr, 0);
+ } else {
+ // Unlike for words, there are no shl/shr instructions to store
+ // unaligned halfwords so the code stores individual bytes, in case
+ // the address isn't halfword-aligned.
+ __ Sb(val, adr, 0);
+ __ Srl(AT, val, 8);
+ __ Sb(AT, adr, 1);
+ }
+}
+
+// void libcore.io.Memory.pokeInt(long address, int value)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeIntNative(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register val = invoke->GetLocations()->InAt(1).AsRegister<Register>();
+
+ if (IsR6()) {
+ __ Sw(val, adr, 0);
+ } else {
+ __ Swr(val, adr, 0);
+ __ Swl(val, adr, 3);
+ }
+}
+
+// void libcore.io.Memory.pokeLong(long address, long value)
+void IntrinsicLocationsBuilderMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ CreateIntIntToVoidLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register adr = invoke->GetLocations()->InAt(0).AsRegisterPairLow<Register>();
+ Register val_lo = invoke->GetLocations()->InAt(1).AsRegisterPairLow<Register>();
+ Register val_hi = invoke->GetLocations()->InAt(1).AsRegisterPairHigh<Register>();
+
+ if (IsR6()) {
+ __ Sw(val_lo, adr, 0);
+ __ Sw(val_hi, adr, 4);
+ } else {
+ __ Swr(val_lo, adr, 0);
+ __ Swl(val_lo, adr, 3);
+ __ Swr(val_hi, adr, 4);
+ __ Swl(val_hi, adr, 7);
+ }
+}
+
+// char java.lang.String.charAt(int index)
+void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kCallOnSlowPath,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ MipsAssembler* assembler = GetAssembler();
+
+ // Location of reference to data array
+ const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
+ // Location of count
+ const int32_t count_offset = mirror::String::CountOffset().Int32Value();
+
+ Register obj = locations->InAt(0).AsRegister<Register>();
+ Register idx = locations->InAt(1).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ // TODO: Maybe we can support range check elimination. Overall,
+ // though, I think it's not worth the cost.
+ // TODO: For simplicity, the index parameter is requested in a
+ // register, so different from Quick we will not optimize the
+ // code for constants (which would save a register).
+
+ SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
+ codegen_->AddSlowPath(slow_path);
+
+ // Load the string size
+ __ Lw(TMP, obj, count_offset);
+ codegen_->MaybeRecordImplicitNullCheck(invoke);
+ // Revert to slow path if idx is too large, or negative
+ __ Bgeu(idx, TMP, slow_path->GetEntryLabel());
+
+ // out = obj[2*idx].
+ __ Sll(TMP, idx, 1); // idx * 2
+ __ Addu(TMP, TMP, obj); // Address of char at location idx
+ __ Lhu(out, TMP, value_offset); // Load char at location idx
+
+ __ Bind(slow_path->GetExitLabel());
+}
+
// boolean java.lang.String.equals(Object anObject)
void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
@@ -956,14 +1169,6 @@ UNIMPLEMENTED_INTRINSIC(MathFloor)
UNIMPLEMENTED_INTRINSIC(MathRint)
UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(MathRoundFloat)
-UNIMPLEMENTED_INTRINSIC(MemoryPeekByte)
-UNIMPLEMENTED_INTRINSIC(MemoryPeekIntNative)
-UNIMPLEMENTED_INTRINSIC(MemoryPeekLongNative)
-UNIMPLEMENTED_INTRINSIC(MemoryPeekShortNative)
-UNIMPLEMENTED_INTRINSIC(MemoryPokeByte)
-UNIMPLEMENTED_INTRINSIC(MemoryPokeIntNative)
-UNIMPLEMENTED_INTRINSIC(MemoryPokeLongNative)
-UNIMPLEMENTED_INTRINSIC(MemoryPokeShortNative)
UNIMPLEMENTED_INTRINSIC(ThreadCurrentThread)
UNIMPLEMENTED_INTRINSIC(UnsafeGet)
UNIMPLEMENTED_INTRINSIC(UnsafeGetVolatile)
@@ -983,7 +1188,6 @@ UNIMPLEMENTED_INTRINSIC(UnsafePutLongVolatile)
UNIMPLEMENTED_INTRINSIC(UnsafeCASInt)
UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)
UNIMPLEMENTED_INTRINSIC(UnsafeCASObject)
-UNIMPLEMENTED_INTRINSIC(StringCharAt)
UNIMPLEMENTED_INTRINSIC(StringCompareTo)
UNIMPLEMENTED_INTRINSIC(StringIndexOf)
UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
@@ -1016,8 +1220,6 @@ UNIMPLEMENTED_INTRINSIC(MathTanh)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1025,6 +1227,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerSignum)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index ac2850342d..cf3a3657de 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1764,8 +1764,6 @@ UNIMPLEMENTED_INTRINSIC(MathTanh)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1773,6 +1771,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerSignum)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index ab4f6f9d28..260a8773fb 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -261,7 +261,8 @@ static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) {
locations->SetOut(Location::SameAsFirstInput());
HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect();
DCHECK(static_or_direct != nullptr);
- if (invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
+ if (static_or_direct->HasSpecialInput() &&
+ invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
// We need addressibility for the constant area.
locations->SetInAt(1, Location::RequiresRegister());
// We need a temporary to hold the constant.
@@ -276,7 +277,7 @@ static void MathAbsFP(LocationSummary* locations,
Location output = locations->Out();
DCHECK(output.IsFpuRegister());
- if (locations->InAt(1).IsValid()) {
+ if (locations->GetInputCount() == 2 && locations->InAt(1).IsValid()) {
DCHECK(locations->InAt(1).IsRegister());
// We also have a constant area pointer.
Register constant_area = locations->InAt(1).AsRegister<Register>();
@@ -465,7 +466,7 @@ static void GenMinMaxFP(LocationSummary* locations,
// NaN handling.
__ Bind(&nan);
// Do we have a constant area pointer?
- if (locations->InAt(2).IsValid()) {
+ if (locations->GetInputCount() == 3 && locations->InAt(2).IsValid()) {
DCHECK(locations->InAt(2).IsRegister());
Register constant_area = locations->InAt(2).AsRegister<Register>();
if (is_double) {
@@ -510,7 +511,8 @@ static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
locations->SetOut(Location::SameAsFirstInput());
HInvokeStaticOrDirect* static_or_direct = invoke->AsInvokeStaticOrDirect();
DCHECK(static_or_direct != nullptr);
- if (invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
+ if (static_or_direct->HasSpecialInput() &&
+ invoke->InputAt(static_or_direct->GetSpecialInputIndex())->IsX86ComputeBaseMethodAddress()) {
locations->SetInAt(2, Location::RequiresRegister());
}
}
@@ -2633,8 +2635,6 @@ UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -2642,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index c9a43442b3..93e8c00e5a 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2717,10 +2717,10 @@ UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index a6b4078f46..33bb2e8f30 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -141,6 +141,7 @@ void LICM::Run() {
DCHECK(!instruction->HasEnvironment());
}
instruction->MoveBefore(pre_header->GetLastInstruction());
+ MaybeRecordStat(MethodCompilationStat::kLoopInvariantMoved);
} else if (instruction->CanThrow()) {
// If `instruction` can throw, we cannot move further instructions
// that can throw as well.
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
index 0b5a0f103b..bf56f53d46 100644
--- a/compiler/optimizing/licm.h
+++ b/compiler/optimizing/licm.h
@@ -26,8 +26,9 @@ class SideEffectsAnalysis;
class LICM : public HOptimization {
public:
- LICM(HGraph* graph, const SideEffectsAnalysis& side_effects)
- : HOptimization(graph, kLoopInvariantCodeMotionPassName), side_effects_(side_effects) {}
+ LICM(HGraph* graph, const SideEffectsAnalysis& side_effects, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kLoopInvariantCodeMotionPassName, stats),
+ side_effects_(side_effects) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 9fb32f4001..d446539700 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -79,7 +79,7 @@ class LICMTest : public CommonCompilerTest {
graph_->BuildDominatorTree();
SideEffectsAnalysis side_effects(graph_);
side_effects.Run();
- LICM(graph_, side_effects).Run();
+ LICM(graph_, side_effects, nullptr).Run();
}
// General building fields.
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ca66f631a6..f36dc6e2fd 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -15,6 +15,8 @@
*/
#include "nodes.h"
+#include <cfloat>
+
#include "code_generator.h"
#include "common_dominator.h"
#include "ssa_builder.h"
@@ -27,6 +29,12 @@
namespace art {
+// Enable floating-point static evaluation during constant folding
+// only if all floating-point operations and constants evaluate in the
+// range and precision of the type used (i.e., 32-bit float, 64-bit
+// double).
+static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == 0);
+
void HGraph::InitializeInexactObjectRTI(StackHandleScopeCollection* handles) {
ScopedObjectAccess soa(Thread::Current());
// Create the inexact Object reference type and store it in the HGraph.
@@ -1159,6 +1167,12 @@ HConstant* HUnaryOperation::TryStaticEvaluation() const {
return Evaluate(GetInput()->AsIntConstant());
} else if (GetInput()->IsLongConstant()) {
return Evaluate(GetInput()->AsLongConstant());
+ } else if (kEnableFloatingPointStaticEvaluation) {
+ if (GetInput()->IsFloatConstant()) {
+ return Evaluate(GetInput()->AsFloatConstant());
+ } else if (GetInput()->IsDoubleConstant()) {
+ return Evaluate(GetInput()->AsDoubleConstant());
+ }
}
return nullptr;
}
@@ -1178,6 +1192,12 @@ HConstant* HBinaryOperation::TryStaticEvaluation() const {
}
} else if (GetLeft()->IsNullConstant() && GetRight()->IsNullConstant()) {
return Evaluate(GetLeft()->AsNullConstant(), GetRight()->AsNullConstant());
+ } else if (kEnableFloatingPointStaticEvaluation) {
+ if (GetLeft()->IsFloatConstant() && GetRight()->IsFloatConstant()) {
+ return Evaluate(GetLeft()->AsFloatConstant(), GetRight()->AsFloatConstant());
+ } else if (GetLeft()->IsDoubleConstant() && GetRight()->IsDoubleConstant()) {
+ return Evaluate(GetLeft()->AsDoubleConstant(), GetRight()->AsDoubleConstant());
+ }
}
return nullptr;
}
@@ -1205,6 +1225,20 @@ HInstruction* HBinaryOperation::GetLeastConstantLeft() const {
}
}
+std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs) {
+ switch (rhs) {
+ case ComparisonBias::kNoBias:
+ return os << "no_bias";
+ case ComparisonBias::kGtBias:
+ return os << "gt_bias";
+ case ComparisonBias::kLtBias:
+ return os << "lt_bias";
+ default:
+ LOG(FATAL) << "Unknown ComparisonBias: " << static_cast<int>(rhs);
+ UNREACHABLE();
+ }
+}
+
bool HCondition::IsBeforeWhenDisregardMoves(HInstruction* instruction) const {
return this == instruction->GetPreviousDisregardingMoves();
}
@@ -1386,7 +1420,38 @@ HBasicBlock* HBasicBlock::SplitCatchBlockAfterMoveException() {
}
}
-HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
+HBasicBlock* HBasicBlock::SplitBeforeForInlining(HInstruction* cursor) {
+ DCHECK_EQ(cursor->GetBlock(), this);
+
+ HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(),
+ cursor->GetDexPc());
+ new_block->instructions_.first_instruction_ = cursor;
+ new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
+ instructions_.last_instruction_ = cursor->previous_;
+ if (cursor->previous_ == nullptr) {
+ instructions_.first_instruction_ = nullptr;
+ } else {
+ cursor->previous_->next_ = nullptr;
+ cursor->previous_ = nullptr;
+ }
+
+ new_block->instructions_.SetBlockOfInstructions(new_block);
+
+ for (HBasicBlock* successor : GetSuccessors()) {
+ new_block->successors_.push_back(successor);
+ successor->predecessors_[successor->GetPredecessorIndexOf(this)] = new_block;
+ }
+ successors_.clear();
+
+ for (HBasicBlock* dominated : GetDominatedBlocks()) {
+ dominated->dominator_ = new_block;
+ new_block->dominated_blocks_.push_back(dominated);
+ }
+ dominated_blocks_.clear();
+ return new_block;
+}
+
+HBasicBlock* HBasicBlock::SplitAfterForInlining(HInstruction* cursor) {
DCHECK(!cursor->IsControlFlow());
DCHECK_NE(instructions_.last_instruction_, cursor);
DCHECK_EQ(cursor->GetBlock(), this);
@@ -1539,6 +1604,20 @@ void HInstructionList::AddAfter(HInstruction* cursor, const HInstructionList& in
}
}
+void HInstructionList::AddBefore(HInstruction* cursor, const HInstructionList& instruction_list) {
+ DCHECK(Contains(cursor));
+ if (!instruction_list.IsEmpty()) {
+ if (cursor == first_instruction_) {
+ first_instruction_ = instruction_list.first_instruction_;
+ } else {
+ cursor->previous_->next_ = instruction_list.first_instruction_;
+ }
+ instruction_list.last_instruction_->next_ = cursor;
+ instruction_list.first_instruction_->previous_ = cursor->previous_;
+ cursor->previous_ = instruction_list.last_instruction_;
+ }
+}
+
void HInstructionList::Add(const HInstructionList& instruction_list) {
if (IsEmpty()) {
first_instruction_ = instruction_list.first_instruction_;
@@ -1781,18 +1860,6 @@ void HBasicBlock::ReplaceWith(HBasicBlock* other) {
graph_ = nullptr;
}
-// Create space in `blocks` for adding `number_of_new_blocks` entries
-// starting at location `at`. Blocks after `at` are moved accordingly.
-static void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks,
- size_t number_of_new_blocks,
- size_t after) {
- DCHECK_LT(after, blocks->size());
- size_t old_size = blocks->size();
- size_t new_size = old_size + number_of_new_blocks;
- blocks->resize(new_size);
- std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
-}
-
void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) {
DCHECK_EQ(block->GetGraph(), this);
DCHECK(block->GetSuccessors().empty());
@@ -1846,7 +1913,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
DCHECK(!body->IsInLoop());
HInstruction* last = body->GetLastInstruction();
- invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions());
+ // Note that we add instructions before the invoke only to simplify polymorphic inlining.
+ invoke->GetBlock()->instructions_.AddBefore(invoke, body->GetInstructions());
body->GetInstructions().SetBlockOfInstructions(invoke->GetBlock());
// Replace the invoke with the return value of the inlined graph.
@@ -1864,7 +1932,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
// with the second half.
ArenaAllocator* allocator = outer_graph->GetArena();
HBasicBlock* at = invoke->GetBlock();
- HBasicBlock* to = at->SplitAfter(invoke);
+ // Note that we split before the invoke only to simplify polymorphic inlining.
+ HBasicBlock* to = at->SplitBeforeForInlining(invoke);
HBasicBlock* first = entry_block_->GetSuccessors()[0];
DCHECK(!first->IsInLoop());
@@ -2030,13 +2099,6 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
}
}
- if (return_value != nullptr) {
- invoke->ReplaceWith(return_value);
- }
-
- // Finally remove the invoke from the caller.
- invoke->GetBlock()->RemoveInstruction(invoke);
-
return return_value;
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 18b256f48e..399afabea6 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -131,6 +131,7 @@ class HInstructionList : public ValueObject {
void SetBlockOfInstructions(HBasicBlock* block) const;
void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list);
+ void AddBefore(HInstruction* cursor, const HInstructionList& instruction_list);
void Add(const HInstructionList& instruction_list);
// Return the number of instructions in the list. This is an expensive operation.
@@ -345,8 +346,9 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
void ComputeTryBlockInformation();
// Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
- // Returns the instruction used to replace the invoke expression or null if the
- // invoke is for a void method.
+ // Returns the instruction to replace the invoke expression or null if the
+ // invoke is for a void method. Note that the caller is responsible for replacing
+ // and removing the invoke instruction.
HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke);
// Need to add a couple of blocks to test if the loop body is entered and
@@ -617,6 +619,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
friend class SsaBuilder; // For caching constants.
friend class SsaLivenessAnalysis; // For the linear order.
+ friend class HInliner; // For the reverse post order.
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
@@ -971,12 +974,15 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> {
// loop and try/catch information.
HBasicBlock* SplitBefore(HInstruction* cursor);
- // Split the block into two blocks just after `cursor`. Returns the newly
+ // Split the block into two blocks just before `cursor`. Returns the newly
// created block. Note that this method just updates raw block information,
// like predecessors, successors, dominators, and instruction list. It does not
// update the graph, reverse post order, loop information, nor make sure the
// blocks are consistent (for example ending with a control flow instruction).
- HBasicBlock* SplitAfter(HInstruction* cursor);
+ HBasicBlock* SplitBeforeForInlining(HInstruction* cursor);
+
+ // Similar to `SplitBeforeForInlining` but does it after `cursor`.
+ HBasicBlock* SplitAfterForInlining(HInstruction* cursor);
// Split catch block into two blocks after the original move-exception bytecode
// instruction, or at the beginning if not present. Returns the newly created,
@@ -2062,6 +2068,7 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
}
SideEffects GetSideEffects() const { return side_effects_; }
+ void SetSideEffects(SideEffects other) { side_effects_ = other; }
void AddSideEffects(SideEffects other) { side_effects_.Add(other); }
size_t GetLifetimePosition() const { return lifetime_position_; }
@@ -2100,7 +2107,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
- void SetSideEffects(SideEffects other) { side_effects_ = other; }
private:
void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
@@ -2393,7 +2399,7 @@ class HIntConstant : public HConstant {
}
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- DCHECK(other->IsIntConstant());
+ DCHECK(other->IsIntConstant()) << other->DebugName();
return other->AsIntConstant()->value_ == value_;
}
@@ -2426,7 +2432,7 @@ class HLongConstant : public HConstant {
uint64_t GetValueAsUint64() const OVERRIDE { return value_; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- DCHECK(other->IsLongConstant());
+ DCHECK(other->IsLongConstant()) << other->DebugName();
return other->AsLongConstant()->value_ == value_;
}
@@ -2448,6 +2454,92 @@ class HLongConstant : public HConstant {
DISALLOW_COPY_AND_ASSIGN(HLongConstant);
};
+class HFloatConstant : public HConstant {
+ public:
+ float GetValue() const { return value_; }
+
+ uint64_t GetValueAsUint64() const OVERRIDE {
+ return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_));
+ }
+
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ DCHECK(other->IsFloatConstant()) << other->DebugName();
+ return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64();
+ }
+
+ size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
+
+ bool IsMinusOne() const OVERRIDE {
+ return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f));
+ }
+ bool IsZero() const OVERRIDE {
+ return value_ == 0.0f;
+ }
+ bool IsOne() const OVERRIDE {
+ return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(1.0f);
+ }
+ bool IsNaN() const {
+ return std::isnan(value_);
+ }
+
+ DECLARE_INSTRUCTION(FloatConstant);
+
+ private:
+ explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimFloat, dex_pc), value_(value) {}
+ explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimFloat, dex_pc), value_(bit_cast<float, int32_t>(value)) {}
+
+ const float value_;
+
+ // Only the SsaBuilder and HGraph can create floating-point constants.
+ friend class SsaBuilder;
+ friend class HGraph;
+ DISALLOW_COPY_AND_ASSIGN(HFloatConstant);
+};
+
+class HDoubleConstant : public HConstant {
+ public:
+ double GetValue() const { return value_; }
+
+ uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); }
+
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ DCHECK(other->IsDoubleConstant()) << other->DebugName();
+ return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64();
+ }
+
+ size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
+
+ bool IsMinusOne() const OVERRIDE {
+ return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0));
+ }
+ bool IsZero() const OVERRIDE {
+ return value_ == 0.0;
+ }
+ bool IsOne() const OVERRIDE {
+ return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>(1.0);
+ }
+ bool IsNaN() const {
+ return std::isnan(value_);
+ }
+
+ DECLARE_INSTRUCTION(DoubleConstant);
+
+ private:
+ explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimDouble, dex_pc), value_(value) {}
+ explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
+ : HConstant(Primitive::kPrimDouble, dex_pc), value_(bit_cast<double, int64_t>(value)) {}
+
+ const double value_;
+
+ // Only the SsaBuilder and HGraph can create floating-point constants.
+ friend class SsaBuilder;
+ friend class HGraph;
+ DISALLOW_COPY_AND_ASSIGN(HDoubleConstant);
+};
+
// Conditional branch. A block ending with an HIf instruction must have
// two successors.
class HIf : public HTemplateInstruction<1> {
@@ -2649,14 +2741,16 @@ class HUnaryOperation : public HExpression<1> {
return true;
}
- // Try to statically evaluate `operation` and return a HConstant
- // containing the result of this evaluation. If `operation` cannot
+ // Try to statically evaluate `this` and return a HConstant
+ // containing the result of this evaluation. If `this` cannot
// be evaluated as a constant, return null.
HConstant* TryStaticEvaluation() const;
// Apply this operation to `x`.
virtual HConstant* Evaluate(HIntConstant* x) const = 0;
virtual HConstant* Evaluate(HLongConstant* x) const = 0;
+ virtual HConstant* Evaluate(HFloatConstant* x) const = 0;
+ virtual HConstant* Evaluate(HDoubleConstant* x) const = 0;
DECLARE_ABSTRACT_INSTRUCTION(UnaryOperation);
@@ -2719,12 +2813,17 @@ class HBinaryOperation : public HExpression<2> {
return true;
}
- // Try to statically evaluate `operation` and return a HConstant
- // containing the result of this evaluation. If `operation` cannot
+ // Try to statically evaluate `this` and return a HConstant
+ // containing the result of this evaluation. If `this` cannot
// be evaluated as a constant, return null.
HConstant* TryStaticEvaluation() const;
// Apply this operation to `x` and `y`.
+ virtual HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED,
+ HNullConstant* y ATTRIBUTE_UNUSED) const {
+ VLOG(compiler) << DebugName() << " is not defined for the (null, null) case.";
+ return nullptr;
+ }
virtual HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const = 0;
virtual HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const = 0;
virtual HConstant* Evaluate(HIntConstant* x ATTRIBUTE_UNUSED,
@@ -2737,11 +2836,8 @@ class HBinaryOperation : public HExpression<2> {
VLOG(compiler) << DebugName() << " is not defined for the (long, int) case.";
return nullptr;
}
- virtual HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED,
- HNullConstant* y ATTRIBUTE_UNUSED) const {
- VLOG(compiler) << DebugName() << " is not defined for the (null, null) case.";
- return nullptr;
- }
+ virtual HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const = 0;
+ virtual HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const = 0;
// Returns an input that can legally be used as the right input and is
// constant, or null.
@@ -2765,6 +2861,8 @@ enum class ComparisonBias {
kLtBias, // return -1 for NaN comparisons
};
+std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs);
+
class HCondition : public HBinaryOperation {
public:
HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
@@ -2782,7 +2880,7 @@ class HCondition : public HBinaryOperation {
virtual IfCondition GetOppositeCondition() const = 0;
bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; }
-
+ ComparisonBias GetBias() const { return bias_; }
void SetBias(ComparisonBias bias) { bias_ = bias; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -2790,17 +2888,34 @@ class HCondition : public HBinaryOperation {
}
bool IsFPConditionTrueIfNaN() const {
- DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType()));
+ DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
IfCondition if_cond = GetCondition();
return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE);
}
bool IsFPConditionFalseIfNaN() const {
- DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType()));
+ DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
IfCondition if_cond = GetCondition();
return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ);
}
+ protected:
+ template <typename T>
+ int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); }
+
+ template <typename T>
+ int32_t CompareFP(T x, T y) const {
+ DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
+ DCHECK_NE(GetBias(), ComparisonBias::kNoBias);
+ // Handle the bias.
+ return std::isunordered(x, y) ? (IsGtBias() ? 1 : -1) : Compare(x, y);
+ }
+
+ // Return an integer constant containing the result of a condition evaluated at compile time.
+ HIntConstant* MakeConstantCondition(bool value, uint32_t dex_pc) const {
+ return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc);
+ }
+
private:
// Needed if we merge a HCompare into a HCondition.
ComparisonBias bias_;
@@ -2816,17 +2931,25 @@ class HEqual : public HCondition {
bool IsCommutative() const OVERRIDE { return true; }
+ HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED,
+ HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ return MakeConstantCondition(true, GetDexPc());
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ // In the following Evaluate methods, a HCompare instruction has
+ // been merged into this HEqual instruction; evaluate it as
+ // `Compare(x, y) == 0`.
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0),
+ GetDexPc());
}
- HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED,
- HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(1);
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
}
DECLARE_INSTRUCTION(Equal);
@@ -2852,17 +2975,24 @@ class HNotEqual : public HCondition {
bool IsCommutative() const OVERRIDE { return true; }
+ HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED,
+ HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ return MakeConstantCondition(false, GetDexPc());
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ // In the following Evaluate methods, a HCompare instruction has
+ // been merged into this HNotEqual instruction; evaluate it as
+ // `Compare(x, y) != 0`.
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc());
}
- HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED,
- HNullConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(0);
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
}
DECLARE_INSTRUCTION(NotEqual);
@@ -2887,12 +3017,19 @@ class HLessThan : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ // In the following Evaluate methods, a HCompare instruction has
+ // been merged into this HLessThan instruction; evaluate it as
+ // `Compare(x, y) < 0`.
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
}
DECLARE_INSTRUCTION(LessThan);
@@ -2917,12 +3054,19 @@ class HLessThanOrEqual : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ // In the following Evaluate methods, a HCompare instruction has
+ // been merged into this HLessThanOrEqual instruction; evaluate it as
+ // `Compare(x, y) <= 0`.
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
}
DECLARE_INSTRUCTION(LessThanOrEqual);
@@ -2947,12 +3091,19 @@ class HGreaterThan : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ // In the following Evaluate methods, a HCompare instruction has
+ // been merged into this HGreaterThan instruction; evaluate it as
+ // `Compare(x, y) > 0`.
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
}
DECLARE_INSTRUCTION(GreaterThan);
@@ -2977,12 +3128,19 @@ class HGreaterThanOrEqual : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ // In the following Evaluate methods, a HCompare instruction has
+ // been merged into this HGreaterThanOrEqual instruction; evaluate it as
+ // `Compare(x, y) >= 0`.
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantCondition(Compute(Compare(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return MakeConstantCondition(Compute(CompareFP(x->GetValue(), y->GetValue()), 0), GetDexPc());
}
DECLARE_INSTRUCTION(GreaterThanOrEqual);
@@ -3007,14 +3165,20 @@ class HBelow : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint32_t>(x->GetValue()),
- static_cast<uint32_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint64_t>(x->GetValue()),
- static_cast<uint64_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
}
DECLARE_INSTRUCTION(Below);
@@ -3028,7 +3192,9 @@ class HBelow : public HCondition {
}
private:
- template <typename T> bool Compute(T x, T y) const { return x < y; }
+ template <typename T> bool Compute(T x, T y) const {
+ return MakeUnsigned(x) < MakeUnsigned(y);
+ }
DISALLOW_COPY_AND_ASSIGN(HBelow);
};
@@ -3039,14 +3205,20 @@ class HBelowOrEqual : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint32_t>(x->GetValue()),
- static_cast<uint32_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint64_t>(x->GetValue()),
- static_cast<uint64_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
}
DECLARE_INSTRUCTION(BelowOrEqual);
@@ -3060,7 +3232,9 @@ class HBelowOrEqual : public HCondition {
}
private:
- template <typename T> bool Compute(T x, T y) const { return x <= y; }
+ template <typename T> bool Compute(T x, T y) const {
+ return MakeUnsigned(x) <= MakeUnsigned(y);
+ }
DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual);
};
@@ -3071,14 +3245,20 @@ class HAbove : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint32_t>(x->GetValue()),
- static_cast<uint32_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint64_t>(x->GetValue()),
- static_cast<uint64_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
}
DECLARE_INSTRUCTION(Above);
@@ -3092,7 +3272,9 @@ class HAbove : public HCondition {
}
private:
- template <typename T> bool Compute(T x, T y) const { return x > y; }
+ template <typename T> bool Compute(T x, T y) const {
+ return MakeUnsigned(x) > MakeUnsigned(y);
+ }
DISALLOW_COPY_AND_ASSIGN(HAbove);
};
@@ -3103,14 +3285,20 @@ class HAboveOrEqual : public HCondition {
: HCondition(first, second, dex_pc) {}
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint32_t>(x->GetValue()),
- static_cast<uint32_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(static_cast<uint64_t>(x->GetValue()),
- static_cast<uint64_t>(y->GetValue())), GetDexPc());
+ return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
}
DECLARE_INSTRUCTION(AboveOrEqual);
@@ -3124,7 +3312,9 @@ class HAboveOrEqual : public HCondition {
}
private:
- template <typename T> bool Compute(T x, T y) const { return x >= y; }
+ template <typename T> bool Compute(T x, T y) const {
+ return MakeUnsigned(x) >= MakeUnsigned(y);
+ }
DISALLOW_COPY_AND_ASSIGN(HAboveOrEqual);
};
@@ -3149,15 +3339,32 @@ class HCompare : public HBinaryOperation {
}
template <typename T>
- int32_t Compute(T x, T y) const { return x == y ? 0 : x > y ? 1 : -1; }
+ int32_t Compute(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); }
+
+ template <typename T>
+ int32_t ComputeFP(T x, T y) const {
+ DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
+ DCHECK_NE(GetBias(), ComparisonBias::kNoBias);
+ // Handle the bias.
+ return std::isunordered(x, y) ? (IsGtBias() ? 1 : -1) : Compute(x, y);
+ }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ // Note that there is no "cmp-int" Dex instruction so we shouldn't
+ // reach this code path when processing a freshly built HIR
+ // graph. However HCompare integer instructions can be synthesized
+ // by the instruction simplifier to implement IntegerCompare and
+ // IntegerSignum intrinsics, so we have to handle this case.
+ return MakeConstantComparison(Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ return MakeConstantComparison(Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return MakeConstantComparison(ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return MakeConstantComparison(ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
}
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
@@ -3166,8 +3373,12 @@ class HCompare : public HBinaryOperation {
ComparisonBias GetBias() const { return bias_; }
- bool IsGtBias() { return bias_ == ComparisonBias::kGtBias; }
-
+ // Does this compare instruction have a "gt bias" (vs an "lt bias")?
+ // Only meaninfgul for floating-point comparisons.
+ bool IsGtBias() const {
+ DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
+ return bias_ == ComparisonBias::kGtBias;
+ }
static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) {
// MIPS64 uses a runtime call for FP comparisons.
@@ -3176,6 +3387,13 @@ class HCompare : public HBinaryOperation {
DECLARE_INSTRUCTION(Compare);
+ protected:
+ // Return an integer constant containing the result of a comparison evaluated at compile time.
+ HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const {
+ DCHECK(value == -1 || value == 0 || value == 1) << value;
+ return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc);
+ }
+
private:
const ComparisonBias bias_;
@@ -3233,92 +3451,6 @@ class HStoreLocal : public HTemplateInstruction<2> {
DISALLOW_COPY_AND_ASSIGN(HStoreLocal);
};
-class HFloatConstant : public HConstant {
- public:
- float GetValue() const { return value_; }
-
- uint64_t GetValueAsUint64() const OVERRIDE {
- return static_cast<uint64_t>(bit_cast<uint32_t, float>(value_));
- }
-
- bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- DCHECK(other->IsFloatConstant());
- return other->AsFloatConstant()->GetValueAsUint64() == GetValueAsUint64();
- }
-
- size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
-
- bool IsMinusOne() const OVERRIDE {
- return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f));
- }
- bool IsZero() const OVERRIDE {
- return value_ == 0.0f;
- }
- bool IsOne() const OVERRIDE {
- return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(1.0f);
- }
- bool IsNaN() const {
- return std::isnan(value_);
- }
-
- DECLARE_INSTRUCTION(FloatConstant);
-
- private:
- explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
- : HConstant(Primitive::kPrimFloat, dex_pc), value_(value) {}
- explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
- : HConstant(Primitive::kPrimFloat, dex_pc), value_(bit_cast<float, int32_t>(value)) {}
-
- const float value_;
-
- // Only the SsaBuilder and HGraph can create floating-point constants.
- friend class SsaBuilder;
- friend class HGraph;
- DISALLOW_COPY_AND_ASSIGN(HFloatConstant);
-};
-
-class HDoubleConstant : public HConstant {
- public:
- double GetValue() const { return value_; }
-
- uint64_t GetValueAsUint64() const OVERRIDE { return bit_cast<uint64_t, double>(value_); }
-
- bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- DCHECK(other->IsDoubleConstant());
- return other->AsDoubleConstant()->GetValueAsUint64() == GetValueAsUint64();
- }
-
- size_t ComputeHashCode() const OVERRIDE { return static_cast<size_t>(GetValue()); }
-
- bool IsMinusOne() const OVERRIDE {
- return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0));
- }
- bool IsZero() const OVERRIDE {
- return value_ == 0.0;
- }
- bool IsOne() const OVERRIDE {
- return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>(1.0);
- }
- bool IsNaN() const {
- return std::isnan(value_);
- }
-
- DECLARE_INSTRUCTION(DoubleConstant);
-
- private:
- explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
- : HConstant(Primitive::kPrimDouble, dex_pc), value_(value) {}
- explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
- : HConstant(Primitive::kPrimDouble, dex_pc), value_(bit_cast<double, int64_t>(value)) {}
-
- const double value_;
-
- // Only the SsaBuilder and HGraph can create floating-point constants.
- friend class SsaBuilder;
- friend class HGraph;
- DISALLOW_COPY_AND_ASSIGN(HDoubleConstant);
-};
-
class HNewInstance : public HExpression<2> {
public:
HNewInstance(HInstruction* cls,
@@ -3671,6 +3803,7 @@ class HInvokeStaticOrDirect : public HInvoke {
// method pointer; otherwise there may be one platform-specific special input,
// such as PC-relative addressing base.
uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); }
+ bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); }
InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; }
void SetOptimizedInvokeType(InvokeType invoke_type) {
@@ -3869,6 +4002,12 @@ class HNeg : public HUnaryOperation {
HConstant* Evaluate(HLongConstant* x) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetFloatConstant(Compute(x->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetDoubleConstant(Compute(x->GetValue()), GetDexPc());
+ }
DECLARE_INSTRUCTION(Neg);
@@ -3935,6 +4074,14 @@ class HAdd : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetFloatConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetDoubleConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
DECLARE_INSTRUCTION(Add);
@@ -3960,6 +4107,14 @@ class HSub : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetFloatConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetDoubleConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
DECLARE_INSTRUCTION(Sub);
@@ -3987,6 +4142,14 @@ class HMul : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetFloatConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetDoubleConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
DECLARE_INSTRUCTION(Mul);
@@ -4003,7 +4166,8 @@ class HDiv : public HBinaryOperation {
: HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
template <typename T>
- T Compute(T x, T y) const {
+ T ComputeIntegral(T x, T y) const {
+ DCHECK(!Primitive::IsFloatingPointType(GetType())) << GetType();
// Our graph structure ensures we never have 0 for `y` during
// constant folding.
DCHECK_NE(y, 0);
@@ -4011,13 +4175,27 @@ class HDiv : public HBinaryOperation {
return (y == -1) ? -x : x / y;
}
+ template <typename T>
+ T ComputeFP(T x, T y) const {
+ DCHECK(Primitive::IsFloatingPointType(GetType())) << GetType();
+ return x / y;
+ }
+
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetFloatConstant(
+ ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetDoubleConstant(
+ ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
}
static SideEffects SideEffectsForArchRuntimeCalls() {
@@ -4040,7 +4218,8 @@ class HRem : public HBinaryOperation {
: HBinaryOperation(result_type, left, right, SideEffectsForArchRuntimeCalls(), dex_pc) {}
template <typename T>
- T Compute(T x, T y) const {
+ T ComputeIntegral(T x, T y) const {
+ DCHECK(!Primitive::IsFloatingPointType(GetType())) << GetType();
// Our graph structure ensures we never have 0 for `y` during
// constant folding.
DCHECK_NE(y, 0);
@@ -4048,15 +4227,28 @@ class HRem : public HBinaryOperation {
return (y == -1) ? 0 : x % y;
}
+ template <typename T>
+ T ComputeFP(T x, T y) const {
+ DCHECK(Primitive::IsFloatingPointType(GetType())) << GetType();
+ return std::fmod(x, y);
+ }
+
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ ComputeIntegral(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetFloatConstant(
+ ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetDoubleConstant(
+ ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
}
-
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
@@ -4123,6 +4315,16 @@ class HShl : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(Shl);
@@ -4159,6 +4361,16 @@ class HShr : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(Shr);
@@ -4196,6 +4408,16 @@ class HUShr : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(UShr);
@@ -4232,6 +4454,16 @@ class HAnd : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(And);
@@ -4268,6 +4500,16 @@ class HOr : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(Or);
@@ -4304,6 +4546,16 @@ class HXor : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(Xor);
@@ -4342,6 +4594,16 @@ class HRor : public HBinaryOperation {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(Ror);
@@ -4408,6 +4670,14 @@ class HNot : public HUnaryOperation {
HConstant* Evaluate(HLongConstant* x) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(Not);
@@ -4426,7 +4696,7 @@ class HBooleanNot : public HUnaryOperation {
}
template <typename T> bool Compute(T x) const {
- DCHECK(IsUint<1>(x));
+ DCHECK(IsUint<1>(x)) << x;
return !x;
}
@@ -4437,6 +4707,14 @@ class HBooleanNot : public HUnaryOperation {
LOG(FATAL) << DebugName() << " is not defined for long values";
UNREACHABLE();
}
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED) const OVERRIDE {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
DECLARE_INSTRUCTION(BooleanNot);
@@ -4784,10 +5062,10 @@ class HArrayGet : public HExpression<2> {
DCHECK_EQ(GetArray(), other->GetArray());
DCHECK_EQ(GetIndex(), other->GetIndex());
if (Primitive::IsIntOrLongType(GetType())) {
- DCHECK(Primitive::IsFloatingPointType(other->GetType()));
+ DCHECK(Primitive::IsFloatingPointType(other->GetType())) << other->GetType();
} else {
- DCHECK(Primitive::IsFloatingPointType(GetType()));
- DCHECK(Primitive::IsIntOrLongType(other->GetType()));
+ DCHECK(Primitive::IsFloatingPointType(GetType())) << GetType();
+ DCHECK(Primitive::IsIntOrLongType(other->GetType())) << other->GetType();
}
}
return result;
@@ -6002,7 +6280,7 @@ inline int64_t Int64FromConstant(HConstant* constant) {
} else if (constant->IsLongConstant()) {
return constant->AsLongConstant()->GetValue();
} else {
- DCHECK(constant->IsNullConstant());
+ DCHECK(constant->IsNullConstant()) << constant->DebugName();
return 0;
}
}
@@ -6097,6 +6375,18 @@ class SwitchTable : public ValueObject {
DISALLOW_COPY_AND_ASSIGN(SwitchTable);
};
+// Create space in `blocks` for adding `number_of_new_blocks` entries
+// starting at location `at`. Blocks after `at` are moved accordingly.
+inline void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks,
+ size_t number_of_new_blocks,
+ size_t after) {
+ DCHECK_LT(after, blocks->size());
+ size_t old_size = blocks->size();
+ size_t new_size = old_size + number_of_new_blocks;
+ blocks->resize(new_size);
+ std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
+}
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 12b748b7b6..b1891c979e 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -505,12 +505,12 @@ static void RunOptimizations(HGraph* graph,
graph, stats, HDeadCodeElimination::kFinalDeadCodeEliminationPassName);
HConstantFolding* fold1 = new (arena) HConstantFolding(graph);
InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
- HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph);
+ HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph, stats);
HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining");
HConstantFolding* fold3 = new (arena) HConstantFolding(graph, "constant_folding_after_bce");
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects);
- LICM* licm = new (arena) LICM(graph, *side_effects);
+ LICM* licm = new (arena) LICM(graph, *side_effects, stats);
LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects);
HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects, induction);
@@ -519,7 +519,7 @@ static void RunOptimizations(HGraph* graph,
graph, stats, "instruction_simplifier_after_bce");
InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_before_codegen");
- IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver);
+ IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver, stats);
HOptimization* optimizations1[] = {
intrinsics,
@@ -651,7 +651,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
DexCompilationUnit dex_compilation_unit(
nullptr, class_loader, Runtime::Current()->GetClassLinker(), dex_file, code_item,
class_def_idx, method_idx, access_flags,
- compiler_driver->GetVerifiedMethod(&dex_file, method_idx), dex_cache);
+ nullptr, dex_cache);
bool requires_barrier = dex_compilation_unit.IsConstructor()
&& compiler_driver->RequiresConstructorBarrier(Thread::Current(),
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 52a7b10cad..179004bd40 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -56,6 +56,10 @@ enum MethodCompilationStat {
kMonomorphicCall,
kPolymorphicCall,
kMegamorphicCall,
+ kBooleanSimplified,
+ kIntrinsicRecognized,
+ kLoopInvariantMoved,
+ kSelectGenerated,
kLastStat
};
@@ -124,7 +128,11 @@ class OptimizingCompilerStats {
case kInlinedPolymorphicCall: name = "InlinedPolymorphicCall"; break;
case kMonomorphicCall: name = "MonomorphicCall"; break;
case kPolymorphicCall: name = "PolymorphicCall"; break;
- case kMegamorphicCall: name = "kMegamorphicCall"; break;
+ case kMegamorphicCall: name = "MegamorphicCall"; break;
+ case kBooleanSimplified : name = "BooleanSimplified"; break;
+ case kIntrinsicRecognized : name = "IntrinsicRecognized"; break;
+ case kLoopInvariantMoved : name = "LoopInvariantMoved"; break;
+ case kSelectGenerated : name = "SelectGenerated"; break;
case kLastStat:
LOG(FATAL) << "invalid stat "
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 324d84f3db..0ad104eaa7 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -138,15 +138,7 @@ bool PrepareForRegisterAllocation::CanEmitConditionAt(HCondition* condition,
}
if (user->IsSelect() && user->AsSelect()->GetCondition() == condition) {
- if (GetGraph()->GetInstructionSet() == kX86) {
- // Long values and long condition inputs result in 8 required core registers.
- // We don't have that many on x86. Materialize the condition in such case.
- return user->GetType() != Primitive::kPrimLong ||
- condition->InputAt(1)->GetType() != Primitive::kPrimLong ||
- condition->InputAt(1)->IsConstant();
- } else {
- return true;
- }
+ return true;
}
return false;
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index 105b30ae5d..e52476ea03 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -141,6 +141,8 @@ void HSelectGenerator::Run() {
block->MergeWith(merge_block);
}
+ MaybeRecordStat(MethodCompilationStat::kSelectGenerated);
+
// No need to update dominance information, as we are simplifying
// a simple diamond shape, where the join block is merged with the
// entry block. Any following blocks would have had the join block
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
index f9d6d4d8de..c6dca581cc 100644
--- a/compiler/optimizing/select_generator.h
+++ b/compiler/optimizing/select_generator.h
@@ -47,8 +47,8 @@ namespace art {
class HSelectGenerator : public HOptimization {
public:
- explicit HSelectGenerator(HGraph* graph)
- : HOptimization(graph, kSelectGeneratorPassName) {}
+ HSelectGenerator(HGraph* graph, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kSelectGeneratorPassName, stats) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 43f2499b24..09ca8b7b44 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -422,6 +422,34 @@ bool SsaBuilder::FixAmbiguousArrayOps() {
return true;
}
+static bool HasAliasInEnvironments(HInstruction* instruction) {
+ for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
+ !use_it.Done();
+ use_it.Advance()) {
+ HEnvironment* use = use_it.Current()->GetUser();
+ HUseListNode<HEnvironment*>* next = use_it.Current()->GetNext();
+ if (next != nullptr && next->GetUser() == use) {
+ return true;
+ }
+ }
+
+ if (kIsDebugBuild) {
+ // Do a quadratic search to ensure same environment uses are next
+ // to each other.
+ for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
+ !use_it.Done();
+ use_it.Advance()) {
+ HUseListNode<HEnvironment*>* current = use_it.Current();
+ HUseListNode<HEnvironment*>* next = current->GetNext();
+ while (next != nullptr) {
+ DCHECK(next->GetUser() != current->GetUser());
+ next = next->GetNext();
+ }
+ }
+ }
+ return false;
+}
+
void SsaBuilder::RemoveRedundantUninitializedStrings() {
if (GetGraph()->IsDebuggable()) {
// Do not perform the optimization for consistency with the interpreter
@@ -433,7 +461,7 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() {
// Replace NewInstance of String with NullConstant if not used prior to
// calling StringFactory. In case of deoptimization, the interpreter is
// expected to skip null check on the `this` argument of the StringFactory call.
- if (!new_instance->HasNonEnvironmentUses()) {
+ if (!new_instance->HasNonEnvironmentUses() && !HasAliasInEnvironments(new_instance)) {
new_instance->ReplaceWith(GetGraph()->GetNullConstant());
new_instance->GetBlock()->RemoveInstruction(new_instance);
diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc
deleted file mode 100644
index 85335efcc4..0000000000
--- a/compiler/profile_assistant.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "profile_assistant.h"
-
-#include "base/unix_file/fd_file.h"
-#include "os.h"
-
-namespace art {
-
-// Minimum number of new methods that profiles must contain to enable recompilation.
-static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
-
-bool ProfileAssistant::ProcessProfilesInternal(
- const std::vector<ScopedFlock>& profile_files,
- const std::vector<ScopedFlock>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info) {
- DCHECK(!profile_files.empty());
- DCHECK(!reference_profile_files.empty() ||
- (profile_files.size() == reference_profile_files.size()));
-
- std::vector<ProfileCompilationInfo> new_info(profile_files.size());
- bool should_compile = false;
- // Read the main profile files.
- for (size_t i = 0; i < new_info.size(); i++) {
- if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) {
- LOG(WARNING) << "Could not load profile file at index " << i;
- return false;
- }
- // Do we have enough new profiled methods that will make the compilation worthwhile?
- should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation);
- }
-
- if (!should_compile) {
- return true;
- }
-
- std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo());
- // Merge information.
- for (size_t i = 0; i < new_info.size(); i++) {
- if (!reference_profile_files.empty()) {
- if (!new_info[i].Load(reference_profile_files[i].GetFile()->Fd())) {
- LOG(WARNING) << "Could not load reference profile file at index " << i;
- return false;
- }
- }
- // Merge all data into a single object.
- if (!result->Load(new_info[i])) {
- LOG(WARNING) << "Could not merge profile data at index " << i;
- return false;
- }
- }
- // We were successful in merging all profile information. Update the files.
- for (size_t i = 0; i < new_info.size(); i++) {
- if (!reference_profile_files.empty()) {
- if (!reference_profile_files[i].GetFile()->ClearContent()) {
- PLOG(WARNING) << "Could not clear reference profile file at index " << i;
- return false;
- }
- if (!new_info[i].Save(reference_profile_files[i].GetFile()->Fd())) {
- LOG(WARNING) << "Could not save reference profile file at index " << i;
- return false;
- }
- if (!profile_files[i].GetFile()->ClearContent()) {
- PLOG(WARNING) << "Could not clear profile file at index " << i;
- return false;
- }
- }
- }
-
- *profile_compilation_info = result.release();
- return true;
-}
-
-class ScopedCollectionFlock {
- public:
- explicit ScopedCollectionFlock(size_t size) : flocks_(size) {}
-
- // Will block until all the locks are acquired.
- bool Init(const std::vector<std::string>& filenames, /* out */ std::string* error) {
- for (size_t i = 0; i < filenames.size(); i++) {
- if (!flocks_[i].Init(filenames[i].c_str(), O_RDWR, /* block */ true, error)) {
- *error += " (index=" + std::to_string(i) + ")";
- return false;
- }
- }
- return true;
- }
-
- // Will block until all the locks are acquired.
- bool Init(const std::vector<uint32_t>& fds, /* out */ std::string* error) {
- for (size_t i = 0; i < fds.size(); i++) {
- // We do not own the descriptor, so disable auto-close and don't check usage.
- File file(fds[i], false);
- file.DisableAutoClose();
- if (!flocks_[i].Init(&file, error)) {
- *error += " (index=" + std::to_string(i) + ")";
- return false;
- }
- }
- return true;
- }
-
- const std::vector<ScopedFlock>& Get() const { return flocks_; }
-
- private:
- std::vector<ScopedFlock> flocks_;
-};
-
-bool ProfileAssistant::ProcessProfiles(
- const std::vector<uint32_t>& profile_files_fd,
- const std::vector<uint32_t>& reference_profile_files_fd,
- /*out*/ ProfileCompilationInfo** profile_compilation_info) {
- *profile_compilation_info = nullptr;
-
- std::string error;
- ScopedCollectionFlock profile_files_flocks(profile_files_fd.size());
- if (!profile_files_flocks.Init(profile_files_fd, &error)) {
- LOG(WARNING) << "Could not lock profile files: " << error;
- return false;
- }
- ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files_fd.size());
- if (!reference_profile_files_flocks.Init(reference_profile_files_fd, &error)) {
- LOG(WARNING) << "Could not lock reference profile files: " << error;
- return false;
- }
-
- return ProcessProfilesInternal(profile_files_flocks.Get(),
- reference_profile_files_flocks.Get(),
- profile_compilation_info);
-}
-
-bool ProfileAssistant::ProcessProfiles(
- const std::vector<std::string>& profile_files,
- const std::vector<std::string>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info) {
- *profile_compilation_info = nullptr;
-
- std::string error;
- ScopedCollectionFlock profile_files_flocks(profile_files.size());
- if (!profile_files_flocks.Init(profile_files, &error)) {
- LOG(WARNING) << "Could not lock profile files: " << error;
- return false;
- }
- ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size());
- if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) {
- LOG(WARNING) << "Could not lock reference profile files: " << error;
- return false;
- }
-
- return ProcessProfilesInternal(profile_files_flocks.Get(),
- reference_profile_files_flocks.Get(),
- profile_compilation_info);
-}
-
-} // namespace art
diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h
deleted file mode 100644
index ad5e2163cf..0000000000
--- a/compiler/profile_assistant.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_PROFILE_ASSISTANT_H_
-#define ART_COMPILER_PROFILE_ASSISTANT_H_
-
-#include <string>
-#include <vector>
-
-#include "base/scoped_flock.h"
-#include "jit/offline_profiling_info.cc"
-
-namespace art {
-
-class ProfileAssistant {
- public:
- // Process the profile information present in the given files. Returns true
- // if the analysis ended up successfully (i.e. no errors during reading,
- // merging or writing of profile files).
- //
- // If the returned value is true and there is a significant difference between
- // profile_files and reference_profile_files:
- // - profile_compilation_info is set to a not null object that
- // can be used to drive compilation. It will be the merge of all the data
- // found in profile_files and reference_profile_files.
- // - the data from profile_files[i] is merged into
- // reference_profile_files[i] and the corresponding backing file is
- // updated.
- //
- // If the returned value is false or the difference is insignificant,
- // profile_compilation_info will be set to null.
- //
- // Additional notes:
- // - as mentioned above, this function may update the content of the files
- // passed with the reference_profile_files.
- // - if reference_profile_files is not empty it must be the same size as
- // profile_files.
- static bool ProcessProfiles(
- const std::vector<std::string>& profile_files,
- const std::vector<std::string>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info);
-
- static bool ProcessProfiles(
- const std::vector<uint32_t>& profile_files_fd_,
- const std::vector<uint32_t>& reference_profile_files_fd_,
- /*out*/ ProfileCompilationInfo** profile_compilation_info);
-
- private:
- static bool ProcessProfilesInternal(
- const std::vector<ScopedFlock>& profile_files,
- const std::vector<ScopedFlock>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info);
-
- DISALLOW_COPY_AND_ASSIGN(ProfileAssistant);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_PROFILE_ASSISTANT_H_
diff --git a/compiler/profile_assistant_test.cc b/compiler/profile_assistant_test.cc
deleted file mode 100644
index 58b7513377..0000000000
--- a/compiler/profile_assistant_test.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
-#include "compiler/profile_assistant.h"
-#include "jit/offline_profiling_info.h"
-
-namespace art {
-
-class ProfileAssistantTest : public CommonRuntimeTest {
- protected:
- void SetupProfile(const std::string& id,
- uint32_t checksum,
- uint16_t number_of_methods,
- const ScratchFile& profile,
- ProfileCompilationInfo* info,
- uint16_t start_method_index = 0) {
- std::string dex_location1 = "location1" + id;
- uint32_t dex_location_checksum1 = checksum;
- std::string dex_location2 = "location2" + id;
- uint32_t dex_location_checksum2 = 10 * checksum;
- for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
- ASSERT_TRUE(info->AddData(dex_location1, dex_location_checksum1, i));
- ASSERT_TRUE(info->AddData(dex_location2, dex_location_checksum2, i));
- }
- ASSERT_TRUE(info->Save(GetFd(profile)));
- ASSERT_EQ(0, profile.GetFile()->Flush());
- ASSERT_TRUE(profile.GetFile()->ResetOffset());
- }
-
- uint32_t GetFd(const ScratchFile& file) const {
- return static_cast<uint32_t>(file.GetFd());
- }
-};
-
-TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
-
- // We should advise compilation.
- ProfileCompilationInfo* result;
- ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result != nullptr);
-
- // The resulting compilation info must be equal to the merge of the inputs.
- ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Equals(*result));
-
- // The information from profiles must be transfered to the reference profiles.
- ProfileCompilationInfo file_info1;
- ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1)));
- ASSERT_TRUE(file_info1.Equals(info1));
-
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2)));
- ASSERT_TRUE(file_info2.Equals(info2));
-
- // Initial profiles must be cleared.
- ASSERT_EQ(0, profile1.GetFile()->GetLength());
- ASSERT_EQ(0, profile2.GetFile()->GetLength());
-}
-
-TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- // The new profile info will contain the methods with indices 0-100.
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
-
-
- // The reference profile info will contain the methods with indices 50-150.
- const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
- ProfileCompilationInfo reference_info1;
- SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile1,
- &reference_info1, kNumberOfMethodsToEnableCompilation / 2);
- ProfileCompilationInfo reference_info2;
- SetupProfile("p2", 2, kNumberOfMethodsAlreadyCompiled, reference_profile2,
- &reference_info2, kNumberOfMethodsToEnableCompilation / 2);
-
- // We should advise compilation.
- ProfileCompilationInfo* result;
- ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result != nullptr);
-
- // The resulting compilation info must be equal to the merge of the inputs
- ProfileCompilationInfo expected;
- ASSERT_TRUE(expected.Load(info1));
- ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Load(reference_info1));
- ASSERT_TRUE(expected.Load(reference_info2));
- ASSERT_TRUE(expected.Equals(*result));
-
- // The information from profiles must be transfered to the reference profiles.
- ProfileCompilationInfo file_info1;
- ProfileCompilationInfo merge1;
- ASSERT_TRUE(merge1.Load(info1));
- ASSERT_TRUE(merge1.Load(reference_info1));
- ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1)));
- ASSERT_TRUE(file_info1.Equals(merge1));
-
- ProfileCompilationInfo file_info2;
- ProfileCompilationInfo merge2;
- ASSERT_TRUE(merge2.Load(info2));
- ASSERT_TRUE(merge2.Load(reference_info2));
- ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2)));
- ASSERT_TRUE(file_info2.Equals(merge2));
-
- // Initial profiles must be cleared.
- ASSERT_EQ(0, profile1.GetFile()->GetLength());
- ASSERT_EQ(0, profile2.GetFile()->GetLength());
-}
-
-TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- const uint16_t kNumberOfMethodsToSkipCompilation = 1;
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2);
-
- // We should not advise compilation.
- ProfileCompilationInfo* result = nullptr;
- ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result == nullptr);
-
- // The information from profiles must remain the same.
- ProfileCompilationInfo file_info1;
- ASSERT_TRUE(profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
- ASSERT_TRUE(file_info1.Equals(info1));
-
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(profile2.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
- ASSERT_TRUE(file_info2.Equals(info2));
-
- // Reference profile files must remain empty.
- ASSERT_EQ(0, reference_profile1.GetFile()->GetLength());
- ASSERT_EQ(0, reference_profile2.GetFile()->GetLength());
-}
-
-TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
- ScratchFile profile1;
- ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1),
- GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
-
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- // Assign different hashes for the same dex file. This will make merging of information to fail.
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo info2;
- SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
-
- // We should fail processing.
- ProfileCompilationInfo* result = nullptr;
- ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result == nullptr);
-
- // The information from profiles must still remain the same.
- ProfileCompilationInfo file_info1;
- ASSERT_TRUE(profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
- ASSERT_TRUE(file_info1.Equals(info1));
-
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(profile2.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
- ASSERT_TRUE(file_info2.Equals(info2));
-
- // Reference profile files must still remain empty.
- ASSERT_EQ(0, reference_profile1.GetFile()->GetLength());
- ASSERT_EQ(0, reference_profile2.GetFile()->GetLength());
-}
-
-TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
- ScratchFile profile1;
- ScratchFile reference_profile;
-
- std::vector<uint32_t> profile_fds({
- GetFd(profile1)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile)});
-
- const uint16_t kNumberOfMethodsToEnableCompilation = 100;
- // Assign different hashes for the same dex file. This will make merging of information to fail.
- ProfileCompilationInfo info1;
- SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1);
- ProfileCompilationInfo reference_info;
- SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info);
-
- // We should not advise compilation.
- ProfileCompilationInfo* result = nullptr;
- ASSERT_TRUE(profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
- ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result == nullptr);
-
- // The information from profiles must still remain the same.
- ProfileCompilationInfo file_info1;
- ASSERT_TRUE(profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
- ASSERT_TRUE(file_info1.Equals(info1));
-
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(reference_profile)));
- ASSERT_TRUE(file_info2.Equals(reference_info));
-}
-
-} // namespace art
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index ac9c097892..6fd65ee9a4 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -426,6 +426,16 @@ void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
EmitI(0x23, rs, rt, imm16);
}
+void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
+ CHECK(!IsR6());
+ EmitI(0x22, rs, rt, imm16);
+}
+
+void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
+ CHECK(!IsR6());
+ EmitI(0x26, rs, rt, imm16);
+}
+
void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
EmitI(0x24, rs, rt, imm16);
}
@@ -465,6 +475,16 @@ void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
EmitI(0x2b, rs, rt, imm16);
}
+void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
+ CHECK(!IsR6());
+ EmitI(0x2a, rs, rt, imm16);
+}
+
+void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
+ CHECK(!IsR6());
+ EmitI(0x2e, rs, rt, imm16);
+}
+
void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
EmitR(0, rs, rt, rd, 0, 0x2a);
}
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 01c6490f88..2262af49b3 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -162,6 +162,8 @@ class MipsAssembler FINAL : public Assembler {
void Lb(Register rt, Register rs, uint16_t imm16);
void Lh(Register rt, Register rs, uint16_t imm16);
void Lw(Register rt, Register rs, uint16_t imm16);
+ void Lwl(Register rt, Register rs, uint16_t imm16);
+ void Lwr(Register rt, Register rs, uint16_t imm16);
void Lbu(Register rt, Register rs, uint16_t imm16);
void Lhu(Register rt, Register rs, uint16_t imm16);
void Lui(Register rt, uint16_t imm16);
@@ -172,6 +174,8 @@ class MipsAssembler FINAL : public Assembler {
void Sb(Register rt, Register rs, uint16_t imm16);
void Sh(Register rt, Register rs, uint16_t imm16);
void Sw(Register rt, Register rs, uint16_t imm16);
+ void Swl(Register rt, Register rs, uint16_t imm16);
+ void Swr(Register rt, Register rs, uint16_t imm16);
void Slt(Register rd, Register rs, Register rt);
void Sltu(Register rd, Register rs, Register rt);
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index 5fc3deebd3..9e27f07ff2 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -335,6 +335,18 @@ TEST_F(AssemblerMIPSTest, Nor) {
DriverStr(RepeatRRR(&mips::MipsAssembler::Nor, "nor ${reg1}, ${reg2}, ${reg3}"), "Nor");
}
+//////////
+// MISC //
+//////////
+
+TEST_F(AssemblerMIPSTest, Movz) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::Movz, "movz ${reg1}, ${reg2}, ${reg3}"), "Movz");
+}
+
+TEST_F(AssemblerMIPSTest, Movn) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::Movn, "movn ${reg1}, ${reg2}, ${reg3}"), "Movn");
+}
+
TEST_F(AssemblerMIPSTest, Seb) {
DriverStr(RepeatRR(&mips::MipsAssembler::Seb, "seb ${reg1}, ${reg2}"), "Seb");
}
@@ -363,6 +375,10 @@ TEST_F(AssemblerMIPSTest, Srlv) {
DriverStr(RepeatRRR(&mips::MipsAssembler::Srlv, "srlv ${reg1}, ${reg2}, ${reg3}"), "Srlv");
}
+TEST_F(AssemblerMIPSTest, Rotrv) {
+ DriverStr(RepeatRRR(&mips::MipsAssembler::Rotrv, "rotrv ${reg1}, ${reg2}, ${reg3}"), "rotrv");
+}
+
TEST_F(AssemblerMIPSTest, Srav) {
DriverStr(RepeatRRR(&mips::MipsAssembler::Srav, "srav ${reg1}, ${reg2}, ${reg3}"), "Srav");
}
@@ -405,6 +421,14 @@ TEST_F(AssemblerMIPSTest, Ext) {
DriverStr(expected, "Ext");
}
+TEST_F(AssemblerMIPSTest, ClzR2) {
+ DriverStr(RepeatRR(&mips::MipsAssembler::ClzR2, "clz ${reg1}, ${reg2}"), "clzR2");
+}
+
+TEST_F(AssemblerMIPSTest, CloR2) {
+ DriverStr(RepeatRR(&mips::MipsAssembler::CloR2, "clo ${reg1}, ${reg2}"), "cloR2");
+}
+
TEST_F(AssemblerMIPSTest, Lb) {
DriverStr(RepeatRRIb(&mips::MipsAssembler::Lb, -16, "lb ${reg1}, {imm}(${reg2})"), "Lb");
}
@@ -413,10 +437,18 @@ TEST_F(AssemblerMIPSTest, Lh) {
DriverStr(RepeatRRIb(&mips::MipsAssembler::Lh, -16, "lh ${reg1}, {imm}(${reg2})"), "Lh");
}
+TEST_F(AssemblerMIPSTest, Lwl) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::Lwl, -16, "lwl ${reg1}, {imm}(${reg2})"), "Lwl");
+}
+
TEST_F(AssemblerMIPSTest, Lw) {
DriverStr(RepeatRRIb(&mips::MipsAssembler::Lw, -16, "lw ${reg1}, {imm}(${reg2})"), "Lw");
}
+TEST_F(AssemblerMIPSTest, Lwr) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::Lwr, -16, "lwr ${reg1}, {imm}(${reg2})"), "Lwr");
+}
+
TEST_F(AssemblerMIPSTest, Lbu) {
DriverStr(RepeatRRIb(&mips::MipsAssembler::Lbu, -16, "lbu ${reg1}, {imm}(${reg2})"), "Lbu");
}
@@ -445,10 +477,18 @@ TEST_F(AssemblerMIPSTest, Sh) {
DriverStr(RepeatRRIb(&mips::MipsAssembler::Sh, -16, "sh ${reg1}, {imm}(${reg2})"), "Sh");
}
+TEST_F(AssemblerMIPSTest, Swl) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::Swl, -16, "swl ${reg1}, {imm}(${reg2})"), "Swl");
+}
+
TEST_F(AssemblerMIPSTest, Sw) {
DriverStr(RepeatRRIb(&mips::MipsAssembler::Sw, -16, "sw ${reg1}, {imm}(${reg2})"), "Sw");
}
+TEST_F(AssemblerMIPSTest, Swr) {
+ DriverStr(RepeatRRIb(&mips::MipsAssembler::Swr, -16, "swr ${reg1}, {imm}(${reg2})"), "Swr");
+}
+
TEST_F(AssemblerMIPSTest, Slt) {
DriverStr(RepeatRRR(&mips::MipsAssembler::Slt, "slt ${reg1}, ${reg2}, ${reg3}"), "Slt");
}