diff options
Diffstat (limited to 'compiler')
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"); } |