diff options
241 files changed, 5973 insertions, 2125 deletions
diff --git a/Android.mk b/Android.mk index 2e05d33209..e762814385 100644 --- a/Android.mk +++ b/Android.mk @@ -86,6 +86,7 @@ include $(art_path)/disassembler/Android.mk include $(art_path)/oatdump/Android.mk include $(art_path)/imgdiag/Android.mk include $(art_path)/patchoat/Android.mk +include $(art_path)/profman/Android.mk include $(art_path)/dalvikvm/Android.mk include $(art_path)/tools/Android.mk include $(art_path)/tools/ahat/Android.mk diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index dc5385309a..02bce411b4 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -118,8 +118,7 @@ endif ART_TARGET_CLANG_arm := false ART_TARGET_CLANG_arm64 := ART_TARGET_CLANG_mips := -# b/25928358, illegal instruction on mips64r6 with -O0 -ART_TARGET_CLANG_mips64 := false +ART_TARGET_CLANG_mips64 := ART_TARGET_CLANG_x86 := ART_TARGET_CLANG_x86_64 := diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index ab7036717a..c9af1c67a4 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -205,7 +205,7 @@ define build-art-test-dex LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT) ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),) LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex - LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true + LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp endif include $(BUILD_JAVA_LIBRARY) $(5) := $$(LOCAL_INSTALLED_MODULE) @@ -221,7 +221,7 @@ define build-art-test-dex LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION) ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),) LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex - LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true + LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp endif include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) $(6) := $$(LOCAL_INSTALLED_MODULE) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 704d69a37a..22c0e8d7d0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -144,6 +144,12 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_default_no-pic_32) \ oatdump +# Profile assistant tests requires profman utility. +ART_GTEST_profile_assistant_test_HOST_DEPS := \ + $(HOST_OUT_EXECUTABLES)/profmand +ART_GTEST_profile_assistant_test_TARGET_DEPS := \ + profman + # The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art @@ -153,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ dexlist/dexlist_test.cc \ imgdiag/imgdiag_test.cc \ oatdump/oatdump_test.cc \ + profman/profile_assistant_test.cc \ runtime/arch/arch_test.cc \ runtime/arch/instruction_set_test.cc \ runtime/arch/instruction_set_features_test.cc \ @@ -270,7 +277,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/optimizing/ssa_test.cc \ compiler/optimizing/stack_map_test.cc \ compiler/optimizing/suspend_check_test.cc \ - compiler/profile_assistant_test.cc \ compiler/utils/arena_allocator_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/swap_space_test.cc \ diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index dc2c9c954a..81b854e9c3 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -291,6 +291,13 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) { } { + const char* log_args = "-verbose:collector"; + LogVerbosity log_verbosity = LogVerbosity(); + log_verbosity.collector = true; + EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose); + } + + { const char* log_args = "-verbose:oat"; LogVerbosity log_verbosity = LogVerbosity(); log_verbosity.oat = true; diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 740199d541..c0a00cce70 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -584,6 +584,8 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> { for (size_t j = 0; j < verbose_options.size(); ++j) { if (verbose_options[j] == "class") { log_verbosity.class_linker = true; + } else if (verbose_options[j] == "collector") { + log_verbosity.collector = true; } else if (verbose_options[j] == "compiler") { log_verbosity.compiler = true; } else if (verbose_options[j] == "deopt") { 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.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/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"); } diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk index 77f8d6cd9f..dfc379fdae 100644 --- a/dex2oat/Android.mk +++ b/dex2oat/Android.mk @@ -55,20 +55,42 @@ ifeq ($(ART_BUILD_TARGET_DEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain,art/compiler,target,debug,$(dex2oat_target_arch))) endif +# Note: the order is important because of static linking resolution. +DEX2OAT_STATIC_DEPENDENCIES := \ + libziparchive-host \ + libnativehelper \ + libnativebridge \ + libnativeloader \ + libsigchain_dummy \ + libvixl \ + liblog \ + libz \ + libbacktrace \ + libLLVMObject \ + libLLVMBitReader \ + libLLVMMC \ + libLLVMMCParser \ + libLLVMCore \ + libLLVMSupport \ + libcutils \ + libunwindbacktrace \ + libutils \ + libbase \ + liblz4 \ + liblzma + # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target. ifeq ($(ART_BUILD_HOST_NDEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch))) ifeq ($(ART_BUILD_HOST_STATIC),true) - $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixl liblog libz \ - libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch),static)) + $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static)) endif endif ifeq ($(ART_BUILD_HOST_DEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host liblz4,art/compiler,host,debug,$(dex2oat_host_arch))) ifeq ($(ART_BUILD_HOST_STATIC),true) - $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixld liblog libz \ - libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,debug,$(dex2oat_host_arch),static)) + $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static)) endif endif diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 541fb5a423..f7efdc5bac 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -40,6 +40,7 @@ #include "art_method-inl.h" #include "base/dumpable.h" #include "base/macros.h" +#include "base/scoped_flock.h" #include "base/stl_util.h" #include "base/stringpiece.h" #include "base/time_utils.h" @@ -71,7 +72,6 @@ #include "mirror/object_array-inl.h" #include "oat_writer.h" #include "os.h" -#include "profile_assistant.h" #include "runtime.h" #include "runtime_options.h" #include "ScopedLocalRef.h" @@ -339,23 +339,10 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" Example: --runtime-arg -Xms256m"); UsageError(""); UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); - UsageError(" Can be specified multiple time, in which case the data from the different"); - UsageError(" profiles will be aggregated."); - UsageError(""); - UsageError(" --reference-profile-file=<filename>: specify a reference profile file to use when"); - UsageError(" compiling. The data in this file will be compared with the data in the"); - UsageError(" associated --profile-file and the compilation will proceed only if there is"); - UsageError(" a significant difference (--reference-profile-file is paired with"); - UsageError(" --profile-file in the natural order). If the compilation was attempted then"); - UsageError(" --profile-file will be merged into --reference-profile-file. Valid only when"); - UsageError(" specified together with --profile-file."); UsageError(""); UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); UsageError(" Cannot be used together with --profile-file."); UsageError(""); - UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); - UsageError(" accepts a file descriptor. Cannot be used together with"); - UsageError(" --reference-profile-file."); UsageError(" --print-pass-names: print a list of pass names"); UsageError(""); UsageError(" --disable-passes=<pass-names>: disable one or more passes separated by comma."); @@ -517,14 +504,6 @@ static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) { return dex_files_size >= kMinDexFileCumulativeSizeForSwap; } -static void CloseAllFds(const std::vector<uint32_t>& fds, const char* descriptor) { - for (size_t i = 0; i < fds.size(); i++) { - if (close(fds[i]) < 0) { - PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; - } - } -} - class Dex2Oat FINAL { public: explicit Dex2Oat(TimingLogger* timings) : @@ -567,10 +546,12 @@ class Dex2Oat FINAL { dump_passes_(false), dump_timing_(false), dump_slow_timing_(kIsDebugBuild), - swap_fd_(-1), + swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), + profile_file_fd_(kInvalidFd), timings_(timings), - force_determinism_(false) {} + force_determinism_(false) + {} ~Dex2Oat() { // Log completion time before deleting the runtime_, because this accesses @@ -824,25 +805,8 @@ class Dex2Oat FINAL { } } - if (!profile_files_.empty() && !profile_files_fd_.empty()) { - Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); - } - if (!profile_files_.empty()) { - if (!reference_profile_files_.empty() && - (reference_profile_files_.size() != profile_files_.size())) { - Usage("If specified, --reference-profile-file should match the number of --profile-file."); - } - } else if (!reference_profile_files_.empty()) { - Usage("--reference-profile-file should only be supplied with --profile-file"); - } - if (!profile_files_fd_.empty()) { - if (!reference_profile_files_fd_.empty() && - (reference_profile_files_fd_.size() != profile_files_fd_.size())) { - Usage("If specified, --reference-profile-file-fd should match the number", - " of --profile-file-fd."); - } - } else if (!reference_profile_files_fd_.empty()) { - Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) { + Usage("Profile file should not be specified with both --profile-file-fd and --profile-file"); } if (!parser_options->oat_symbols.empty()) { @@ -1153,16 +1117,9 @@ class Dex2Oat FINAL { } else if (option.starts_with("--compiler-backend=")) { ParseCompilerBackend(option, parser_options.get()); } else if (option.starts_with("--profile-file=")) { - profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); - } else if (option.starts_with("--reference-profile-file=")) { - reference_profile_files_.push_back( - option.substr(strlen("--reference-profile-file=")).ToString()); + profile_file_ = option.substr(strlen("--profile-file=")).ToString(); } else if (option.starts_with("--profile-file-fd=")) { - ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); - } else if (option.starts_with("--reference-profile-file-fd=")) { - ParseFdForCollection(option, "--reference_profile-file-fd", &reference_profile_files_fd_); - } else if (option == "--no-profile-file") { - // No profile + ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage); } else if (option == "--host") { is_host_ = true; } else if (option == "--runtime-arg") { @@ -1865,33 +1822,44 @@ class Dex2Oat FINAL { } bool UseProfileGuidedCompilation() const { - return !profile_files_.empty() || !profile_files_fd_.empty(); + return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd); } - bool ProcessProfiles() { + bool LoadProfile() { DCHECK(UseProfileGuidedCompilation()); - ProfileCompilationInfo* info = nullptr; - bool result = false; - if (profile_files_.empty()) { - DCHECK(!profile_files_fd_.empty()); - result = ProfileAssistant::ProcessProfiles( - profile_files_fd_, reference_profile_files_fd_, &info); - CloseAllFds(profile_files_fd_, "profile_files_fd_"); - CloseAllFds(reference_profile_files_fd_, "reference_profile_files_fd_"); + + profile_compilation_info_.reset(new ProfileCompilationInfo()); + ScopedFlock flock; + bool success = false; + std::string error; + if (profile_file_fd_ != -1) { + // The file doesn't need to be flushed so don't check the usage. + // Pass a bogus path so that we can easily attribute any reported error. + File file(profile_file_fd_, "profile", /*check_usage*/ false, /*read_only_mode*/ true); + if (flock.Init(&file, &error)) { + success = profile_compilation_info_->Load(profile_file_fd_); + } } else { - result = ProfileAssistant::ProcessProfiles( - profile_files_, reference_profile_files_, &info); + if (flock.Init(profile_file_.c_str(), O_RDONLY, /* block */ true, &error)) { + success = profile_compilation_info_->Load(flock.GetFile()->Fd()); + } + } + if (!error.empty()) { + LOG(WARNING) << "Cannot lock profiles: " << error; } - profile_compilation_info_.reset(info); + if (!success) { + profile_compilation_info_.reset(nullptr); + } - return result; + return success; } bool ShouldCompileBasedOnProfiles() const { DCHECK(UseProfileGuidedCompilation()); - // If we are given profiles, compile only if we have new information. - return profile_compilation_info_ != nullptr; + // If we are given a profile, compile only if we have some data in it. + return (profile_compilation_info_ != nullptr) && + (profile_compilation_info_->GetNumberOfMethods() != 0); } private: @@ -2460,10 +2428,8 @@ class Dex2Oat FINAL { int swap_fd_; std::string app_image_file_name_; int app_image_fd_; - std::vector<std::string> profile_files_; - std::vector<std::string> reference_profile_files_; - std::vector<uint32_t> profile_files_fd_; - std::vector<uint32_t> reference_profile_files_fd_; + std::string profile_file_; + int profile_file_fd_; std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_; TimingLogger* timings_; std::unique_ptr<CumulativeLogger> compiler_phases_timings_; @@ -2592,7 +2558,7 @@ static int dex2oat(int argc, char** argv) { // Process profile information and assess if we need to do a profile guided compilation. // This operation involves I/O. if (dex2oat.UseProfileGuidedCompilation()) { - if (dex2oat.ProcessProfiles()) { + if (dex2oat.LoadProfile()) { if (!dex2oat.ShouldCompileBasedOnProfiles()) { LOG(INFO) << "Skipped compilation because of insignificant profile delta"; return EXIT_SUCCESS; diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index f9226878a9..428266fac6 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -306,13 +306,17 @@ static const MipsInstruction gMipsInstructions[] = { { kITypeMask, 32u << kOpcodeShift, "lb", "TO", }, { kITypeMask, 33u << kOpcodeShift, "lh", "TO", }, + { kITypeMask, 34u << kOpcodeShift, "lwl", "TO", }, { kITypeMask, 35u << kOpcodeShift, "lw", "TO", }, { kITypeMask, 36u << kOpcodeShift, "lbu", "TO", }, { kITypeMask, 37u << kOpcodeShift, "lhu", "TO", }, + { kITypeMask, 38u << kOpcodeShift, "lwr", "TO", }, { kITypeMask, 39u << kOpcodeShift, "lwu", "TO", }, { kITypeMask, 40u << kOpcodeShift, "sb", "TO", }, { kITypeMask, 41u << kOpcodeShift, "sh", "TO", }, + { kITypeMask, 42u << kOpcodeShift, "swl", "TO", }, { kITypeMask, 43u << kOpcodeShift, "sw", "TO", }, + { kITypeMask, 46u << kOpcodeShift, "swr", "TO", }, { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", }, { kJTypeMask, 50u << kOpcodeShift, "bc", "P" }, { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", }, diff --git a/profman/Android.mk b/profman/Android.mk new file mode 100644 index 0000000000..d38d107d21 --- /dev/null +++ b/profman/Android.mk @@ -0,0 +1,45 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) + +include art/build/Android.executable.mk + +PROFMAN_SRC_FILES := \ + profman.cc \ + profile_assistant.cc + +# TODO: Remove this when the framework (installd) supports pushing the +# right instruction-set parameter for the primary architecture. +ifneq ($(filter ro.zygote=zygote64,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),) + profman_arch := 64 +else + profman_arch := 32 +endif + +ifeq ($(ART_BUILD_TARGET_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,ndebug,$(profman_arch))) +endif +ifeq ($(ART_BUILD_TARGET_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,debug,$(profman_arch))) +endif + +ifeq ($(ART_BUILD_HOST_NDEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,ndebug)) +endif +ifeq ($(ART_BUILD_HOST_DEBUG),true) + $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,debug)) +endif diff --git a/compiler/profile_assistant.cc b/profman/profile_assistant.cc index 85335efcc4..58e8a3a7d1 100644 --- a/compiler/profile_assistant.cc +++ b/profman/profile_assistant.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -24,13 +24,10 @@ namespace art { // Minimum number of new methods that profiles must contain to enable recompilation. static constexpr const uint32_t kMinNewMethodsForCompilation = 10; -bool ProfileAssistant::ProcessProfilesInternal( +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal( const std::vector<ScopedFlock>& profile_files, - const std::vector<ScopedFlock>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { + const ScopedFlock& reference_profile_file) { 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; @@ -38,51 +35,53 @@ bool ProfileAssistant::ProcessProfilesInternal( 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; + return kErrorBadProfiles; } // 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; + return kSkipCompilation; } - std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo()); // Merge information. + ProfileCompilationInfo info; + if (!info.Load(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not load reference profile file"; + return kErrorBadProfiles; + } + 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])) { + if (!info.Load(new_info[i])) { LOG(WARNING) << "Could not merge profile data at index " << i; - return false; + return kErrorBadProfiles; } } - // 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; - } - } + // We were successful in merging all profile information. Update the reference profile. + if (!reference_profile_file.GetFile()->ClearContent()) { + PLOG(WARNING) << "Could not clear reference profile file"; + return kErrorIO; } + if (!info.Save(reference_profile_file.GetFile()->Fd())) { + LOG(WARNING) << "Could not save reference profile file"; + return kErrorIO; + } + + return kCompile; +} - *profile_compilation_info = result.release(); - return true; +static bool InitFlock(const std::string& filename, ScopedFlock& flock, std::string* error) { + return flock.Init(filename.c_str(), O_RDWR, /* block */ true, error); +} + +static bool InitFlock(int fd, ScopedFlock& flock, std::string* error) { + DCHECK_GE(fd, 0); + // We do not own the descriptor, so disable auto-close and don't check usage. + File file(fd, false); + file.DisableAutoClose(); + return flock.Init(&file, error); } class ScopedCollectionFlock { @@ -92,7 +91,7 @@ class ScopedCollectionFlock { // 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)) { + if (!InitFlock(filenames[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -101,12 +100,10 @@ class ScopedCollectionFlock { } // Will block until all the locks are acquired. - bool Init(const std::vector<uint32_t>& fds, /* out */ std::string* error) { + bool Init(const std::vector<int>& 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)) { + DCHECK_GE(fds[i], 0); + if (!InitFlock(fds[i], flocks_[i], error)) { *error += " (index=" + std::to_string(i) + ")"; return false; } @@ -120,50 +117,43 @@ class ScopedCollectionFlock { 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; - +ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles( + const std::vector<int>& profile_files_fd, + int reference_profile_file_fd) { + DCHECK_GE(reference_profile_file_fd, 0); 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; + return kErrorCannotLock; } - 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; + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file_fd, reference_profile_file_flock, &error)) { + LOG(WARNING) << "Could not lock reference profiled files: " << error; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } -bool ProfileAssistant::ProcessProfiles( +ProfileAssistant::ProcessingResult 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; - + const std::string& reference_profile_file) { 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; + return kErrorCannotLock; } - ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size()); - if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) { + ScopedFlock reference_profile_file_flock; + if (!InitFlock(reference_profile_file, reference_profile_file_flock, &error)) { LOG(WARNING) << "Could not lock reference profile files: " << error; - return false; + return kErrorCannotLock; } return ProcessProfilesInternal(profile_files_flocks.Get(), - reference_profile_files_flocks.Get(), - profile_compilation_info); + reference_profile_file_flock); } } // namespace art diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h new file mode 100644 index 0000000000..d3c75b817a --- /dev/null +++ b/profman/profile_assistant.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef ART_PROFMAN_PROFILE_ASSISTANT_H_ +#define ART_PROFMAN_PROFILE_ASSISTANT_H_ + +#include <string> +#include <vector> + +#include "base/scoped_flock.h" +#include "jit/offline_profiling_info.h" + +namespace art { + +class ProfileAssistant { + public: + // These also serve as return codes of profman and are processed by installd + // (frameworks/native/cmds/installd/commands.cpp) + enum ProcessingResult { + kCompile = 0, + kSkipCompilation = 1, + kErrorBadProfiles = 2, + kErrorIO = 3, + kErrorCannotLock = 4 + }; + + // Process the profile information present in the given files. Returns one of + // ProcessingResult values depending on profile information and whether or not + // the analysis ended up successfully (i.e. no errors during reading, + // merging or writing of profile files). + // + // When the returned value is kCompile there is a significant difference + // between profile_files and reference_profile_files. In this case + // reference_profile will be updated with the profiling info obtain after + // merging all profiles. + // + // When the returned value is kSkipCompilation, the difference between the + // merge of the current profiles and the reference one is insignificant. In + // this case no file will be updated. + // + static ProcessingResult ProcessProfiles( + const std::vector<std::string>& profile_files, + const std::string& reference_profile_file); + + static ProcessingResult ProcessProfiles( + const std::vector<int>& profile_files_fd_, + int reference_profile_file_fd); + + private: + static ProcessingResult ProcessProfilesInternal( + const std::vector<ScopedFlock>& profile_files, + const ScopedFlock& reference_profile_file); + + DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); +}; + +} // namespace art + +#endif // ART_PROFMAN_PROFILE_ASSISTANT_H_ diff --git a/compiler/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 58b7513377..3faa8eb53f 100644 --- a/compiler/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -18,8 +18,9 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" -#include "compiler/profile_assistant.h" +#include "profile_assistant.h" #include "jit/offline_profiling_info.h" +#include "utils.h" namespace art { @@ -44,23 +45,47 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_TRUE(profile.GetFile()->ResetOffset()); } - uint32_t GetFd(const ScratchFile& file) const { - return static_cast<uint32_t>(file.GetFd()); + int GetFd(const ScratchFile& file) const { + return static_cast<int>(file.GetFd()); + } + + void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) { + ProfileCompilationInfo file_info; + ASSERT_TRUE(file.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info.Load(GetFd(file))); + ASSERT_TRUE(file_info.Equals(info)); + } + + // Runs test with given arguments. + int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { + std::string file_path = GetTestAndroidRoot(); + file_path += "/bin/profman"; + if (kIsDebugBuild) { + file_path += "d"; + } + + EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + std::vector<std::string> argv_str; + argv_str.push_back(file_path); + for (size_t k = 0; k < profiles_fd.size(); k++) { + argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k])); + } + argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd)); + + std::string error; + return ExecAndReturnCode(argv_str, &error); } }; TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; ProfileCompilationInfo info1; @@ -69,44 +94,32 @@ TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { 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); - + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs. + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + ProfileCompilationInfo expected; ASSERT_TRUE(expected.Load(info1)); ASSERT_TRUE(expected.Load(info2)); - ASSERT_TRUE(expected.Equals(*result)); + 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()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); // The new profile info will contain the methods with indices 0-100. const uint16_t kNumberOfMethodsToEnableCompilation = 100; @@ -118,60 +131,39 @@ TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { // 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); + ProfileCompilationInfo reference_info; + SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile, + &reference_info, kNumberOfMethodsToEnableCompilation / 2); // We should advise compilation. - ProfileCompilationInfo* result; - ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); - ASSERT_TRUE(result != nullptr); + ASSERT_EQ(ProfileAssistant::kCompile, + ProcessProfiles(profile_fds, reference_profile_fd)); // The resulting compilation info must be equal to the merge of the inputs + ProfileCompilationInfo result; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(result.Load(reference_profile_fd)); + 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)); + ASSERT_TRUE(expected.Load(reference_info)); + ASSERT_TRUE(expected.Equals(result)); - 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()); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToSkipCompilation = 1; ProfileCompilationInfo info1; @@ -180,9 +172,8 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { 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); + ASSERT_EQ(ProfileAssistant::kSkipCompilation, + ProcessProfiles(profile_fds, reference_profile_fd)); // The information from profiles must remain the same. ProfileCompilationInfo file_info1; @@ -196,22 +187,22 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { 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()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); + + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { ScratchFile profile1; ScratchFile profile2; - ScratchFile reference_profile1; - ScratchFile reference_profile2; + ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1), GetFd(profile2)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile1), - GetFd(reference_profile2)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -221,34 +212,24 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { 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); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - // 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)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); + CheckProfileInfo(profile2, info2); // Reference profile files must still remain empty. - ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); - ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); } TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { ScratchFile profile1; ScratchFile reference_profile; - std::vector<uint32_t> profile_fds({ + std::vector<int> profile_fds({ GetFd(profile1)}); - std::vector<uint32_t> reference_profile_fds({ - GetFd(reference_profile)}); + int reference_profile_fd = GetFd(reference_profile); const uint16_t kNumberOfMethodsToEnableCompilation = 100; // Assign different hashes for the same dex file. This will make merging of information to fail. @@ -258,22 +239,13 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { 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)); + ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, + ProcessProfiles(profile_fds, reference_profile_fd)); - ProfileCompilationInfo file_info2; - ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); - ASSERT_TRUE(file_info2.Load(GetFd(reference_profile))); - ASSERT_TRUE(file_info2.Equals(reference_info)); + // The information from profiles must remain the same. + CheckProfileInfo(profile1, info1); } } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc new file mode 100644 index 0000000000..7c9e449ed5 --- /dev/null +++ b/profman/profman.cc @@ -0,0 +1,208 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "base/dumpable.h" +#include "base/scoped_flock.h" +#include "base/stringpiece.h" +#include "base/stringprintf.h" +#include "base/time_utils.h" +#include "base/unix_file/fd_file.h" +#include "jit/offline_profiling_info.h" +#include "utils.h" +#include "profile_assistant.h" + +namespace art { + +static int original_argc; +static char** original_argv; + +static std::string CommandLine() { + std::vector<std::string> command; + for (int i = 0; i < original_argc; ++i) { + command.push_back(original_argv[i]); + } + return Join(command, ' '); +} + +static void UsageErrorV(const char* fmt, va_list ap) { + std::string error; + StringAppendV(&error, fmt, ap); + LOG(ERROR) << error; +} + +static void UsageError(const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); +} + +NO_RETURN static void Usage(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + UsageErrorV(fmt, ap); + va_end(ap); + + UsageError("Command: %s", CommandLine().c_str()); + UsageError("Usage: profman [options]..."); + UsageError(""); + UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); + UsageError(" Can be specified multiple time, in which case the data from the different"); + UsageError(" profiles will be aggregated."); + UsageError(""); + UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); + UsageError(" Cannot be used together with --profile-file."); + UsageError(""); + UsageError(" --reference-profile-file=<filename>: specify a reference profile."); + UsageError(" The data in this file will be compared with the data obtained by merging"); + UsageError(" all the files specified with --profile-file or --profile-file-fd."); + UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into"); + UsageError(" --reference-profile-file. "); + UsageError(""); + UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); + UsageError(" accepts a file descriptor. Cannot be used together with"); + UsageError(" --reference-profile-file."); + UsageError(""); + + exit(EXIT_FAILURE); +} + +class ProfMan FINAL { + public: + ProfMan() : + reference_profile_file_fd_(-1), + start_ns_(NanoTime()) {} + + ~ProfMan() { + LogCompletionTime(); + } + + void ParseArgs(int argc, char **argv) { + original_argc = argc; + original_argv = argv; + + InitLogging(argv); + + // Skip over the command name. + argv++; + argc--; + + if (argc == 0) { + Usage("No arguments specified"); + } + + for (int i = 0; i < argc; ++i) { + const StringPiece option(argv[i]); + const bool log_options = false; + if (log_options) { + LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i]; + } + if (option.starts_with("--profile-file=")) { + profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString()); + } else if (option.starts_with("--profile-file-fd=")) { + ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); + } else if (option.starts_with("--reference-profile-file=")) { + reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString(); + } else if (option.starts_with("--reference-profile-file-fd=")) { + ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage); + } else { + Usage("Unknown argument %s", option.data()); + } + } + + if (profile_files_.empty() && profile_files_fd_.empty()) { + Usage("No profile files specified."); + } + if (!profile_files_.empty() && !profile_files_fd_.empty()) { + Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); + } + if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) { + Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); + } + if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) { + Usage("Reference profile file not specified"); + } + } + + ProfileAssistant::ProcessingResult ProcessProfiles() { + ProfileAssistant::ProcessingResult result; + if (profile_files_.empty()) { + // The file doesn't need to be flushed here (ProcessProfiles will do it) + // so don't check the usage. + File file(reference_profile_file_fd_, false); + result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_); + CloseAllFds(profile_files_fd_, "profile_files_fd_"); + } else { + result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_); + } + return result; + } + + private: + static void ParseFdForCollection(const StringPiece& option, + const char* arg_name, + std::vector<int>* fds) { + int fd; + ParseUintOption(option, arg_name, &fd, Usage); + fds->push_back(fd); + } + + static void CloseAllFds(const std::vector<int>& fds, const char* descriptor) { + for (size_t i = 0; i < fds.size(); i++) { + if (close(fds[i]) < 0) { + PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; + } + } + } + + void LogCompletionTime() { + LOG(INFO) << "profman took " << PrettyDuration(NanoTime() - start_ns_); + } + + std::vector<std::string> profile_files_; + std::vector<int> profile_files_fd_; + std::string reference_profile_file_; + int reference_profile_file_fd_; + uint64_t start_ns_; +}; + +// See ProfileAssistant::ProcessingResult for return codes. +static int profman(int argc, char** argv) { + ProfMan profman; + + // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. + profman.ParseArgs(argc, argv); + + // Process profile information and assess if we need to do a profile guided compilation. + // This operation involves I/O. + return profman.ProcessProfiles(); +} + +} // namespace art + +int main(int argc, char **argv) { + return art::profman(argc, argv); +} + diff --git a/runtime/Android.mk b/runtime/Android.mk index e9f7add1af..947de8a79e 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -238,6 +238,7 @@ LIBART_HOST_LDFLAGS := # (empty) body is called. JIT_DEBUG_REGISTER_CODE_LDFLAGS := -Wl,--keep-unique,__jit_debug_register_code LIBART_TARGET_LDFLAGS_arm := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) +LIBART_TARGET_LDFLAGS_arm64 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) LIBART_TARGET_LDFLAGS_x86 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) LIBART_TARGET_LDFLAGS_x86_64 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS) JIT_DEBUG_REGISTER_CODE_LDFLAGS := diff --git a/runtime/art_method.h b/runtime/art_method.h index 078a978505..ec00a7b900 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -132,6 +132,14 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccFinal) != 0; } + bool IsCopied() { + const bool copied = (GetAccessFlags() & kAccCopied) != 0; + // (IsMiranda() || IsDefaultConflicting()) implies copied + DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied) + << "Miranda or default-conflict methods must always be copied."; + return copied; + } + bool IsMiranda() { return (GetAccessFlags() & kAccMiranda) != 0; } diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 771b2d0509..a4b38ea963 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -222,6 +222,10 @@ ArenaPool::ArenaPool(bool use_malloc, bool low_4gb) } ArenaPool::~ArenaPool() { + ReclaimMemory(); +} + +void ArenaPool::ReclaimMemory() { while (free_arenas_ != nullptr) { auto* arena = free_arenas_; free_arenas_ = free_arenas_->next_; @@ -229,6 +233,11 @@ ArenaPool::~ArenaPool() { } } +void ArenaPool::LockReclaimMemory() { + MutexLock lock(Thread::Current(), lock_); + ReclaimMemory(); +} + Arena* ArenaPool::AllocArena(size_t size) { Thread* self = Thread::Current(); Arena* ret = nullptr; diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 36334c4129..8a96571e99 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -276,6 +276,8 @@ class ArenaPool { Arena* AllocArena(size_t size) REQUIRES(!lock_); void FreeArenaChain(Arena* first) REQUIRES(!lock_); size_t GetBytesAllocated() const REQUIRES(!lock_); + void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS; + void LockReclaimMemory() REQUIRES(!lock_); // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works // use_malloc is false. void TrimMaps() REQUIRES(!lock_); diff --git a/runtime/base/logging.h b/runtime/base/logging.h index de46b0c118..8aaeaac0dc 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -37,6 +37,7 @@ enum LogSeverity { // and the "-verbose:" command line argument. struct LogVerbosity { bool class_linker; // Enabled with "-verbose:class". + bool collector; bool compiler; bool deopt; bool gc; diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 82a5f9611c..6972b3ef3f 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -1009,10 +1009,6 @@ void Locks::Init() { DCHECK(alloc_tracker_lock_ == nullptr); alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kInterpreterStringInitMapLock); - DCHECK(interpreter_string_init_map_lock_ == nullptr); - interpreter_string_init_map_lock_ = new Mutex("Interpreter String initializer reference map lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock); DCHECK(thread_list_lock_ == nullptr); thread_list_lock_ = new Mutex("thread list lock", current_lock_level); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index f674a6f3c8..e72f2a2e7b 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -102,7 +102,6 @@ enum LockLevel { kMonitorListLock, kJniLoadLibraryLock, kThreadListLock, - kInterpreterStringInitMapLock, kAllocTrackerLock, kDeoptimizationLock, kProfilerLock, diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h index 1236585afd..bd19d00544 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/runtime/base/scoped_arena_containers.h @@ -201,20 +201,29 @@ inline ScopedArenaAllocatorAdapter<void> ScopedArenaAllocator::Adapter(ArenaAllo template <typename T> class ArenaDelete { static constexpr uint8_t kMagicFill = 0xCE; - public: - void operator()(T* ptr) const { - ptr->~T(); + + protected: + // Used for variable sized objects such as RegisterLine. + ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const { if (RUNNING_ON_MEMORY_TOOL > 0) { - // Writing to the memory will fail if it we already destroyed the pointer with + // Writing to the memory will fail ift we already destroyed the pointer with // DestroyOnlyDelete since we make it no access. - memset(ptr, kMagicFill, sizeof(T)); - MEMORY_TOOL_MAKE_NOACCESS(ptr, sizeof(T)); + memset(ptr, kMagicFill, size); + MEMORY_TOOL_MAKE_NOACCESS(ptr, size); } else if (kIsDebugBuild) { CHECK(ArenaStack::ArenaTagForAllocation(reinterpret_cast<void*>(ptr)) == ArenaFreeTag::kUsed) << "Freeing invalid object " << ptr; ArenaStack::ArenaTagForAllocation(reinterpret_cast<void*>(ptr)) = ArenaFreeTag::kFree; // Write a magic value to try and catch use after free error. - memset(ptr, kMagicFill, sizeof(T)); + memset(ptr, kMagicFill, size); + } + } + + public: + void operator()(T* ptr) const { + if (ptr != nullptr) { + ptr->~T(); + ProtectMemory(ptr, sizeof(T)); } } }; diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index 814cbd093a..0e8031f4f2 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -83,7 +83,7 @@ bool ScopedFlock::Init(const char* filename, int flags, bool block, std::string* } bool ScopedFlock::Init(File* file, std::string* error_msg) { - file_.reset(new File(dup(file->Fd()), true)); + file_.reset(new File(dup(file->Fd()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode())); if (file_->Fd() == -1) { file_.reset(); *error_msg = StringPrintf("Failed to duplicate open file '%s': %s", @@ -114,7 +114,13 @@ ScopedFlock::~ScopedFlock() { if (file_.get() != nullptr) { int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); CHECK_EQ(0, flock_result); - if (file_->FlushCloseOrErase() != 0) { + int close_result = -1; + if (file_->ReadOnlyMode()) { + close_result = file_->Close(); + } else { + close_result = file_->FlushCloseOrErase(); + } + if (close_result != 0) { PLOG(WARNING) << "Could not close scoped file lock file."; } } diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index e17bebb4fb..4672948f31 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -35,18 +35,22 @@ namespace unix_file { -FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) { +FdFile::FdFile() + : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, bool check_usage) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), auto_close_(true) { + fd_(fd), auto_close_(true), read_only_mode_(false) { } FdFile::FdFile(int fd, const std::string& path, bool check_usage) + : FdFile(fd, path, check_usage, false) { +} + +FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode) : guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck), - fd_(fd), file_path_(path), auto_close_(true) { - CHECK_NE(0U, path.size()); + fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) { } FdFile::~FdFile() { @@ -99,6 +103,7 @@ bool FdFile::Open(const std::string& path, int flags) { bool FdFile::Open(const std::string& path, int flags, mode_t mode) { CHECK_EQ(fd_, -1) << path; + read_only_mode_ = (flags & O_RDONLY) != 0; fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); if (fd_ == -1) { return false; @@ -136,6 +141,7 @@ int FdFile::Close() { } int FdFile::Flush() { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(fdatasync(fd_)); #else @@ -155,6 +161,7 @@ int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const { } int FdFile::SetLength(int64_t new_length) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length)); #else @@ -171,6 +178,7 @@ int64_t FdFile::GetLength() const { } int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) { + DCHECK(!read_only_mode_); #ifdef __linux__ int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset)); #else @@ -184,6 +192,14 @@ int FdFile::Fd() const { return fd_; } +bool FdFile::ReadOnlyMode() const { + return read_only_mode_; +} + +bool FdFile::CheckUsage() const { + return guard_state_ != GuardState::kNoCheck; +} + bool FdFile::IsOpened() const { return fd_ >= 0; } @@ -219,6 +235,7 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) { } bool FdFile::WriteFully(const void* buffer, size_t byte_count) { + DCHECK(!read_only_mode_); const char* ptr = static_cast<const char*>(buffer); moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file."); while (byte_count > 0) { @@ -233,6 +250,7 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) { } bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { + DCHECK(!read_only_mode_); off_t off = static_cast<off_t>(offset); off_t sz = static_cast<off_t>(size); if (offset < 0 || static_cast<int64_t>(off) != offset || @@ -279,12 +297,14 @@ bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { } void FdFile::Erase() { + DCHECK(!read_only_mode_); TEMP_FAILURE_RETRY(SetLength(0)); TEMP_FAILURE_RETRY(Flush()); TEMP_FAILURE_RETRY(Close()); } int FdFile::FlushCloseOrErase() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "CloseOrErase failed while flushing a file."; @@ -301,6 +321,7 @@ int FdFile::FlushCloseOrErase() { } int FdFile::FlushClose() { + DCHECK(!read_only_mode_); int flush_result = TEMP_FAILURE_RETRY(Flush()); if (flush_result != 0) { LOG(::art::ERROR) << "FlushClose failed while flushing a file."; @@ -317,6 +338,7 @@ void FdFile::MarkUnchecked() { } bool FdFile::ClearContent() { + DCHECK(!read_only_mode_); if (SetLength(0) < 0) { PLOG(art::ERROR) << "Failed to reset the length"; return false; @@ -325,6 +347,7 @@ bool FdFile::ClearContent() { } bool FdFile::ResetOffset() { + DCHECK(!read_only_mode_); off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET)); if (rc == static_cast<off_t>(-1)) { PLOG(art::ERROR) << "Failed to reset the offset"; diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 1e2d8af151..8040afe9b7 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -37,6 +37,7 @@ class FdFile : public RandomAccessFile { // file descriptor. (Use DisableAutoClose to retain ownership.) FdFile(int fd, bool checkUsage); FdFile(int fd, const std::string& path, bool checkUsage); + FdFile(int fd, const std::string& path, bool checkUsage, bool read_only_mode); // Destroys an FdFile, closing the file descriptor if Close hasn't already // been called. (If you care about the return value of Close, call it @@ -68,6 +69,8 @@ class FdFile : public RandomAccessFile { // Bonus API. int Fd() const; + bool ReadOnlyMode() const; + bool CheckUsage() const; bool IsOpened() const; const std::string& GetPath() const { return file_path_; @@ -119,6 +122,7 @@ class FdFile : public RandomAccessFile { int fd_; std::string file_path_; bool auto_close_; + bool read_only_mode_; DISALLOW_COPY_AND_ASSIGN(FdFile); }; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5278d1bb05..9ea082769a 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -759,7 +759,7 @@ static void SanityCheckArtMethod(ArtMethod* m, SHARED_REQUIRES(Locks::mutator_lock_) { if (m->IsRuntimeMethod()) { CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m); - } else if (m->IsMiranda()) { + } else if (m->IsCopied()) { CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m); } else if (expected_class != nullptr) { CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m); @@ -1137,18 +1137,18 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*)); - const bool is_miranda = method->IsMiranda(); + const bool is_copied = method->IsCopied(); if (resolved_types != nullptr) { bool in_image_space = false; - if (kIsDebugBuild || is_miranda) { + if (kIsDebugBuild || is_copied) { in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin()); } // Must be in image space for non-miranda method. - DCHECK(is_miranda || in_image_space) + DCHECK(is_copied || in_image_space) << resolved_types << " is not in image starting at " << reinterpret_cast<void*>(header_.GetImageBegin()); - if (!is_miranda || in_image_space) { + if (!is_copied || in_image_space) { // Go through the array so that we don't need to do a slow map lookup. method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types), sizeof(void*)); @@ -1157,15 +1157,15 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor { ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*)); if (resolved_methods != nullptr) { bool in_image_space = false; - if (kIsDebugBuild || is_miranda) { + if (kIsDebugBuild || is_copied) { in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin()); } // Must be in image space for non-miranda method. - DCHECK(is_miranda || in_image_space) + DCHECK(is_copied || in_image_space) << resolved_methods << " is not in image starting at " << reinterpret_cast<void*>(header_.GetImageBegin()); - if (!is_miranda || in_image_space) { + if (!is_copied || in_image_space) { // Go through the array so that we don't need to do a slow map lookup. method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods), sizeof(void*)); @@ -6459,7 +6459,7 @@ bool ClassLinker::LinkInterfaceMethods( for (ArtMethod* mir_method : miranda_methods) { ArtMethod& new_method = *out; new_method.CopyFrom(mir_method, image_pointer_size_); - new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda); + new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda | kAccCopied); DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u) << "Miranda method should be abstract!"; move_table.emplace(mir_method, &new_method); @@ -6478,7 +6478,9 @@ bool ClassLinker::LinkInterfaceMethods( // yet it shouldn't have methods that are skipping access checks. // TODO This is rather arbitrary. We should maybe support classes where only some of its // methods are skip_access_checks. - new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccSkipAccessChecks); + constexpr uint32_t kSetFlags = kAccDefault | kAccCopied; + constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks; + new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); move_table.emplace(def_method, &new_method); ++out; } @@ -6489,7 +6491,7 @@ bool ClassLinker::LinkInterfaceMethods( // this as a default, non-abstract method, since thats what it is. Also clear the // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have // methods that are skipping access checks. - constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict; + constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied; constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks); new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); DCHECK(new_method.IsDefaultConflicting()); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 3a0f3e5065..488826b6c4 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -263,7 +263,7 @@ class ClassLinkerTest : public CommonRuntimeTest { for (ArtMethod& method : klass->GetCopiedMethods(sizeof(void*))) { AssertMethod(&method); EXPECT_FALSE(method.IsDirect()); - EXPECT_TRUE(method.IsMiranda() || method.IsDefault() || method.IsDefaultConflicting()); + EXPECT_TRUE(method.IsCopied()); EXPECT_TRUE(method.GetDeclaringClass()->IsInterface()) << "declaring class: " << PrettyClass(method.GetDeclaringClass()); EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get())) @@ -1225,12 +1225,12 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) { dex_cache->SetLocation(location.Get()); const DexFile* old_dex_file = dex_cache->GetDexFile(); - DexFile* dex_file = new DexFile(old_dex_file->Begin(), - old_dex_file->Size(), - location->ToModifiedUtf8(), - 0u, - nullptr, - nullptr); + std::unique_ptr<DexFile> dex_file(new DexFile(old_dex_file->Begin(), + old_dex_file->Size(), + location->ToModifiedUtf8(), + 0u, + nullptr, + nullptr)); { WriterMutexLock mu(soa.Self(), *class_linker->DexLock()); // Check that inserting with a UTF16 name works. diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 369e4083a2..4de5388d8c 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -34,11 +34,7 @@ int32_t AllocRecordStackTraceElement::ComputeLineNumber() const { const char* AllocRecord::GetClassDescriptor(std::string* storage) const { // klass_ could contain null only if we implement class unloading. - if (UNLIKELY(klass_.IsNull())) { - return "null"; - } else { - return klass_.Read()->GetDescriptor(storage); - } + return klass_.IsNull() ? "null" : klass_.Read()->GetDescriptor(storage); } void AllocRecordObjectMap::SetProperties() { @@ -105,8 +101,19 @@ void AllocRecordObjectMap::VisitRoots(RootVisitor* visitor) { size_t count = recent_record_max_; // Only visit the last recent_record_max_ number of allocation records in entries_ and mark the // klass_ fields as strong roots. - for (auto it = entries_.rbegin(), end = entries_.rend(); count > 0 && it != end; count--, ++it) { - buffered_visitor.VisitRootIfNonNull(it->second->GetClassGcRoot()); + for (auto it = entries_.rbegin(), end = entries_.rend(); it != end; ++it) { + AllocRecord* record = it->second; + if (count > 0) { + buffered_visitor.VisitRootIfNonNull(record->GetClassGcRoot()); + --count; + } + // Visit all of the stack frames to make sure no methods in the stack traces get unloaded by + // class unloading. + for (size_t i = 0, depth = record->GetDepth(); i < depth; ++i) { + const AllocRecordStackTraceElement& element = record->StackElement(i); + DCHECK(element.GetMethod() != nullptr); + element.GetMethod()->VisitRoots(buffered_visitor, sizeof(void*)); + } } } @@ -131,12 +138,7 @@ void AllocRecordObjectMap::SweepAllocationRecords(IsMarkedVisitor* visitor) { VLOG(heap) << "Start SweepAllocationRecords()"; size_t count_deleted = 0, count_moved = 0, count = 0; // Only the first (size - recent_record_max_) number of records can be deleted. - size_t delete_bound; - if (entries_.size() <= recent_record_max_) { - delete_bound = 0; - } else { - delete_bound = entries_.size() - recent_record_max_; - } + const size_t delete_bound = std::max(entries_.size(), recent_record_max_) - recent_record_max_; for (auto it = entries_.begin(), end = entries_.end(); it != end;) { ++count; // This does not need a read barrier because this is called by GC. @@ -187,7 +189,6 @@ struct AllocRecordStackVisitor : public StackVisitor { SHARED_REQUIRES(Locks::mutator_lock_) : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), trace(trace_in), - depth(0), max_depth(max) {} // TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses @@ -209,7 +210,7 @@ struct AllocRecordStackVisitor : public StackVisitor { } AllocRecordStackTrace* trace; - size_t depth; + size_t depth = 0u; const size_t max_depth; }; @@ -237,9 +238,12 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) { << records->max_stack_depth_ << " frames, taking up to " << PrettySize(sz * records->alloc_record_max_) << ")"; heap->SetAllocationRecords(records); - heap->SetAllocTrackingEnabled(true); } Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); + { + MutexLock mu(self, *Locks::alloc_tracker_lock_); + heap->SetAllocTrackingEnabled(true); + } } else { { MutexLock mu(self, *Locks::alloc_tracker_lock_); diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 8b125dd167..2c487fe8df 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -58,10 +58,16 @@ RosAlloc::RosAlloc(void* base, size_t capacity, size_t max_capacity, page_release_mode_(page_release_mode), page_release_size_threshold_(page_release_size_threshold), is_running_on_memory_tool_(running_on_memory_tool) { + DCHECK_ALIGNED(base, kPageSize); DCHECK_EQ(RoundUp(capacity, kPageSize), capacity); DCHECK_EQ(RoundUp(max_capacity, kPageSize), max_capacity); CHECK_LE(capacity, max_capacity); CHECK_ALIGNED(page_release_size_threshold_, kPageSize); + // Zero the memory explicitly (don't rely on that the mem map is zero-initialized). + if (!kMadviseZeroes) { + memset(base_, 0, max_capacity); + } + CHECK_EQ(madvise(base_, max_capacity, MADV_DONTNEED), 0); if (!initialized_) { Initialize(); } diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index a472a8bffb..b12cb5b0dd 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -192,6 +192,7 @@ class RosAlloc { Verify(); } DCHECK(slot != nullptr); + DCHECK(slot->Next() == nullptr); Slot** headp = reinterpret_cast<Slot**>(&head_); Slot** tailp = kUseTail ? reinterpret_cast<Slot**>(&tail_) : nullptr; Slot* old_head = *headp; diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 8e1b7f4d1a..d393f0b1d2 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -1622,7 +1622,9 @@ class ConcurrentCopyingRefFieldsVisitor { inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { DCHECK(!region_space_->IsInFromSpace(to_ref)); ConcurrentCopyingRefFieldsVisitor visitor(this); - to_ref->VisitReferences(visitor, visitor); + // Disable the read barrier for a performance reason. + to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>( + visitor, visitor); } // Process a field. diff --git a/runtime/gc/collector/immune_region.h b/runtime/gc/collector/immune_region.h index b60426daf0..c9ac435b2b 100644 --- a/runtime/gc/collector/immune_region.h +++ b/runtime/gc/collector/immune_region.h @@ -66,6 +66,10 @@ class ImmuneRegion { return end_; } + size_t Size() const { + return size_; + } + private: void UpdateSize() { size_ = reinterpret_cast<uintptr_t>(end_) - reinterpret_cast<uintptr_t>(begin_); diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 8f9a9e294f..26da4ca5a9 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -18,6 +18,7 @@ #include "gc/space/space-inl.h" #include "mirror/object.h" +#include "oat_file.h" namespace art { namespace gc { @@ -45,11 +46,16 @@ void ImmuneSpaces::CreateLargestImmuneRegion() { space::ImageSpace* image_space = space->AsImageSpace(); // Update the end to include the other non-heap sections. space_end = RoundUp(reinterpret_cast<uintptr_t>(image_space->GetImageEnd()), kPageSize); - uintptr_t oat_begin = reinterpret_cast<uintptr_t>(image_space->GetOatFileBegin()); - uintptr_t oat_end = reinterpret_cast<uintptr_t>(image_space->GetOatFileEnd()); - if (space_end == oat_begin) { - DCHECK_GE(oat_end, oat_begin); - space_end = oat_end; + // For the app image case, GetOatFileBegin is where the oat file was mapped during image + // creation, the actual oat file could be somewhere else. + const OatFile* const image_oat_file = image_space->GetOatFile(); + if (image_oat_file != nullptr) { + uintptr_t oat_begin = reinterpret_cast<uintptr_t>(image_oat_file->Begin()); + uintptr_t oat_end = reinterpret_cast<uintptr_t>(image_oat_file->End()); + if (space_end == oat_begin) { + DCHECK_GE(oat_end, oat_begin); + space_end = oat_end; + } } } if (cur_begin == 0u) { @@ -71,6 +77,8 @@ void ImmuneSpaces::CreateLargestImmuneRegion() { } largest_immune_region_.SetBegin(reinterpret_cast<mirror::Object*>(best_begin)); largest_immune_region_.SetEnd(reinterpret_cast<mirror::Object*>(best_end)); + VLOG(collector) << "Immune region " << largest_immune_region_.Begin() << "-" + << largest_immune_region_.End(); } void ImmuneSpaces::AddSpace(space::ContinuousSpace* space) { diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc index ea290dd07d..56838f5c06 100644 --- a/runtime/gc/collector/immune_spaces_test.cc +++ b/runtime/gc/collector/immune_spaces_test.cc @@ -72,17 +72,31 @@ TEST_F(ImmuneSpacesTest, AppendBasic) { EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), b.Limit()); } +class DummyOatFile : public OatFile { + public: + DummyOatFile(uint8_t* begin, uint8_t* end) : OatFile("Location", /*is_executable*/ false) { + begin_ = begin; + end_ = end; + } +}; + class DummyImageSpace : public space::ImageSpace { public: - DummyImageSpace(MemMap* map, accounting::ContinuousSpaceBitmap* live_bitmap) + DummyImageSpace(MemMap* map, + accounting::ContinuousSpaceBitmap* live_bitmap, + std::unique_ptr<DummyOatFile>&& oat_file) : ImageSpace("DummyImageSpace", /*image_location*/"", map, live_bitmap, - map->End()) {} + map->End()) { + oat_file_ = std::move(oat_file); + oat_file_non_owned_ = oat_file_.get(); + } - // OatSize is how large the oat file is after the image. - static DummyImageSpace* Create(size_t size, size_t oat_size) { + // Size is the size of the image space, oat offset is where the oat file is located + // after the end of image space. oat_size is the size of the oat file. + static DummyImageSpace* Create(size_t size, size_t oat_offset, size_t oat_size) { std::string error_str; std::unique_ptr<MemMap> map(MemMap::MapAnonymous("DummyImageSpace", nullptr, @@ -100,6 +114,9 @@ class DummyImageSpace : public space::ImageSpace { if (live_bitmap == nullptr) { return nullptr; } + // The actual mapped oat file may not be directly after the image for the app image case. + std::unique_ptr<DummyOatFile> oat_file(new DummyOatFile(map->End() + oat_offset, + map->End() + oat_offset + oat_size)); // Create image header. ImageSection sections[ImageHeader::kSectionCount]; new (map->Begin()) ImageHeader( @@ -108,6 +125,7 @@ class DummyImageSpace : public space::ImageSpace { sections, /*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1, /*oat_checksum*/0u, + // The oat file data in the header is always right after the image space. /*oat_file_begin*/PointerToLowMemUInt32(map->End()), /*oat_data_begin*/PointerToLowMemUInt32(map->End()), /*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size), @@ -121,7 +139,7 @@ class DummyImageSpace : public space::ImageSpace { /*is_pic*/false, ImageHeader::kStorageModeUncompressed, /*storage_size*/0u); - return new DummyImageSpace(map.release(), live_bitmap.release()); + return new DummyImageSpace(map.release(), live_bitmap.release(), std::move(oat_file)); } }; @@ -129,7 +147,9 @@ TEST_F(ImmuneSpacesTest, AppendAfterImage) { ImmuneSpaces spaces; constexpr size_t image_size = 123 * kPageSize; constexpr size_t image_oat_size = 321 * kPageSize; - std::unique_ptr<DummyImageSpace> image_space(DummyImageSpace::Create(image_size, image_oat_size)); + std::unique_ptr<DummyImageSpace> image_space(DummyImageSpace::Create(image_size, + 0, + image_oat_size)); ASSERT_TRUE(image_space != nullptr); const ImageHeader& image_header = image_space->GetImageHeader(); EXPECT_EQ(image_header.GetImageSize(), image_size); @@ -150,6 +170,18 @@ TEST_F(ImmuneSpacesTest, AppendAfterImage) { EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), image_space->Begin()); EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), space.Limit()); + // Check that appending with a gap between the map does not include the oat file. + image_space.reset(DummyImageSpace::Create(image_size, kPageSize, image_oat_size)); + spaces.Reset(); + { + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + spaces.AddSpace(image_space.get()); + } + EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), + image_space->Begin()); + // Size should be equal, we should not add the oat file since it is not adjacent to the image + // space. + EXPECT_EQ(spaces.GetLargestImmuneRegion().Size(), image_size); } } // namespace collector diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3c9312f256..a656fb8faf 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -845,6 +845,13 @@ void Heap::DecrementDisableMovingGC(Thread* self) { void Heap::IncrementDisableThreadFlip(Thread* self) { // Supposed to be called by mutators. If thread_flip_running_ is true, block. Otherwise, go ahead. CHECK(kUseReadBarrier); + bool is_nested = self->GetDisableThreadFlipCount() > 0; + self->IncrementDisableThreadFlipCount(); + if (is_nested) { + // If this is a nested JNI critical section enter, we don't need to wait or increment the global + // counter. The global counter is incremented only once for a thread for the outermost enter. + return; + } ScopedThreadStateChange tsc(self, kWaitingForGcThreadFlip); MutexLock mu(self, *thread_flip_lock_); bool has_waited = false; @@ -867,10 +874,20 @@ void Heap::DecrementDisableThreadFlip(Thread* self) { // Supposed to be called by mutators. Decrement disable_thread_flip_count_ and potentially wake up // the GC waiting before doing a thread flip. CHECK(kUseReadBarrier); + self->DecrementDisableThreadFlipCount(); + bool is_outermost = self->GetDisableThreadFlipCount() == 0; + if (!is_outermost) { + // If this is not an outermost JNI critical exit, we don't need to decrement the global counter. + // The global counter is decremented only once for a thread for the outermost exit. + return; + } MutexLock mu(self, *thread_flip_lock_); CHECK_GT(disable_thread_flip_count_, 0U); --disable_thread_flip_count_; - thread_flip_cond_->Broadcast(self); + if (disable_thread_flip_count_ == 0) { + // Potentially notify the GC thread blocking to begin a thread flip. + thread_flip_cond_->Broadcast(self); + } } void Heap::ThreadFlipBegin(Thread* self) { @@ -882,7 +899,8 @@ void Heap::ThreadFlipBegin(Thread* self) { bool has_waited = false; uint64_t wait_start = NanoTime(); CHECK(!thread_flip_running_); - // Set this to true before waiting so that a new mutator entering a JNI critical won't starve GC. + // Set this to true before waiting so that frequent JNI critical enter/exits won't starve + // GC. This like a writer preference of a reader-writer lock. thread_flip_running_ = true; while (disable_thread_flip_count_ > 0) { has_waited = true; @@ -904,6 +922,7 @@ void Heap::ThreadFlipEnd(Thread* self) { MutexLock mu(self, *thread_flip_lock_); CHECK(thread_flip_running_); thread_flip_running_ = false; + // Potentially notify mutator threads blocking to enter a JNI critical section. thread_flip_cond_->Broadcast(self); } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c02e2d3864..a181e23b53 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -1113,6 +1113,8 @@ class Heap { // Used to synchronize between JNI critical calls and the thread flip of the CC collector. Mutex* thread_flip_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; std::unique_ptr<ConditionVariable> thread_flip_cond_ GUARDED_BY(thread_flip_lock_); + // This counter keeps track of how many threads are currently in a JNI critical section. This is + // incremented once per thread even with nested enters. size_t disable_thread_flip_count_ GUARDED_BY(thread_flip_lock_); bool thread_flip_running_ GUARDED_BY(thread_flip_lock_); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 0c06c386b5..894ce9af72 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -676,13 +676,17 @@ class RelocationRange { dest_(dest), length_(length) {} - bool ContainsSource(uintptr_t address) const { + bool InSource(uintptr_t address) const { return address - source_ < length_; } + bool InDest(uintptr_t address) const { + return address - dest_ < length_; + } + // Translate a source address to the destination space. uintptr_t ToDest(uintptr_t address) const { - DCHECK(ContainsSource(address)); + DCHECK(InSource(address)); return address + Delta(); } @@ -724,24 +728,28 @@ class FixupVisitor : public ValueObject { template <typename T> ALWAYS_INLINE T* ForwardObject(T* src) const { const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src); - if (boot_image_.ContainsSource(uint_src)) { + if (boot_image_.InSource(uint_src)) { return reinterpret_cast<T*>(boot_image_.ToDest(uint_src)); } - if (app_image_.ContainsSource(uint_src)) { + if (app_image_.InSource(uint_src)) { return reinterpret_cast<T*>(app_image_.ToDest(uint_src)); } + // Since we are fixing up the app image, there should only be pointers to the app image and + // boot image. + DCHECK(src == nullptr) << reinterpret_cast<const void*>(src); return src; } // Return the relocated address of a code pointer (contained by an oat file). ALWAYS_INLINE const void* ForwardCode(const void* src) const { const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src); - if (boot_oat_.ContainsSource(uint_src)) { + if (boot_oat_.InSource(uint_src)) { return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src)); } - if (app_oat_.ContainsSource(uint_src)) { + if (app_oat_.InSource(uint_src)) { return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src)); } + DCHECK(src == nullptr) << src; return src; } @@ -766,6 +774,11 @@ class FixupObjectAdapter : public FixupVisitor { template<typename... Args> explicit FixupObjectAdapter(Args... args) : FixupVisitor(args...) {} + // Must be called on pointers that already have been relocated to the destination relocation. + ALWAYS_INLINE bool IsInAppImage(mirror::Object* object) const { + return app_image_.InDest(reinterpret_cast<uintptr_t>(object)); + } + template <typename T> T* operator()(T* obj) const { return ForwardObject(obj); @@ -816,7 +829,10 @@ class FixupRootVisitor : public FixupVisitor { class FixupObjectVisitor : public FixupVisitor { public: template<typename... Args> - explicit FixupObjectVisitor(Args... args) : FixupVisitor(args...) {} + explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* pointer_array_visited, + Args... args) + : FixupVisitor(args...), + pointer_array_visited_(pointer_array_visited) {} // Fix up separately since we also need to fix up method entrypoints. ALWAYS_INLINE void VisitRootIfNonNull( @@ -841,6 +857,19 @@ class FixupObjectVisitor : public FixupVisitor { } } + // Visit a pointer array and forward corresponding native data. Ignores pointer arrays in the + // boot image. Uses the bitmap to ensure the same array is not visited multiple times. + template <typename Visitor> + void VisitPointerArray(mirror::PointerArray* array, const Visitor& visitor) const + NO_THREAD_SAFETY_ANALYSIS { + if (array != nullptr && + visitor.IsInAppImage(array) && + !pointer_array_visited_->Test(array)) { + array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, sizeof(void*), visitor); + pointer_array_visited_->Set(array); + } + } + // java.lang.ref.Reference visitor. void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { @@ -859,11 +888,9 @@ class FixupObjectVisitor : public FixupVisitor { mirror::Class* klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>(); FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_); klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(klass, sizeof(void*), visitor); - // Deal with the arrays. - mirror::PointerArray* vtable = klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(); - if (vtable != nullptr) { - vtable->Fixup<kVerifyNone, kWithoutReadBarrier>(vtable, sizeof(void*), visitor); - } + // Deal with the pointer arrays. Use the helper function since multiple classes can reference + // the same arrays. + VisitPointerArray(klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(), visitor); mirror::IfTable* iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>(); if (iftable != nullptr) { for (int32_t i = 0, count = iftable->Count(); i < count; ++i) { @@ -871,12 +898,15 @@ class FixupObjectVisitor : public FixupVisitor { mirror::PointerArray* methods = iftable->GetMethodArray<kVerifyNone, kWithoutReadBarrier>(i); DCHECK(methods != nullptr); - methods->Fixup<kVerifyNone, kWithoutReadBarrier>(methods, sizeof(void*), visitor); + VisitPointerArray(methods, visitor); } } } } } + + private: + gc::accounting::ContinuousSpaceBitmap* const pointer_array_visited_; }; class ForwardObjectAdapter { @@ -1010,9 +1040,18 @@ static bool RelocateInPlace(ImageHeader& image_header, const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects); uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset()); uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End()); - // Two pass approach, fix up all classes first, then fix up non class-objects. - FixupObjectVisitor fixup_object_visitor(boot_image, boot_oat, app_image, app_oat); if (fixup_image) { + // Two pass approach, fix up all classes first, then fix up non class-objects. + // The visited bitmap is used to ensure that pointer arrays are not forwarded twice. + std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> visited_bitmap( + gc::accounting::ContinuousSpaceBitmap::Create("Pointer array bitmap", + target_base, + image_header.GetImageSize())); + FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(), + boot_image, + boot_oat, + app_image, + app_oat); TimingLogger::ScopedTiming timing("Fixup classes", &logger); // Fixup class only touches app image classes, don't need the mutator lock since the space is // not yet visible to the GC. @@ -1025,7 +1064,7 @@ static bool RelocateInPlace(ImageHeader& image_header, bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor); FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat); // Fixup image roots. - CHECK(app_image.ContainsSource(reinterpret_cast<uintptr_t>( + CHECK(app_image.InSource(reinterpret_cast<uintptr_t>( image_header.GetImageRoots<kWithoutReadBarrier>()))); image_header.RelocateImageObjects(app_image.Delta()); CHECK_EQ(image_header.GetImageBegin(), target_base); diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h index ea8b8aae5f..6cb2465539 100644 --- a/runtime/gc/space/memory_tool_malloc_space-inl.h +++ b/runtime/gc/space/memory_tool_malloc_space-inl.h @@ -240,9 +240,9 @@ MemoryToolMallocSpace<S, kAdjustForRedzoneInAllocSize, kUseObjSizeForUsable>::MemoryToolMallocSpace( MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) { - MEMORY_TOOL_MAKE_DEFINED(mem_map->Begin(), initial_size); - MEMORY_TOOL_MAKE_UNDEFINED(mem_map->Begin() + initial_size, - mem_map->Size() - initial_size); + // Don't want to change the valgrind states of the mem map here as the allocator is already + // initialized at this point and that may interfere with what the allocator does internally. Note + // that the tail beyond the initial size is mprotected. } template <typename S, diff --git a/runtime/image.h b/runtime/image.h index c449e43243..146ee00c84 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -143,6 +143,8 @@ class PACKED(4) ImageHeader { oat_checksum_ = oat_checksum; } + // The location that the oat file was expected to be when the image was created. The actual + // oat file may be at a different location for application images. uint8_t* GetOatFileBegin() const { return reinterpret_cast<uint8_t*>(oat_file_begin_); } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index 56aeefc2f5..e3cbf53873 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -290,6 +290,14 @@ class Instrumentation { bool IsActive() const SHARED_REQUIRES(Locks::mutator_lock_) { return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ || have_field_read_listeners_ || have_field_write_listeners_ || + have_exception_caught_listeners_ || have_method_unwind_listeners_ || + have_branch_listeners_ || have_invoke_virtual_or_interface_listeners_; + } + + // Any instrumentation *other* than what is needed for Jit profiling active? + bool NonJitProfilingActive() const SHARED_REQUIRES(Locks::mutator_lock_) { + return have_dex_pc_listeners_ || have_method_exit_listeners_ || + have_field_read_listeners_ || have_field_write_listeners_ || have_exception_caught_listeners_ || have_method_unwind_listeners_; } diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 4fd3c78f44..a595d33f04 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -320,12 +320,13 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, // No Mterp variant - just use the switch interpreter. return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register, false); + } else if (UNLIKELY(!Runtime::Current()->IsStarted())) { + return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register, + false); } else { - const instrumentation::Instrumentation* const instrumentation = - Runtime::Current()->GetInstrumentation(); while (true) { - if (instrumentation->IsActive() || !Runtime::Current()->IsStarted()) { - // TODO: allow JIT profiling instrumentation. Now, just punt on all instrumentation. + // Mterp does not support all instrumentation/debugging. + if (MterpShouldSwitchInterpreters()) { #if !defined(__clang__) return ExecuteGotoImpl<false, false>(self, code_item, shadow_frame, result_register); #else diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index cbaa8173d2..3453abcd64 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -733,39 +733,21 @@ static inline bool DoCallCommon(ArtMethod* called_method, } if (string_init && !self->IsExceptionPending()) { - // Set the new string result of the StringFactory. - shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL()); - // Overwrite all potential copies of the original result of the new-instance of string with the - // new result of the StringFactory. Use the verifier to find this set of registers. - ArtMethod* method = shadow_frame.GetMethod(); - MethodReference method_ref = method->ToMethodReference(); - SafeMap<uint32_t, std::set<uint32_t>>* string_init_map_ptr = nullptr; - MethodRefToStringInitRegMap& method_to_string_init_map = Runtime::Current()->GetStringInitMap(); - { - MutexLock mu(self, *Locks::interpreter_string_init_map_lock_); - auto it = method_to_string_init_map.find(method_ref); - if (it != method_to_string_init_map.end()) { - string_init_map_ptr = &it->second; - } - } - if (string_init_map_ptr == nullptr) { - SafeMap<uint32_t, std::set<uint32_t>> string_init_map = - verifier::MethodVerifier::FindStringInitMap(method); - MutexLock mu(self, *Locks::interpreter_string_init_map_lock_); - auto it = method_to_string_init_map.lower_bound(method_ref); - if (it == method_to_string_init_map.end() || - method_to_string_init_map.key_comp()(method_ref, it->first)) { - it = method_to_string_init_map.PutBefore(it, method_ref, std::move(string_init_map)); - } - string_init_map_ptr = &it->second; - } - if (string_init_map_ptr->size() != 0) { - uint32_t dex_pc = shadow_frame.GetDexPC(); - auto map_it = string_init_map_ptr->find(dex_pc); - if (map_it != string_init_map_ptr->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) { - shadow_frame.SetVRegReference(*set_it, result->GetL()); + mirror::Object* existing = shadow_frame.GetVRegReference(string_init_vreg_this); + if (existing == nullptr) { + // If it's null, we come from compiled code that was deoptimized. Nothing to do, + // as the compiler verified there was no alias. + // Set the new string result of the StringFactory. + shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL()); + } else { + // Replace the fake string that was allocated with the StringFactory result. + for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) { + if (shadow_frame.GetVRegReference(i) == existing) { + DCHECK_EQ(shadow_frame.GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); + shadow_frame.SetVRegReference(i, result->GetL()); + DCHECK_EQ(shadow_frame.GetVRegReference(i), + reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i))); } } } diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 949112de5c..19d971ead8 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -948,11 +948,15 @@ NO_RETURN void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shad __attribute__((cold)) SHARED_REQUIRES(Locks::mutator_lock_); +static inline bool TraceExecutionEnabled() { + // Return true if you want TraceExecution invocation before each bytecode execution. + return false; +} + static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst, const uint32_t dex_pc) SHARED_REQUIRES(Locks::mutator_lock_) { - constexpr bool kTracing = false; - if (kTracing) { + if (TraceExecutionEnabled()) { #define TRACE_LOG std::cerr std::ostringstream oss; oss << PrettyMethod(shadow_frame.GetMethod()) diff --git a/runtime/interpreter/mterp/arm/bincmp.S b/runtime/interpreter/mterp/arm/bincmp.S index 474bc3c276..774e1676b7 100644 --- a/runtime/interpreter/mterp/arm/bincmp.S +++ b/runtime/interpreter/mterp/arm/bincmp.S @@ -6,17 +6,29 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - mov${revcmp} r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + b${revcmp} .L_${opcode}_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r2, rINST, rINST @ convert to bytes, check sign + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_${opcode}_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -25,10 +37,10 @@ GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - mov${revcmp} r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + mov${revcmp} rINST, #2 @ rINST<- BYTE branch dist for not-taken + adds r2, rINST, rINST @ convert to bytes, check sign FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S index 1dba856ecb..3456a7559b 100644 --- a/runtime/interpreter/mterp/arm/footer.S +++ b/runtime/interpreter/mterp/arm/footer.S @@ -12,7 +12,6 @@ * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -103,8 +102,12 @@ MterpException: ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] add rPC, r0, #CODEITEM_INSNS_OFFSET add rPC, rPC, r1, lsl #1 @ generate new dex_pc_ptr - str rPC, [rFP, #OFF_FP_DEX_PC_PTR] + /* Do we need to switch interpreters? */ + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE ip GOTO_OPCODE ip @@ -116,14 +119,33 @@ MterpException: */ MterpCheckSuspendAndContinue: ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bne 1f + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +1: EXPORT_PC mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) - blne MterpSuspendCheck @ (self) + bl MterpSuspendCheck @ (self) + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpLogOSR +#endif + mov r0, #1 @ Signal normal return + b MterpDone + +/* * Bail out to reference interpreter. */ MterpFallback: diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S index b2370bffb4..298af8a57e 100644 --- a/runtime/interpreter/mterp/arm/header.S +++ b/runtime/interpreter/mterp/arm/header.S @@ -85,6 +85,9 @@ unspecified registers or condition codes. */ #include "asm_support.h" +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* During bringup, we'll use the shadow frame model instead of rFP */ /* single-purpose registers, given names for clarity */ #define rPC r4 @@ -109,14 +112,6 @@ unspecified registers or condition codes. #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - -/* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must * be done *before* something throws. * diff --git a/runtime/interpreter/mterp/arm/invoke.S b/runtime/interpreter/mterp/arm/invoke.S index 7575865f1b..e47dd1b3ca 100644 --- a/runtime/interpreter/mterp/arm/invoke.S +++ b/runtime/interpreter/mterp/arm/invoke.S @@ -14,6 +14,9 @@ cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip diff --git a/runtime/interpreter/mterp/arm/op_goto.S b/runtime/interpreter/mterp/arm/op_goto.S index 9b3632aba2..6861950909 100644 --- a/runtime/interpreter/mterp/arm/op_goto.S +++ b/runtime/interpreter/mterp/arm/op_goto.S @@ -6,20 +6,28 @@ */ /* goto +AA */ /* tuning: use sbfx for 6t2+ targets */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsl #16 @ r0<- AAxx0000 - movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended) - add r2, r1, r1 @ r2<- byte offset, set flags - @ If backwards branch refresh rIBASE - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended) + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r2, rINST, rINST @ r2<- byte offset, set flags FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST + @ If backwards branch refresh rIBASE + bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rINST, lsl #16 @ r0<- AAxx0000 - movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended) - add r2, r1, r1 @ r2<- byte offset, set flags + movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended) + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r2, rINST, rINST @ r2<- byte offset, set flags FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST @ If backwards branch refresh rIBASE bmi MterpCheckSuspendAndContinue diff --git a/runtime/interpreter/mterp/arm/op_goto_16.S b/runtime/interpreter/mterp/arm/op_goto_16.S index 2231acdb9e..91639ca796 100644 --- a/runtime/interpreter/mterp/arm/op_goto_16.S +++ b/runtime/interpreter/mterp/arm/op_goto_16.S @@ -5,17 +5,25 @@ * double to get a byte offset. */ /* goto/16 +AAAA */ -#if MTERP_SUSPEND - FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended) - adds r1, r0, r0 @ r1<- byte offset, flags set +#if MTERP_PROFILE_BRANCHES + FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended) + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r1, rINST, rINST @ r1<- byte offset, flags set FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else - FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended) + FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended) ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - adds r1, r0, r0 @ r1<- byte offset, flags set + adds r1, rINST, rINST @ r1<- byte offset, flags set FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_goto_32.S b/runtime/interpreter/mterp/arm/op_goto_32.S index 6b72ff5ce2..e730b527ec 100644 --- a/runtime/interpreter/mterp/arm/op_goto_32.S +++ b/runtime/interpreter/mterp/arm/op_goto_32.S @@ -10,21 +10,29 @@ * offset to byte offset. */ /* goto/32 +AAAAAAAA */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES FETCH r0, 1 @ r0<- aaaa (lo) FETCH r1, 2 @ r1<- AAAA (hi) - orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa - adds r1, r0, r0 @ r1<- byte offset + orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r1, rINST, rINST @ r1<- byte offset FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else FETCH r0, 1 @ r0<- aaaa (lo) FETCH r1, 2 @ r1<- AAAA (hi) + orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa - adds r1, r0, r0 @ r1<- byte offset + adds r1, rINST, rINST @ r1<- byte offset FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_packed_switch.S b/runtime/interpreter/mterp/arm/op_packed_switch.S index 1e3370ea6a..4c369cb136 100644 --- a/runtime/interpreter/mterp/arm/op_packed_switch.S +++ b/runtime/interpreter/mterp/arm/op_packed_switch.S @@ -9,7 +9,7 @@ * for: packed-switch, sparse-switch */ /* op vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES FETCH r0, 1 @ r0<- bbbb (lo) FETCH r1, 2 @ r1<- BBBB (hi) mov r3, rINST, lsr #8 @ r3<- AA @@ -17,9 +17,18 @@ GET_VREG r1, r3 @ r1<- vAA add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2 bl $func @ r0<- code-unit branch offset - adds r1, r0, r0 @ r1<- byte offset; clear V - ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + mov rINST, r0 + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r1, rINST, rINST @ r1<- byte offset; clear V FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST + ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -30,8 +39,9 @@ GET_VREG r1, r3 @ r1<- vAA add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2 bl $func @ r0<- code-unit branch offset + mov rINST, r0 ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - adds r1, r0, r0 @ r1<- byte offset; clear V + adds r1, rINST, rINST @ r1<- byte offset; clear V FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_shl_long.S b/runtime/interpreter/mterp/arm/op_shl_long.S index dc8a679cf7..12ea24883f 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long.S +++ b/runtime/interpreter/mterp/arm/op_shl_long.S @@ -12,16 +12,16 @@ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r2<- r2 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r0, r0, asl r2 @ r0<- r0 << r2 + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S index fd7668de62..4799e77213 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S @@ -6,17 +6,17 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) - mov r0, r0, asl r2 @ r0<- r0 << r2 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_shr_long.S b/runtime/interpreter/mterp/arm/op_shr_long.S index c0edf90f76..88a13d6072 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long.S +++ b/runtime/interpreter/mterp/arm/op_shr_long.S @@ -12,16 +12,16 @@ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, asr r2 @ r1<- r1 >> r2 + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S index ffeaf9c865..78d8bb7dba 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S @@ -6,17 +6,17 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) - mov r1, r1, asr r2 @ r1<- r1 >> r2 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_ushr_long.S b/runtime/interpreter/mterp/arm/op_ushr_long.S index f64c861ce5..f98ec639fa 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long.S @@ -12,16 +12,16 @@ add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S index dbab08d817..840283dd58 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S @@ -6,17 +6,17 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/zcmp.S b/runtime/interpreter/mterp/arm/zcmp.S index 6e9ef55104..800804d95e 100644 --- a/runtime/interpreter/mterp/arm/zcmp.S +++ b/runtime/interpreter/mterp/arm/zcmp.S @@ -6,25 +6,37 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - mov${revcmp} r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + b${revcmp} .L_${opcode}_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_${opcode}_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - mov${revcmp} r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + mov${revcmp} rINST, #2 @ rINST<- inst branch dist for not-taken + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm64/bincmp.S b/runtime/interpreter/mterp/arm64/bincmp.S index ecab2ceba7..ed850fc49d 100644 --- a/runtime/interpreter/mterp/arm64/bincmp.S +++ b/runtime/interpreter/mterp/arm64/bincmp.S @@ -6,17 +6,28 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND - mov w1, wINST, lsr #12 // w1<- B +#if MTERP_PROFILE_BRANCHES + lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) - mov${condition} w1, #2 // w1<- BYTE branch dist for not-taken - adds w2, w1, w1 // convert to bytes, check sign + b.${condition} .L_${opcode}_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_${opcode}_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 // Sign extend branch offset + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else @@ -25,11 +36,11 @@ GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - csel w1, w1, w0, ${condition} // Branch if true - adds w2, w1, w1 // convert to bytes, check sign + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg. + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST diff --git a/runtime/interpreter/mterp/arm64/footer.S b/runtime/interpreter/mterp/arm64/footer.S index b360539a8c..aae78de1b3 100644 --- a/runtime/interpreter/mterp/arm64/footer.S +++ b/runtime/interpreter/mterp/arm64/footer.S @@ -10,7 +10,6 @@ * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -99,8 +98,11 @@ MterpException: ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] add xPC, x0, #CODEITEM_INSNS_OFFSET add xPC, xPC, x1, lsl #1 // generate new dex_pc_ptr - str xPC, [xFP, #OFF_FP_DEX_PC_PTR] + /* Do we need to switch interpreters? */ + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE ip GOTO_OPCODE ip @@ -120,10 +122,24 @@ check1: EXPORT_PC mov x0, xSELF bl MterpSuspendCheck // (self) + cbnz x0, MterpFallback // Something in the environment changed, switch interpreters GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpLogOSR +#endif + mov x0, #1 // Signal normal return + b MterpDone + +/* * Bail out to reference interpreter. */ MterpFallback: diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 351a6075cb..722375002b 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -87,6 +87,9 @@ codes. */ #include "asm_support.h" +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* During bringup, we'll use the shadow frame model instead of xFP */ /* single-purpose registers, given names for clarity */ #define xPC x20 @@ -114,14 +117,6 @@ codes. #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - -/* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must * be done *before* something throws. * diff --git a/runtime/interpreter/mterp/arm64/invoke.S b/runtime/interpreter/mterp/arm64/invoke.S index ff1974c51d..7a32df7bca 100644 --- a/runtime/interpreter/mterp/arm64/invoke.S +++ b/runtime/interpreter/mterp/arm64/invoke.S @@ -9,11 +9,12 @@ mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl $helper cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip diff --git a/runtime/interpreter/mterp/arm64/op_goto.S b/runtime/interpreter/mterp/arm64/op_goto.S index db98a45fae..7e2f6a9c11 100644 --- a/runtime/interpreter/mterp/arm64/op_goto.S +++ b/runtime/interpreter/mterp/arm64/op_goto.S @@ -6,23 +6,20 @@ */ /* goto +AA */ /* tuning: use sbfx for 6t2+ targets */ -#if MTERP_SUSPEND - mov w0, wINST, lsl #16 // w0<- AAxx0000 - movs w1, w0, asr #24 // w1<- ssssssAA (sign-extended) - add w2, w1, w1 // w2<- byte offset, set flags - // If backwards branch refresh rIBASE - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#else - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue lsl w0, wINST, #16 // w0<- AAxx0000 - asr w0, w0, #24 // w0<- ssssssAA (sign-extended) - adds w1, w0, w0 // Convert dalvik offset to byte offset, setting flags + asr wINST, w0, #24 // wINST<- ssssssAA (sign-extended) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue + adds w1, wINST, wINST // Convert dalvik offset to byte offset, setting flags FETCH_ADVANCE_INST_RB w1 // load wINST and advance xPC // If backwards branch refresh rIBASE b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/op_goto_16.S b/runtime/interpreter/mterp/arm64/op_goto_16.S index ff66a23c4e..b2b9924409 100644 --- a/runtime/interpreter/mterp/arm64/op_goto_16.S +++ b/runtime/interpreter/mterp/arm64/op_goto_16.S @@ -5,19 +5,18 @@ * double to get a byte offset. */ /* goto/16 +AAAA */ -#if MTERP_SUSPEND - FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended) - adds w1, w0, w0 // w1<- byte offset, flags set - FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST - ldrmi xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - GET_INST_OPCODE ip // extract opcode from rINST - GOTO_OPCODE ip // jump to next instruction -#else - FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended) + FETCH_S wINST, 1 // wINST<- ssssAAAA (sign-extended) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w1, w0, w0 // w1<- byte offset, flags set + adds w1, wINST, wINST // w1<- byte offset, flags set FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from rINST GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/op_goto_32.S b/runtime/interpreter/mterp/arm64/op_goto_32.S index 8a6980ecea..b785857b9b 100644 --- a/runtime/interpreter/mterp/arm64/op_goto_32.S +++ b/runtime/interpreter/mterp/arm64/op_goto_32.S @@ -10,23 +10,20 @@ * offset to byte offset. */ /* goto/32 +AAAAAAAA */ -#if MTERP_SUSPEND - FETCH w0, 1 // w0<- aaaa (lo) - FETCH w1, 2 // w1<- AAAA (hi) - orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa - adds w1, w0, w0 // w1<- byte offset - FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST - ldrle xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - GET_INST_OPCODE ip // extract opcode from xINST - GOTO_OPCODE ip // jump to next instruction -#else FETCH w0, 1 // w0<- aaaa (lo) FETCH w1, 2 // w1<- AAAA (hi) + orr wINST, w0, w1, lsl #16 // wINST<- AAAAaaaa +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa - adds w1, w0, w0 // w1<- byte offset + adds w1, wINST, wINST // w1<- byte offset FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST b.le MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from xINST GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/op_iget.S b/runtime/interpreter/mterp/arm64/op_iget.S index 165c7308e1..88533bd33d 100644 --- a/runtime/interpreter/mterp/arm64/op_iget.S +++ b/runtime/interpreter/mterp/arm64/op_iget.S @@ -1,4 +1,4 @@ -%default { "is_object":"0", "helper":"artGet32InstanceFromCode"} +%default { "extend":"", "is_object":"0", "helper":"artGet32InstanceFromCode"} /* * General instance field get. * @@ -12,6 +12,7 @@ mov x3, xSELF // w3<- self bl $helper ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET] + $extend ubfx w2, wINST, #8, #4 // w2<- A PREFETCH_INST 2 cbnz x3, MterpPossibleException // bail out diff --git a/runtime/interpreter/mterp/arm64/op_packed_switch.S b/runtime/interpreter/mterp/arm64/op_packed_switch.S index f087d23c0a..e8b4f04dfe 100644 --- a/runtime/interpreter/mterp/arm64/op_packed_switch.S +++ b/runtime/interpreter/mterp/arm64/op_packed_switch.S @@ -9,20 +9,6 @@ * for: packed-switch, sparse-switch */ /* op vAA, +BBBB */ -#if MTERP_SUSPEND - FETCH w0, 1 // w0<- bbbb (lo) - FETCH w1, 2 // w1<- BBBB (hi) - mov w3, wINST, lsr #8 // w3<- AA - orr w0, w0, w1, lsl #16 // w0<- BBBBbbbb - GET_VREG w1, w3 // w1<- vAA - add w0, rPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2 - bl $func // w0<- code-unit branch offset - adds w1, w0, w0 // w1<- byte offset; clear V - ldrle rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#else FETCH w0, 1 // w0<- bbbb (lo) FETCH w1, 2 // w1<- BBBB (hi) lsr w3, wINST, #8 // w3<- AA @@ -30,10 +16,18 @@ GET_VREG w1, w3 // w1<- vAA add x0, xPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2 bl $func // w0<- code-unit branch offset + sbfm xINST, x0, 0, 31 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + mov x2, xINST + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w1, w0, w0 // w1<- byte offset; clear V + adds w1, wINST, wINST // w1<- byte offset; clear V FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST b.le MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/zcmp.S b/runtime/interpreter/mterp/arm64/zcmp.S index d4856d2668..e528d9f030 100644 --- a/runtime/interpreter/mterp/arm64/zcmp.S +++ b/runtime/interpreter/mterp/arm64/zcmp.S @@ -6,26 +6,37 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND - mov w0, wINST, lsr #8 // w0<- AA +#if MTERP_PROFILE_BRANCHES + lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // w1<- branch offset, in code units cmp w2, #0 // compare (vA, 0) - mov${condition} w1, #2 // w1<- inst branch dist for not-taken - adds w1, w1, w1 // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base + b.${condition} .L_${opcode}_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_${opcode}_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags + FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - csel w1, w1, w0, ${condition} // Branch if true - adds w2, w1, w1 // convert to bytes & set flags + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 0afd2765db..8f4741c3ef 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -20,6 +20,8 @@ #include "interpreter/interpreter_common.h" #include "entrypoints/entrypoint_utils-inl.h" #include "mterp.h" +#include "jit/jit.h" +#include "debugger.h" namespace art { namespace interpreter { @@ -45,7 +47,9 @@ void CheckMterpAsmConstants() { void InitMterpTls(Thread* self) { self->SetMterpDefaultIBase(artMterpAsmInstructionStart); self->SetMterpAltIBase(artMterpAsmAltInstructionStart); - self->SetMterpCurrentIBase(artMterpAsmInstructionStart); + self->SetMterpCurrentIBase(TraceExecutionEnabled() ? + artMterpAsmAltInstructionStart : + artMterpAsmInstructionStart); } /* @@ -139,6 +143,20 @@ extern "C" int32_t MterpDoPackedSwitch(const uint16_t* switchData, int32_t testV return entries[index]; } +extern "C" bool MterpShouldSwitchInterpreters() + SHARED_REQUIRES(Locks::mutator_lock_) { + const instrumentation::Instrumentation* const instrumentation = + Runtime::Current()->GetInstrumentation(); + bool unhandled_instrumentation; + // TODO: enable for other targets after more extensive testing. + if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm)) { + unhandled_instrumentation = instrumentation->NonJitProfilingActive(); + } else { + unhandled_instrumentation = instrumentation->IsActive(); + } + return unhandled_instrumentation || Dbg::IsDebuggerActive(); +} + extern "C" bool MterpInvokeVirtual(Thread* self, ShadowFrame* shadow_frame, uint16_t* dex_pc_ptr, uint16_t inst_data ) @@ -429,6 +447,7 @@ extern "C" void MterpCheckBefore(Thread* self, ShadowFrame* shadow_frame) } else { self->AssertNoPendingException(); } + TraceExecution(*shadow_frame, inst, shadow_frame->GetDexPC()); } extern "C" void MterpLogDivideByZeroException(Thread* self, ShadowFrame* shadow_frame) @@ -488,6 +507,14 @@ extern "C" void MterpLogFallback(Thread* self, ShadowFrame* shadow_frame) << self->IsExceptionPending(); } +extern "C" void MterpLogOSR(Thread* self, ShadowFrame* shadow_frame, int32_t offset) + SHARED_REQUIRES(Locks::mutator_lock_) { + UNUSED(self); + const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr()); + uint16_t inst_data = inst->Fetch16(0); + LOG(INFO) << "OSR: " << inst->Opcode(inst_data) << ", offset = " << offset; +} + extern "C" void MterpLogSuspendFallback(Thread* self, ShadowFrame* shadow_frame, uint32_t flags) SHARED_REQUIRES(Locks::mutator_lock_) { UNUSED(self); @@ -500,9 +527,10 @@ extern "C" void MterpLogSuspendFallback(Thread* self, ShadowFrame* shadow_frame, } } -extern "C" void MterpSuspendCheck(Thread* self) +extern "C" bool MterpSuspendCheck(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { self->AllowThreadSuspension(); + return MterpShouldSwitchInterpreters(); } extern "C" int artSet64IndirectStaticFromMterp(uint32_t field_idx, ArtMethod* referrer, @@ -618,5 +646,15 @@ extern "C" mirror::Object* artIGetObjectFromMterp(mirror::Object* obj, uint32_t return obj->GetFieldObject<mirror::Object>(MemberOffset(field_offset)); } +extern "C" bool MterpProfileBranch(Thread* self, ShadowFrame* shadow_frame, int32_t offset) + SHARED_REQUIRES(Locks::mutator_lock_) { + ArtMethod* method = shadow_frame->GetMethod(); + JValue* result = shadow_frame->GetResultRegister(); + uint32_t dex_pc = shadow_frame->GetDexPC(); + const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); + instrumentation->Branch(self, method, dex_pc, offset); + return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result); +} + } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/mterp/mterp.h b/runtime/interpreter/mterp/mterp.h index 90d21e9c89..8d24641ce4 100644 --- a/runtime/interpreter/mterp/mterp.h +++ b/runtime/interpreter/mterp/mterp.h @@ -30,6 +30,7 @@ namespace interpreter { void InitMterpTls(Thread* self); void CheckMterpAsmConstants(); +extern "C" bool MterpShouldSwitchInterpreters(); } // namespace interpreter } // namespace art diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index ee195598db..94cbd2d10e 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -92,6 +92,9 @@ unspecified registers or condition codes. */ #include "asm_support.h" +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* During bringup, we'll use the shadow frame model instead of rFP */ /* single-purpose registers, given names for clarity */ #define rPC r4 @@ -116,14 +119,6 @@ unspecified registers or condition codes. #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - -/* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must * be done *before* something throws. * @@ -1111,20 +1106,28 @@ artMterpAsmInstructionStart = .L_op_nop */ /* goto +AA */ /* tuning: use sbfx for 6t2+ targets */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsl #16 @ r0<- AAxx0000 - movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended) - add r2, r1, r1 @ r2<- byte offset, set flags - @ If backwards branch refresh rIBASE - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended) + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r2, rINST, rINST @ r2<- byte offset, set flags FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST + @ If backwards branch refresh rIBASE + bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] mov r0, rINST, lsl #16 @ r0<- AAxx0000 - movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended) - add r2, r1, r1 @ r2<- byte offset, set flags + movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended) + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r2, rINST, rINST @ r2<- byte offset, set flags FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST @ If backwards branch refresh rIBASE bmi MterpCheckSuspendAndContinue @@ -1143,17 +1146,25 @@ artMterpAsmInstructionStart = .L_op_nop * double to get a byte offset. */ /* goto/16 +AAAA */ -#if MTERP_SUSPEND - FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended) - adds r1, r0, r0 @ r1<- byte offset, flags set +#if MTERP_PROFILE_BRANCHES + FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended) + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r1, rINST, rINST @ r1<- byte offset, flags set FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else - FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended) + FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended) ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - adds r1, r0, r0 @ r1<- byte offset, flags set + adds r1, rINST, rINST @ r1<- byte offset, flags set FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1176,21 +1187,29 @@ artMterpAsmInstructionStart = .L_op_nop * offset to byte offset. */ /* goto/32 +AAAAAAAA */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES FETCH r0, 1 @ r0<- aaaa (lo) FETCH r1, 2 @ r1<- AAAA (hi) - orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa - adds r1, r0, r0 @ r1<- byte offset + orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r1, rINST, rINST @ r1<- byte offset FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else FETCH r0, 1 @ r0<- aaaa (lo) FETCH r1, 2 @ r1<- AAAA (hi) + orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa - adds r1, r0, r0 @ r1<- byte offset + adds r1, rINST, rINST @ r1<- byte offset FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1211,7 +1230,7 @@ artMterpAsmInstructionStart = .L_op_nop * for: packed-switch, sparse-switch */ /* op vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES FETCH r0, 1 @ r0<- bbbb (lo) FETCH r1, 2 @ r1<- BBBB (hi) mov r3, rINST, lsr #8 @ r3<- AA @@ -1219,9 +1238,18 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r1, r3 @ r1<- vAA add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2 bl MterpDoPackedSwitch @ r0<- code-unit branch offset - adds r1, r0, r0 @ r1<- byte offset; clear V - ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + mov rINST, r0 + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r1, rINST, rINST @ r1<- byte offset; clear V FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST + ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1232,8 +1260,9 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r1, r3 @ r1<- vAA add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2 bl MterpDoPackedSwitch @ r0<- code-unit branch offset + mov rINST, r0 ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - adds r1, r0, r0 @ r1<- byte offset; clear V + adds r1, rINST, rINST @ r1<- byte offset; clear V FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1255,7 +1284,7 @@ artMterpAsmInstructionStart = .L_op_nop * for: packed-switch, sparse-switch */ /* op vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES FETCH r0, 1 @ r0<- bbbb (lo) FETCH r1, 2 @ r1<- BBBB (hi) mov r3, rINST, lsr #8 @ r3<- AA @@ -1263,9 +1292,18 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r1, r3 @ r1<- vAA add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2 bl MterpDoSparseSwitch @ r0<- code-unit branch offset - adds r1, r0, r0 @ r1<- byte offset; clear V - ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base + mov rINST, r0 + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + adds r1, rINST, rINST @ r1<- byte offset; clear V FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST + ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1276,8 +1314,9 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r1, r3 @ r1<- vAA add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2 bl MterpDoSparseSwitch @ r0<- code-unit branch offset + mov rINST, r0 ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - adds r1, r0, r0 @ r1<- byte offset; clear V + adds r1, rINST, rINST @ r1<- byte offset; clear V FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST ble MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1495,17 +1534,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movne r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + bne .L_op_if_eq_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r2, rINST, rINST @ convert to bytes, check sign + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_eq_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1514,10 +1565,10 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movne r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + movne rINST, #2 @ rINST<- BYTE branch dist for not-taken + adds r2, rINST, rINST @ convert to bytes, check sign FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1538,17 +1589,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - moveq r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + beq .L_op_if_ne_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r2, rINST, rINST @ convert to bytes, check sign + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_ne_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1557,10 +1620,10 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - moveq r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + moveq rINST, #2 @ rINST<- BYTE branch dist for not-taken + adds r2, rINST, rINST @ convert to bytes, check sign FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1581,17 +1644,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movge r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + bge .L_op_if_lt_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r2, rINST, rINST @ convert to bytes, check sign + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_lt_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1600,10 +1675,10 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movge r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + movge rINST, #2 @ rINST<- BYTE branch dist for not-taken + adds r2, rINST, rINST @ convert to bytes, check sign FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1624,17 +1699,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movlt r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + blt .L_op_if_ge_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r2, rINST, rINST @ convert to bytes, check sign + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_ge_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1643,10 +1730,10 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movlt r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + movlt rINST, #2 @ rINST<- BYTE branch dist for not-taken + adds r2, rINST, rINST @ convert to bytes, check sign FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1667,17 +1754,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movle r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + ble .L_op_if_gt_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r2, rINST, rINST @ convert to bytes, check sign + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_gt_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1686,10 +1785,10 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movle r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + movle rINST, #2 @ rINST<- BYTE branch dist for not-taken + adds r2, rINST, rINST @ convert to bytes, check sign FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1710,17 +1809,29 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r1, rINST, lsr #12 @ r1<- B ubfx r0, rINST, #8, #4 @ r0<- A GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movgt r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + bgt .L_op_if_le_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r2, rINST, rINST @ convert to bytes, check sign + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_le_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else @@ -1729,10 +1840,10 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG r3, r1 @ r3<- vB GET_VREG r2, r0 @ r2<- vA ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - movgt r1, #2 @ r1<- BYTE branch dist for not-taken - adds r2, r1, r1 @ convert to bytes, check sign + movgt rINST, #2 @ rINST<- BYTE branch dist for not-taken + adds r2, rINST, rINST @ convert to bytes, check sign FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1753,25 +1864,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movne r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + bne .L_op_if_eqz_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_eqz_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movne r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + movne rINST, #2 @ rINST<- inst branch dist for not-taken + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1792,25 +1915,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - moveq r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + beq .L_op_if_nez_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_nez_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - moveq r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + moveq rINST, #2 @ rINST<- inst branch dist for not-taken + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1831,25 +1966,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movge r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + bge .L_op_if_ltz_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_ltz_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movge r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + movge rINST, #2 @ rINST<- inst branch dist for not-taken + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1870,25 +2017,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movlt r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + blt .L_op_if_gez_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_gez_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movlt r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + movlt rINST, #2 @ rINST<- inst branch dist for not-taken + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1909,25 +2068,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movle r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + ble .L_op_if_gtz_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_gtz_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movle r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + movle rINST, #2 @ rINST<- inst branch dist for not-taken + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -1948,25 +2119,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND +#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movgt r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + bgt .L_op_if_lez_not_taken + EXPORT_PC + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpProfileBranch @ (self, shadow_frame, offset) + cmp r0, #0 + bne MterpOnStackReplacement @ Note: offset must be in rINST + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST - ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base + bmi MterpCheckSuspendAndContinue + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +.L_op_if_lez_not_taken: + FETCH_ADVANCE_INST 2 @ update rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction #else mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA - FETCH_S r1, 1 @ r1<- branch offset, in code units + FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - movgt r1, #2 @ r1<- inst branch dist for not-taken - adds r1, r1, r1 @ convert to bytes & set flags + movgt rINST, #2 @ rINST<- inst branch dist for not-taken + adds r1, rINST, rINST @ convert to bytes & set flags FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST bmi MterpCheckSuspendAndContinue GET_INST_OPCODE ip @ extract opcode from rINST @@ -3294,6 +3477,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3326,6 +3512,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3358,6 +3547,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3383,6 +3575,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3409,6 +3604,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3453,6 +3651,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3478,6 +3679,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3503,6 +3707,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3528,6 +3735,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3553,6 +3763,9 @@ artMterpAsmInstructionStart = .L_op_nop cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -4948,16 +5161,16 @@ constvalop_long_to_double: add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r2<- r2 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r0, r0, asl r2 @ r0<- r0 << r2 + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -4980,16 +5193,16 @@ constvalop_long_to_double: add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, asr r2 @ r1<- r1 >> r2 + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -5012,16 +5225,16 @@ constvalop_long_to_double: add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] GET_VREG r2, r0 @ r2<- vCC ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1 + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs and r2, r2, #63 @ r0<- r0 & 0x3f add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -6087,17 +6300,17 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r1, r1, asl r2 @ r1<- r1 << r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r1, r1, asl r2 @ r1<- r1 << r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) - mov r0, r0, asl r2 @ r0<- r0 << r2 + movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32) + mov r0, r0, asl r2 @ r0<- r0 << r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -6114,17 +6327,17 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) - mov r1, r1, asr r2 @ r1<- r1 >> r2 + movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32) + mov r1, r1, asr r2 @ r1<- r1 >> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -6141,17 +6354,17 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A GET_VREG r2, r3 @ r2<- vB + CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs add r9, rFP, r9, lsl #2 @ r9<- &fp[A] and r2, r2, #63 @ r2<- r2 & 0x3f ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 - - mov r0, r0, lsr r2 @ r0<- r2 >> r2 - rsb r3, r2, #32 @ r3<- 32 - r2 - orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) - subs ip, r2, #32 @ ip<- r2 - 32 + mov r0, r0, lsr r2 @ r0<- r2 >> r2 + rsb r3, r2, #32 @ r3<- 32 - r2 + orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2)) + subs ip, r2, #32 @ ip<- r2 - 32 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) - mov r1, r1, lsr r2 @ r1<- r1 >>> r2 + movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32) + mov r1, r1, lsr r2 @ r1<- r1 >>> r2 GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -7284,6 +7497,9 @@ constvalop_long_to_double: cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -7309,6 +7525,9 @@ constvalop_long_to_double: cmp r0, #0 beq MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -12098,7 +12317,6 @@ artMterpAsmAltInstructionEnd: * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -12189,8 +12407,12 @@ MterpException: ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] add rPC, r0, #CODEITEM_INSNS_OFFSET add rPC, rPC, r1, lsl #1 @ generate new dex_pc_ptr - str rPC, [rFP, #OFF_FP_DEX_PC_PTR] + /* Do we need to switch interpreters? */ + bl MterpShouldSwitchInterpreters + cmp r0, #0 + bne MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE ip GOTO_OPCODE ip @@ -12202,14 +12424,33 @@ MterpException: */ MterpCheckSuspendAndContinue: ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bne 1f + GET_INST_OPCODE ip @ extract opcode from rINST + GOTO_OPCODE ip @ jump to next instruction +1: EXPORT_PC mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) - blne MterpSuspendCheck @ (self) + bl MterpSuspendCheck @ (self) + cmp r0, #0 + bne MterpFallback GET_INST_OPCODE ip @ extract opcode from rINST GOTO_OPCODE ip @ jump to next instruction /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + mov r0, rSELF + add r1, rFP, #OFF_FP_SHADOWFRAME + mov r2, rINST + bl MterpLogOSR +#endif + mov r0, #1 @ Signal normal return + b MterpDone + +/* * Bail out to reference interpreter. */ MterpFallback: diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index e9d28abf8b..e4825f0489 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -94,6 +94,9 @@ codes. */ #include "asm_support.h" +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* During bringup, we'll use the shadow frame model instead of xFP */ /* single-purpose registers, given names for clarity */ #define xPC x20 @@ -121,14 +124,6 @@ codes. #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) /* - * - * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. - * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually - * mterp should do so as well. - */ -#define MTERP_SUSPEND 0 - -/* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must * be done *before* something throws. * @@ -1087,26 +1082,23 @@ artMterpAsmInstructionStart = .L_op_nop */ /* goto +AA */ /* tuning: use sbfx for 6t2+ targets */ -#if MTERP_SUSPEND - mov w0, wINST, lsl #16 // w0<- AAxx0000 - movs w1, w0, asr #24 // w1<- ssssssAA (sign-extended) - add w2, w1, w1 // w2<- byte offset, set flags - // If backwards branch refresh rIBASE - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#else - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue lsl w0, wINST, #16 // w0<- AAxx0000 - asr w0, w0, #24 // w0<- ssssssAA (sign-extended) - adds w1, w0, w0 // Convert dalvik offset to byte offset, setting flags + asr wINST, w0, #24 // wINST<- ssssssAA (sign-extended) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST +#endif + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue + adds w1, wINST, wINST // Convert dalvik offset to byte offset, setting flags FETCH_ADVANCE_INST_RB w1 // load wINST and advance xPC // If backwards branch refresh rIBASE b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ .balign 128 @@ -1119,22 +1111,21 @@ artMterpAsmInstructionStart = .L_op_nop * double to get a byte offset. */ /* goto/16 +AAAA */ -#if MTERP_SUSPEND - FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended) - adds w1, w0, w0 // w1<- byte offset, flags set - FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST - ldrmi xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - GET_INST_OPCODE ip // extract opcode from rINST - GOTO_OPCODE ip // jump to next instruction -#else - FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended) + FETCH_S wINST, 1 // wINST<- ssssAAAA (sign-extended) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w1, w0, w0 // w1<- byte offset, flags set + adds w1, wINST, wINST // w1<- byte offset, flags set FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from rINST GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ .balign 128 @@ -1152,26 +1143,23 @@ artMterpAsmInstructionStart = .L_op_nop * offset to byte offset. */ /* goto/32 +AAAAAAAA */ -#if MTERP_SUSPEND - FETCH w0, 1 // w0<- aaaa (lo) - FETCH w1, 2 // w1<- AAAA (hi) - orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa - adds w1, w0, w0 // w1<- byte offset - FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST - ldrle xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - GET_INST_OPCODE ip // extract opcode from xINST - GOTO_OPCODE ip // jump to next instruction -#else FETCH w0, 1 // w0<- aaaa (lo) FETCH w1, 2 // w1<- AAAA (hi) + orr wINST, w0, w1, lsl #16 // wINST<- AAAAaaaa +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa - adds w1, w0, w0 // w1<- byte offset + adds w1, wINST, wINST // w1<- byte offset FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST b.le MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from xINST GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ .balign 128 @@ -1187,20 +1175,6 @@ artMterpAsmInstructionStart = .L_op_nop * for: packed-switch, sparse-switch */ /* op vAA, +BBBB */ -#if MTERP_SUSPEND - FETCH w0, 1 // w0<- bbbb (lo) - FETCH w1, 2 // w1<- BBBB (hi) - mov w3, wINST, lsr #8 // w3<- AA - orr w0, w0, w1, lsl #16 // w0<- BBBBbbbb - GET_VREG w1, w3 // w1<- vAA - add w0, rPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2 - bl MterpDoPackedSwitch // w0<- code-unit branch offset - adds w1, w0, w0 // w1<- byte offset; clear V - ldrle rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#else FETCH w0, 1 // w0<- bbbb (lo) FETCH w1, 2 // w1<- BBBB (hi) lsr w3, wINST, #8 // w3<- AA @@ -1208,13 +1182,21 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w1, w3 // w1<- vAA add x0, xPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2 bl MterpDoPackedSwitch // w0<- code-unit branch offset + sbfm xINST, x0, 0, 31 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + mov x2, xINST + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w1, w0, w0 // w1<- byte offset; clear V + adds w1, wINST, wINST // w1<- byte offset; clear V FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST b.le MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ .balign 128 @@ -1231,20 +1213,6 @@ artMterpAsmInstructionStart = .L_op_nop * for: packed-switch, sparse-switch */ /* op vAA, +BBBB */ -#if MTERP_SUSPEND - FETCH w0, 1 // w0<- bbbb (lo) - FETCH w1, 2 // w1<- BBBB (hi) - mov w3, wINST, lsr #8 // w3<- AA - orr w0, w0, w1, lsl #16 // w0<- BBBBbbbb - GET_VREG w1, w3 // w1<- vAA - add w0, rPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2 - bl MterpDoSparseSwitch // w0<- code-unit branch offset - adds w1, w0, w0 // w1<- byte offset; clear V - ldrle rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - GET_INST_OPCODE ip // extract opcode from wINST - GOTO_OPCODE ip // jump to next instruction -#else FETCH w0, 1 // w0<- bbbb (lo) FETCH w1, 2 // w1<- BBBB (hi) lsr w3, wINST, #8 // w3<- AA @@ -1252,13 +1220,21 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w1, w3 // w1<- vAA add x0, xPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2 bl MterpDoSparseSwitch // w0<- code-unit branch offset + sbfm xINST, x0, 0, 31 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + mov x2, xINST + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement +#endif ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] - adds w1, w0, w0 // w1<- byte offset; clear V + adds w1, wINST, wINST // w1<- byte offset; clear V FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST b.le MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1396,17 +1372,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND - mov w1, wINST, lsr #12 // w1<- B +#if MTERP_PROFILE_BRANCHES + lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) - moveq w1, #2 // w1<- BYTE branch dist for not-taken - adds w2, w1, w1 // convert to bytes, check sign + b.eq .L_op_if_eq_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_eq_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 // Sign extend branch offset + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else @@ -1415,11 +1402,11 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - csel w1, w1, w0, eq // Branch if true - adds w2, w1, w1 // convert to bytes, check sign + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg. + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1440,17 +1427,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND - mov w1, wINST, lsr #12 // w1<- B +#if MTERP_PROFILE_BRANCHES + lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) - movne w1, #2 // w1<- BYTE branch dist for not-taken - adds w2, w1, w1 // convert to bytes, check sign + b.ne .L_op_if_ne_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_ne_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 // Sign extend branch offset + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else @@ -1459,11 +1457,11 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - csel w1, w1, w0, ne // Branch if true - adds w2, w1, w1 // convert to bytes, check sign + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg. + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1484,17 +1482,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND - mov w1, wINST, lsr #12 // w1<- B +#if MTERP_PROFILE_BRANCHES + lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) - movlt w1, #2 // w1<- BYTE branch dist for not-taken - adds w2, w1, w1 // convert to bytes, check sign + b.lt .L_op_if_lt_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_lt_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 // Sign extend branch offset + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else @@ -1503,11 +1512,11 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - csel w1, w1, w0, lt // Branch if true - adds w2, w1, w1 // convert to bytes, check sign + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg. + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1528,17 +1537,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND - mov w1, wINST, lsr #12 // w1<- B +#if MTERP_PROFILE_BRANCHES + lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) - movge w1, #2 // w1<- BYTE branch dist for not-taken - adds w2, w1, w1 // convert to bytes, check sign + b.ge .L_op_if_ge_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_ge_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 // Sign extend branch offset + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else @@ -1547,11 +1567,11 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - csel w1, w1, w0, ge // Branch if true - adds w2, w1, w1 // convert to bytes, check sign + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg. + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1572,17 +1592,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND - mov w1, wINST, lsr #12 // w1<- B +#if MTERP_PROFILE_BRANCHES + lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) - movgt w1, #2 // w1<- BYTE branch dist for not-taken - adds w2, w1, w1 // convert to bytes, check sign + b.gt .L_op_if_gt_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_gt_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 // Sign extend branch offset + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else @@ -1591,11 +1622,11 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - csel w1, w1, w0, gt // Branch if true - adds w2, w1, w1 // convert to bytes, check sign + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg. + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1616,17 +1647,28 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#if MTERP_SUSPEND - mov w1, wINST, lsr #12 // w1<- B +#if MTERP_PROFILE_BRANCHES + lsr w1, wINST, #12 // w1<- B ubfx w0, wINST, #8, #4 // w0<- A GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // wINST<- branch offset, in code units cmp w2, w3 // compare (vA, vB) - movle w1, #2 // w1<- BYTE branch dist for not-taken - adds w2, w1, w1 // convert to bytes, check sign + b.le .L_op_if_le_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_le_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 // Sign extend branch offset + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else @@ -1635,11 +1677,11 @@ artMterpAsmInstructionStart = .L_op_nop GET_VREG w3, w1 // w3<- vB GET_VREG w2, w0 // w2<- vA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - csel w1, w1, w0, le // Branch if true - adds w2, w1, w1 // convert to bytes, check sign + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg. + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes, check sign FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1660,26 +1702,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND - mov w0, wINST, lsr #8 // w0<- AA +#if MTERP_PROFILE_BRANCHES + lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // w1<- branch offset, in code units cmp w2, #0 // compare (vA, 0) - moveq w1, #2 // w1<- inst branch dist for not-taken - adds w1, w1, w1 // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base + b.eq .L_op_if_eqz_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_eqz_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags + FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - csel w1, w1, w0, eq // Branch if true - adds w2, w1, w1 // convert to bytes & set flags + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1700,26 +1753,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND - mov w0, wINST, lsr #8 // w0<- AA +#if MTERP_PROFILE_BRANCHES + lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // w1<- branch offset, in code units cmp w2, #0 // compare (vA, 0) - movne w1, #2 // w1<- inst branch dist for not-taken - adds w1, w1, w1 // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base + b.ne .L_op_if_nez_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_nez_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags + FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - csel w1, w1, w0, ne // Branch if true - adds w2, w1, w1 // convert to bytes & set flags + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1740,26 +1804,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND - mov w0, wINST, lsr #8 // w0<- AA +#if MTERP_PROFILE_BRANCHES + lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // w1<- branch offset, in code units cmp w2, #0 // compare (vA, 0) - movlt w1, #2 // w1<- inst branch dist for not-taken - adds w1, w1, w1 // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base + b.lt .L_op_if_ltz_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_ltz_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags + FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - csel w1, w1, w0, lt // Branch if true - adds w2, w1, w1 // convert to bytes & set flags + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1780,26 +1855,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND - mov w0, wINST, lsr #8 // w0<- AA +#if MTERP_PROFILE_BRANCHES + lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // w1<- branch offset, in code units cmp w2, #0 // compare (vA, 0) - movge w1, #2 // w1<- inst branch dist for not-taken - adds w1, w1, w1 // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base + b.ge .L_op_if_gez_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_gez_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags + FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - csel w1, w1, w0, ge // Branch if true - adds w2, w1, w1 // convert to bytes & set flags + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1820,26 +1906,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND - mov w0, wINST, lsr #8 // w0<- AA +#if MTERP_PROFILE_BRANCHES + lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // w1<- branch offset, in code units cmp w2, #0 // compare (vA, 0) - movgt w1, #2 // w1<- inst branch dist for not-taken - adds w1, w1, w1 // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base + b.gt .L_op_if_gtz_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_gtz_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags + FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - csel w1, w1, w0, gt // Branch if true - adds w2, w1, w1 // convert to bytes & set flags + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -1860,26 +1957,37 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_SUSPEND - mov w0, wINST, lsr #8 // w0<- AA +#if MTERP_PROFILE_BRANCHES + lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S w1, 1 // w1<- branch offset, in code units + FETCH_S wINST, 1 // w1<- branch offset, in code units cmp w2, #0 // compare (vA, 0) - movle w1, #2 // w1<- inst branch dist for not-taken - adds w1, w1, w1 // convert to bytes & set flags - FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST - ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base + b.le .L_op_if_lez_taken + FETCH_ADVANCE_INST 2 // update rPC, load wINST + GET_INST_OPCODE ip // extract opcode from wINST + GOTO_OPCODE ip // jump to next instruction +.L_op_if_lez_taken: + EXPORT_PC + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpProfileBranch // (self, shadow_frame, offset) + cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags + FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST + b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction #else lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA FETCH_S w1, 1 // w1<- branch offset, in code units - ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - csel w1, w1, w0, le // Branch if true - adds w2, w1, w1 // convert to bytes & set flags + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg + ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] + adds w2, wINST, wINST // convert to bytes & set flags FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST b.mi MterpCheckSuspendAndContinue GET_INST_OPCODE ip // extract opcode from wINST @@ -2401,6 +2509,7 @@ artMterpAsmInstructionStart = .L_op_nop mov x3, xSELF // w3<- self bl artGet32InstanceFromCode ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET] + ubfx w2, wINST, #8, #4 // w2<- A PREFETCH_INST 2 cbnz x3, MterpPossibleException // bail out @@ -2457,6 +2566,7 @@ artMterpAsmInstructionStart = .L_op_nop mov x3, xSELF // w3<- self bl artGetObjInstanceFromCode ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET] + ubfx w2, wINST, #8, #4 // w2<- A PREFETCH_INST 2 cbnz x3, MterpPossibleException // bail out @@ -2488,6 +2598,7 @@ artMterpAsmInstructionStart = .L_op_nop mov x3, xSELF // w3<- self bl artGetBooleanInstanceFromCode ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET] + uxtb w0, w0 ubfx w2, wINST, #8, #4 // w2<- A PREFETCH_INST 2 cbnz x3, MterpPossibleException // bail out @@ -2519,6 +2630,7 @@ artMterpAsmInstructionStart = .L_op_nop mov x3, xSELF // w3<- self bl artGetByteInstanceFromCode ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET] + sxtb w0, w0 ubfx w2, wINST, #8, #4 // w2<- A PREFETCH_INST 2 cbnz x3, MterpPossibleException // bail out @@ -2550,6 +2662,7 @@ artMterpAsmInstructionStart = .L_op_nop mov x3, xSELF // w3<- self bl artGetCharInstanceFromCode ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET] + uxth w0, w0 ubfx w2, wINST, #8, #4 // w2<- A PREFETCH_INST 2 cbnz x3, MterpPossibleException // bail out @@ -2581,6 +2694,7 @@ artMterpAsmInstructionStart = .L_op_nop mov x3, xSELF // w3<- self bl artGetShortInstanceFromCode ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET] + sxth w0, w0 ubfx w2, wINST, #8, #4 // w2<- A PREFETCH_INST 2 cbnz x3, MterpPossibleException // bail out @@ -3158,11 +3272,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeVirtual cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3190,11 +3305,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeSuper cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3222,11 +3338,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeDirect cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3247,11 +3364,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeStatic cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3273,11 +3391,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeInterface cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3320,11 +3439,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeVirtualRange cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3345,11 +3465,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeSuperRange cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3370,11 +3491,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeDirectRange cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3395,11 +3517,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeStaticRange cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -3420,11 +3543,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeInterfaceRange cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -6852,11 +6976,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeVirtualQuick cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -6877,11 +7002,12 @@ artMterpAsmInstructionStart = .L_op_nop mov x0, xSELF add x1, xFP, #OFF_FP_SHADOWFRAME mov x2, xPC - // and x3, xINST, 0xFFFF mov x3, xINST bl MterpInvokeVirtualQuickRange cbz w0, MterpException FETCH_ADVANCE_INST 3 + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback GET_INST_OPCODE ip GOTO_OPCODE ip @@ -11565,7 +11691,6 @@ artMterpAsmAltInstructionEnd: * has not yet been thrown. Just bail out to the reference interpreter to deal with it. * TUNING: for consistency, we may want to just go ahead and handle these here. */ -#define MTERP_LOGGING 0 common_errDivideByZero: EXPORT_PC #if MTERP_LOGGING @@ -11654,8 +11779,11 @@ MterpException: ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] add xPC, x0, #CODEITEM_INSNS_OFFSET add xPC, xPC, x1, lsl #1 // generate new dex_pc_ptr - str xPC, [xFP, #OFF_FP_DEX_PC_PTR] + /* Do we need to switch interpreters? */ + bl MterpShouldSwitchInterpreters + cbnz w0, MterpFallback /* resume execution at catch block */ + EXPORT_PC FETCH_INST GET_INST_OPCODE ip GOTO_OPCODE ip @@ -11675,10 +11803,24 @@ check1: EXPORT_PC mov x0, xSELF bl MterpSuspendCheck // (self) + cbnz x0, MterpFallback // Something in the environment changed, switch interpreters GET_INST_OPCODE ip // extract opcode from wINST GOTO_OPCODE ip // jump to next instruction /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + mov x0, xSELF + add x1, xFP, #OFF_FP_SHADOWFRAME + sbfm x2, xINST, 0, 31 + bl MterpLogOSR +#endif + mov x0, #1 // Signal normal return + b MterpDone + +/* * Bail out to reference interpreter. */ MterpFallback: diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index 96229ceba0..d365a4f986 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -189,13 +189,21 @@ unspecified registers or condition codes. /* * Refresh handler table. + */ +.macro REFRESH_IBASE + movl rSELF, rIBASE + movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE +.endm + +/* + * Refresh handler table. * IBase handles uses the caller save register so we must restore it after each call. * Also it is used as a result of some 64-bit operations (like imul) and we should * restore it in such cases also. * * TODO: Consider spilling the IBase instead of restoring it from Thread structure. */ -.macro REFRESH_IBASE +.macro RESTORE_IBASE movl rSELF, rIBASE movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE .endm @@ -203,7 +211,7 @@ unspecified registers or condition codes. /* * If rSELF is already loaded then we can use it from known reg. */ -.macro REFRESH_IBASE_FROM_SELF _reg +.macro RESTORE_IBASE_FROM_SELF _reg movl THREAD_CURRENT_IBASE_OFFSET(\_reg), rIBASE .endm @@ -771,8 +779,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -790,8 +798,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -809,8 +817,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -828,8 +836,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %eax movl %eax, OUT_ARG1(%esp) call SYMBOL(artLockObjectFromCode) # (object, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpException ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 @@ -851,8 +859,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %eax movl %eax, OUT_ARG1(%esp) call SYMBOL(artUnlockObjectFromCode) # (object, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpException ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 @@ -874,8 +882,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpCheckCast) # (index, &obj, method, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -903,7 +911,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException andb $0xf, rINSTbl # rINSTbl <- A @@ -943,8 +951,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 34 movl rINST, OUT_ARG2(%esp) call SYMBOL(MterpNewInstance) - REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + RESTORE_IBASE + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -969,8 +977,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpNewArray) - REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + RESTORE_IBASE + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -994,7 +1002,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) call SYMBOL(MterpFilledNewArray) REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -1019,7 +1027,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) call SYMBOL(MterpFilledNewArrayRange) REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -1037,7 +1045,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG1(%esp) call SYMBOL(MterpFillArrayData) # (obj, payload) REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -1923,7 +1931,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG1(%esp) call SYMBOL(artAGetObjectFromMterp) # (array, index) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException SET_VREG_OBJECT %eax, rINST @@ -2090,8 +2098,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 77 movl rINST, OUT_ARG2(%esp) call SYMBOL(MterpAputObject) # (array, index) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -2221,7 +2229,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artGet32InstanceFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $0xf, rINSTbl # rINST <- A @@ -2259,7 +2267,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop andb $0xf, rINSTbl # rINST <- A SET_VREG %eax, rINST SET_VREG_HIGH %edx, rINST - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2285,7 +2293,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artGetObjInstanceFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $0xf, rINSTbl # rINST <- A @@ -2320,7 +2328,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artGetBooleanInstanceFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $0xf, rINSTbl # rINST <- A @@ -2355,7 +2363,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artGetByteInstanceFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $0xf, rINSTbl # rINST <- A @@ -2390,7 +2398,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artGetCharInstanceFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $0xf, rINSTbl # rINST <- A @@ -2425,7 +2433,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artGetShortInstanceFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $0xf, rINSTbl # rINST <- A @@ -2461,9 +2469,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL(artSet32InstanceFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2485,9 +2493,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL(artSet64InstanceFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2503,9 +2511,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpIputObject) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2533,9 +2541,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL(artSet8InstanceFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -2564,9 +2572,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL(artSet8InstanceFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -2595,9 +2603,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL(artSet16InstanceFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -2626,9 +2634,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL(artSet16InstanceFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -2652,7 +2660,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) # self call SYMBOL(artGet32StaticFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException .if 0 @@ -2685,7 +2693,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop jnz MterpException SET_VREG %eax, rINST # fp[A]<- low part SET_VREG_HIGH %edx, rINST # fp[A+1]<- high part - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2709,7 +2717,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) # self call SYMBOL(artGetObjStaticFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException .if 1 @@ -2741,7 +2749,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) # self call SYMBOL(artGetBooleanStaticFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException .if 0 @@ -2773,7 +2781,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) # self call SYMBOL(artGetByteStaticFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException .if 0 @@ -2805,7 +2813,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) # self call SYMBOL(artGetCharStaticFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException .if 0 @@ -2837,7 +2845,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %ecx, OUT_ARG2(%esp) # self call SYMBOL(artGetShortStaticFromCode) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException .if 0 @@ -2869,9 +2877,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artSet32StaticFromCode) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2894,9 +2902,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artSet64IndirectStaticFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2912,9 +2920,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpSputObject) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -2939,9 +2947,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artSet8StaticFromCode) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -2967,9 +2975,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artSet8StaticFromCode) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -2995,9 +3003,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artSet16StaticFromCode) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -3023,9 +3031,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artSet16StaticFromCode) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -3049,9 +3057,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 110 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeVirtual) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 /* @@ -3082,9 +3090,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 111 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeSuper) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 /* @@ -3115,9 +3123,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 112 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeDirect) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3141,9 +3149,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 113 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeStatic) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3168,9 +3176,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 114 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeInterface) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 /* @@ -3215,9 +3223,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 116 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeVirtualRange) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3241,9 +3249,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 117 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeSuperRange) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3267,9 +3275,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 118 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeDirectRange) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3293,9 +3301,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 119 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeStaticRange) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -3319,9 +3327,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 120 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeInterfaceRange) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -4047,10 +4055,10 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop je common_errDivideByZero movl %eax, %edx orl %ecx, %edx - test $0xFFFFFF00, %edx # If both arguments are less + testl $0xFFFFFF00, %edx # If both arguments are less # than 8-bit and +ve jz .Lop_div_int_8 # Do 8-bit divide - test $0xFFFF0000, %edx # If both arguments are less + testl $0xFFFF0000, %edx # If both arguments are less # than 16-bit and +ve jz .Lop_div_int_16 # Do 16-bit divide cmpl $-1, %ecx @@ -4101,10 +4109,10 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop je common_errDivideByZero movl %eax, %edx orl %ecx, %edx - test $0xFFFFFF00, %edx # If both arguments are less + testl $0xFFFFFF00, %edx # If both arguments are less # than 8-bit and +ve jz .Lop_rem_int_8 # Do 8-bit divide - test $0xFFFF0000, %edx # If both arguments are less + testl $0xFFFF0000, %edx # If both arguments are less # than 16-bit and +ve jz .Lop_rem_int_16 # Do 16-bit divide cmpl $-1, %ecx @@ -4785,9 +4793,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop sarl $4, rINST # rINST <- B GET_VREG %eax, rINST # eax <- vB andb $0xf, %cl # ecx <- A - mov rIBASE, LOCAL0(%esp) + movl rIBASE, rINST imull (rFP,%ecx,4), %eax # trashes rIBASE/edx - mov LOCAL0(%esp), rIBASE + movl rINST, rIBASE SET_VREG %eax, %ecx ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 @@ -5514,11 +5522,11 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movzbl rINSTbl, %eax # eax <- 000000BA sarl $4, %eax # eax <- B GET_VREG %eax, %eax # eax <- vB - movswl 2(rPC), %ecx # ecx <- ssssCCCC + movl rIBASE, %ecx + movswl 2(rPC), rIBASE # rIBASE <- ssssCCCC andb $0xf, rINSTbl # rINST <- A - mov rIBASE, LOCAL0(%esp) - imull %ecx, %eax # trashes rIBASE/edx - mov LOCAL0(%esp), rIBASE + imull rIBASE, %eax # trashes rIBASE/edx + movl %ecx, rIBASE SET_VREG %eax, rINST ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -5721,11 +5729,11 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop /* File: x86/op_mul_int_lit8.S */ /* mul/lit8 vAA, vBB, #+CC */ movzbl 2(rPC), %eax # eax <- BB - movsbl 3(rPC), %ecx # ecx <- ssssssCC + movl rIBASE, %ecx GET_VREG %eax, %eax # eax <- rBB - mov rIBASE, LOCAL0(%esp) - imull %ecx, %eax # trashes rIBASE/edx - mov LOCAL0(%esp), rIBASE + movsbl 3(rPC), rIBASE # rIBASE <- ssssssCC + imull rIBASE, %eax # trashes rIBASE/edx + movl %ecx, rIBASE SET_VREG %eax, rINST ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 @@ -5985,7 +5993,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop EXPORT_PC call SYMBOL(artIGetObjectFromMterp) # (obj, offset) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $0xf,rINSTbl # rINST <- A @@ -6037,9 +6045,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 232 movl rINST, OUT_ARG2(%esp) call SYMBOL(MterpIputObjectQuick) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 /* ------------------------------ */ @@ -6062,9 +6070,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 233 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeVirtualQuick) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -6088,9 +6096,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop REFRESH_INST 234 movl rINST, OUT_ARG3(%esp) call SYMBOL(MterpInvokeVirtualQuickRange) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 @@ -12912,7 +12920,7 @@ MterpException: lea OFF_FP_SHADOWFRAME(rFP), %ecx movl %ecx, OUT_ARG1(%esp) call SYMBOL(MterpHandleException) - testl %eax, %eax + testb %al, %al jz MterpExceptionReturn REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax diff --git a/runtime/interpreter/mterp/x86/bindiv.S b/runtime/interpreter/mterp/x86/bindiv.S index bb5b319c49..e87ba45546 100644 --- a/runtime/interpreter/mterp/x86/bindiv.S +++ b/runtime/interpreter/mterp/x86/bindiv.S @@ -13,10 +13,10 @@ je common_errDivideByZero movl %eax, %edx orl %ecx, %edx - test $$0xFFFFFF00, %edx # If both arguments are less + testl $$0xFFFFFF00, %edx # If both arguments are less # than 8-bit and +ve jz .L${opcode}_8 # Do 8-bit divide - test $$0xFFFF0000, %edx # If both arguments are less + testl $$0xFFFF0000, %edx # If both arguments are less # than 16-bit and +ve jz .L${opcode}_16 # Do 16-bit divide cmpl $$-1, %ecx diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index 385e78499f..a1532fa710 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/footer.S @@ -114,7 +114,7 @@ MterpException: lea OFF_FP_SHADOWFRAME(rFP), %ecx movl %ecx, OUT_ARG1(%esp) call SYMBOL(MterpHandleException) - testl %eax, %eax + testb %al, %al jz MterpExceptionReturn REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 0977b901e2..3fbbbf955e 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -182,13 +182,21 @@ unspecified registers or condition codes. /* * Refresh handler table. + */ +.macro REFRESH_IBASE + movl rSELF, rIBASE + movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE +.endm + +/* + * Refresh handler table. * IBase handles uses the caller save register so we must restore it after each call. * Also it is used as a result of some 64-bit operations (like imul) and we should * restore it in such cases also. * * TODO: Consider spilling the IBase instead of restoring it from Thread structure. */ -.macro REFRESH_IBASE +.macro RESTORE_IBASE movl rSELF, rIBASE movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE .endm @@ -196,7 +204,7 @@ unspecified registers or condition codes. /* * If rSELF is already loaded then we can use it from known reg. */ -.macro REFRESH_IBASE_FROM_SELF _reg +.macro RESTORE_IBASE_FROM_SELF _reg movl THREAD_CURRENT_IBASE_OFFSET(\_reg), rIBASE .endm diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S index 054fbfdf69..bbd88cf40b 100644 --- a/runtime/interpreter/mterp/x86/invoke.S +++ b/runtime/interpreter/mterp/x86/invoke.S @@ -14,7 +14,7 @@ REFRESH_INST ${opnum} movl rINST, OUT_ARG3(%esp) call SYMBOL($helper) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_aget_object.S b/runtime/interpreter/mterp/x86/op_aget_object.S index cbfb50cb09..35ec053854 100644 --- a/runtime/interpreter/mterp/x86/op_aget_object.S +++ b/runtime/interpreter/mterp/x86/op_aget_object.S @@ -13,7 +13,7 @@ movl %ecx, OUT_ARG1(%esp) call SYMBOL(artAGetObjectFromMterp) # (array, index) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException SET_VREG_OBJECT %eax, rINST diff --git a/runtime/interpreter/mterp/x86/op_aput_object.S b/runtime/interpreter/mterp/x86/op_aput_object.S index 9cfc2213d2..980b26a401 100644 --- a/runtime/interpreter/mterp/x86/op_aput_object.S +++ b/runtime/interpreter/mterp/x86/op_aput_object.S @@ -9,7 +9,7 @@ REFRESH_INST ${opnum} movl rINST, OUT_ARG2(%esp) call SYMBOL(MterpAputObject) # (array, index) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_check_cast.S b/runtime/interpreter/mterp/x86/op_check_cast.S index ae2ff9ea21..d090aa3785 100644 --- a/runtime/interpreter/mterp/x86/op_check_cast.S +++ b/runtime/interpreter/mterp/x86/op_check_cast.S @@ -12,7 +12,7 @@ movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpCheckCast) # (index, &obj, method, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_class.S b/runtime/interpreter/mterp/x86/op_const_class.S index 343e110f71..60be789214 100644 --- a/runtime/interpreter/mterp/x86/op_const_class.S +++ b/runtime/interpreter/mterp/x86/op_const_class.S @@ -8,7 +8,7 @@ movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_string.S b/runtime/interpreter/mterp/x86/op_const_string.S index bbac69ced4..ff93b232d6 100644 --- a/runtime/interpreter/mterp/x86/op_const_string.S +++ b/runtime/interpreter/mterp/x86/op_const_string.S @@ -8,7 +8,7 @@ movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S index 4236807dd3..e7f952a306 100644 --- a/runtime/interpreter/mterp/x86/op_const_string_jumbo.S +++ b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S @@ -8,7 +8,7 @@ movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_fill_array_data.S b/runtime/interpreter/mterp/x86/op_fill_array_data.S index 004aed9872..5855284901 100644 --- a/runtime/interpreter/mterp/x86/op_fill_array_data.S +++ b/runtime/interpreter/mterp/x86/op_fill_array_data.S @@ -7,6 +7,6 @@ movl %ecx, OUT_ARG1(%esp) call SYMBOL(MterpFillArrayData) # (obj, payload) REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_filled_new_array.S b/runtime/interpreter/mterp/x86/op_filled_new_array.S index a2bac29bc1..35b2fe8dfc 100644 --- a/runtime/interpreter/mterp/x86/op_filled_new_array.S +++ b/runtime/interpreter/mterp/x86/op_filled_new_array.S @@ -15,6 +15,6 @@ movl %ecx, OUT_ARG2(%esp) call SYMBOL($helper) REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_iget.S b/runtime/interpreter/mterp/x86/op_iget.S index 99326105c0..e3304ba6a7 100644 --- a/runtime/interpreter/mterp/x86/op_iget.S +++ b/runtime/interpreter/mterp/x86/op_iget.S @@ -17,7 +17,7 @@ movl %ecx, OUT_ARG3(%esp) # self call SYMBOL($helper) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $$0xf, rINSTbl # rINST <- A diff --git a/runtime/interpreter/mterp/x86/op_iget_object_quick.S b/runtime/interpreter/mterp/x86/op_iget_object_quick.S index fe166948c9..b1551a0179 100644 --- a/runtime/interpreter/mterp/x86/op_iget_object_quick.S +++ b/runtime/interpreter/mterp/x86/op_iget_object_quick.S @@ -9,7 +9,7 @@ EXPORT_PC call SYMBOL(artIGetObjectFromMterp) # (obj, offset) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException # bail out andb $$0xf,rINSTbl # rINST <- A diff --git a/runtime/interpreter/mterp/x86/op_iget_wide.S b/runtime/interpreter/mterp/x86/op_iget_wide.S index 92126b4473..a5d7e6937d 100644 --- a/runtime/interpreter/mterp/x86/op_iget_wide.S +++ b/runtime/interpreter/mterp/x86/op_iget_wide.S @@ -21,5 +21,5 @@ andb $$0xf, rINSTbl # rINST <- A SET_VREG %eax, rINST SET_VREG_HIGH %edx, rINST - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_instance_of.S b/runtime/interpreter/mterp/x86/op_instance_of.S index fd5bf44c78..e6fe5b2cec 100644 --- a/runtime/interpreter/mterp/x86/op_instance_of.S +++ b/runtime/interpreter/mterp/x86/op_instance_of.S @@ -18,7 +18,7 @@ movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException andb $$0xf, rINSTbl # rINSTbl <- A diff --git a/runtime/interpreter/mterp/x86/op_iput.S b/runtime/interpreter/mterp/x86/op_iput.S index 13cfe5ca69..c847e2dc88 100644 --- a/runtime/interpreter/mterp/x86/op_iput.S +++ b/runtime/interpreter/mterp/x86/op_iput.S @@ -19,7 +19,7 @@ movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL($handler) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_object.S b/runtime/interpreter/mterp/x86/op_iput_object.S index f63075c503..e0136970b0 100644 --- a/runtime/interpreter/mterp/x86/op_iput_object.S +++ b/runtime/interpreter/mterp/x86/op_iput_object.S @@ -7,7 +7,7 @@ movl rSELF, %eax movl %eax, OUT_ARG3(%esp) call SYMBOL(MterpIputObject) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_object_quick.S b/runtime/interpreter/mterp/x86/op_iput_object_quick.S index d54b1b772f..cb779295b7 100644 --- a/runtime/interpreter/mterp/x86/op_iput_object_quick.S +++ b/runtime/interpreter/mterp/x86/op_iput_object_quick.S @@ -5,7 +5,7 @@ REFRESH_INST ${opnum} movl rINST, OUT_ARG2(%esp) call SYMBOL(MterpIputObjectQuick) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_wide.S b/runtime/interpreter/mterp/x86/op_iput_wide.S index 573e14d663..122eecf43f 100644 --- a/runtime/interpreter/mterp/x86/op_iput_wide.S +++ b/runtime/interpreter/mterp/x86/op_iput_wide.S @@ -13,7 +13,7 @@ movl OFF_FP_METHOD(rFP), %eax movl %eax, OUT_ARG3(%esp) # referrer call SYMBOL(artSet64InstanceFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpPossibleException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_monitor_enter.S b/runtime/interpreter/mterp/x86/op_monitor_enter.S index 9e885bde93..b35c68488a 100644 --- a/runtime/interpreter/mterp/x86/op_monitor_enter.S +++ b/runtime/interpreter/mterp/x86/op_monitor_enter.S @@ -8,7 +8,7 @@ movl rSELF, %eax movl %eax, OUT_ARG1(%esp) call SYMBOL(artLockObjectFromCode) # (object, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpException ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_monitor_exit.S b/runtime/interpreter/mterp/x86/op_monitor_exit.S index 090480056a..2d17d5e7c5 100644 --- a/runtime/interpreter/mterp/x86/op_monitor_exit.S +++ b/runtime/interpreter/mterp/x86/op_monitor_exit.S @@ -12,7 +12,7 @@ movl rSELF, %eax movl %eax, OUT_ARG1(%esp) call SYMBOL(artUnlockObjectFromCode) # (object, self) - REFRESH_IBASE - testl %eax, %eax + RESTORE_IBASE + testb %al, %al jnz MterpException ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S index f92a28e46a..da699ae19b 100644 --- a/runtime/interpreter/mterp/x86/op_mul_int_2addr.S +++ b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S @@ -3,8 +3,8 @@ sarl $$4, rINST # rINST <- B GET_VREG %eax, rINST # eax <- vB andb $$0xf, %cl # ecx <- A - mov rIBASE, LOCAL0(%esp) + movl rIBASE, rINST imull (rFP,%ecx,4), %eax # trashes rIBASE/edx - mov LOCAL0(%esp), rIBASE + movl rINST, rIBASE SET_VREG %eax, %ecx ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S index 31ab613805..056f491bef 100644 --- a/runtime/interpreter/mterp/x86/op_mul_int_lit16.S +++ b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S @@ -3,10 +3,10 @@ movzbl rINSTbl, %eax # eax <- 000000BA sarl $$4, %eax # eax <- B GET_VREG %eax, %eax # eax <- vB - movswl 2(rPC), %ecx # ecx <- ssssCCCC + movl rIBASE, %ecx + movswl 2(rPC), rIBASE # rIBASE <- ssssCCCC andb $$0xf, rINSTbl # rINST <- A - mov rIBASE, LOCAL0(%esp) - imull %ecx, %eax # trashes rIBASE/edx - mov LOCAL0(%esp), rIBASE + imull rIBASE, %eax # trashes rIBASE/edx + movl %ecx, rIBASE SET_VREG %eax, rINST ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S index 6637aa7384..59b384426c 100644 --- a/runtime/interpreter/mterp/x86/op_mul_int_lit8.S +++ b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S @@ -1,9 +1,9 @@ /* mul/lit8 vAA, vBB, #+CC */ movzbl 2(rPC), %eax # eax <- BB - movsbl 3(rPC), %ecx # ecx <- ssssssCC + movl rIBASE, %ecx GET_VREG %eax, %eax # eax <- rBB - mov rIBASE, LOCAL0(%esp) - imull %ecx, %eax # trashes rIBASE/edx - mov LOCAL0(%esp), rIBASE + movsbl 3(rPC), rIBASE # rIBASE <- ssssssCC + imull rIBASE, %eax # trashes rIBASE/edx + movl %ecx, rIBASE SET_VREG %eax, rINST ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_new_array.S b/runtime/interpreter/mterp/x86/op_new_array.S index 24904774e1..16226e989c 100644 --- a/runtime/interpreter/mterp/x86/op_new_array.S +++ b/runtime/interpreter/mterp/x86/op_new_array.S @@ -15,7 +15,7 @@ movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpNewArray) - REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + RESTORE_IBASE + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_new_instance.S b/runtime/interpreter/mterp/x86/op_new_instance.S index 712a5ebe96..f976accb1e 100644 --- a/runtime/interpreter/mterp/x86/op_new_instance.S +++ b/runtime/interpreter/mterp/x86/op_new_instance.S @@ -10,7 +10,7 @@ REFRESH_INST ${opnum} movl rINST, OUT_ARG2(%esp) call SYMBOL(MterpNewInstance) - REFRESH_IBASE - testl %eax, %eax # 0 means an exception is thrown + RESTORE_IBASE + testb %al, %al # 0 means an exception is thrown jz MterpPossibleException ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sget.S b/runtime/interpreter/mterp/x86/op_sget.S index ec964581af..0e9a3d82da 100644 --- a/runtime/interpreter/mterp/x86/op_sget.S +++ b/runtime/interpreter/mterp/x86/op_sget.S @@ -15,7 +15,7 @@ movl %ecx, OUT_ARG2(%esp) # self call SYMBOL($helper) movl rSELF, %ecx - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) jnz MterpException .if $is_object diff --git a/runtime/interpreter/mterp/x86/op_sget_wide.S b/runtime/interpreter/mterp/x86/op_sget_wide.S index 833f266dd5..2b603034c6 100644 --- a/runtime/interpreter/mterp/x86/op_sget_wide.S +++ b/runtime/interpreter/mterp/x86/op_sget_wide.S @@ -17,5 +17,5 @@ jnz MterpException SET_VREG %eax, rINST # fp[A]<- low part SET_VREG_HIGH %edx, rINST # fp[A+1]<- high part - REFRESH_IBASE_FROM_SELF %ecx + RESTORE_IBASE_FROM_SELF %ecx ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sput.S b/runtime/interpreter/mterp/x86/op_sput.S index a199281088..0b5de0953d 100644 --- a/runtime/interpreter/mterp/x86/op_sput.S +++ b/runtime/interpreter/mterp/x86/op_sput.S @@ -16,7 +16,7 @@ movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL($helper) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sput_object.S b/runtime/interpreter/mterp/x86/op_sput_object.S index e3e57fc87b..0db517723b 100644 --- a/runtime/interpreter/mterp/x86/op_sput_object.S +++ b/runtime/interpreter/mterp/x86/op_sput_object.S @@ -7,7 +7,7 @@ movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) call SYMBOL(MterpSputObject) - testl %eax, %eax + testb %al, %al jz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sput_wide.S b/runtime/interpreter/mterp/x86/op_sput_wide.S index 7544838d52..19cff0db5a 100644 --- a/runtime/interpreter/mterp/x86/op_sput_wide.S +++ b/runtime/interpreter/mterp/x86/op_sput_wide.S @@ -14,7 +14,7 @@ movl rSELF, %ecx movl %ecx, OUT_ARG3(%esp) # self call SYMBOL(artSet64IndirectStaticFromMterp) - testl %eax, %eax + testb %al, %al jnz MterpException - REFRESH_IBASE + RESTORE_IBASE ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 60ad0cbb10..0e175b85eb 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -261,6 +261,16 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField( } } +void UnstartedRuntime::UnstartedClassGetEnclosingClass( + Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { + StackHandleScope<1> hs(self); + Handle<mirror::Class> klass(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsClass())); + if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) { + result->SetL(nullptr); + } + result->SetL(klass->GetDexFile().GetEnclosingClass(klass)); +} + void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass( Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString(); diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h index 047e906614..6d4d711645 100644 --- a/runtime/interpreter/unstarted_runtime_list.h +++ b/runtime/interpreter/unstarted_runtime_list.h @@ -24,6 +24,7 @@ V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \ V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \ V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \ + V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \ V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \ V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \ V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \ diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 80c174c100..bdc7ee2428 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -113,8 +113,7 @@ bool Jit::LoadCompiler(std::string* error_msg) { *error_msg = oss.str(); return false; } - jit_load_ = reinterpret_cast<void* (*)(CompilerCallbacks**, bool*)>( - dlsym(jit_library_handle_, "jit_load")); + jit_load_ = reinterpret_cast<void* (*)(bool*)>(dlsym(jit_library_handle_, "jit_load")); if (jit_load_ == nullptr) { dlclose(jit_library_handle_); *error_msg = "JIT couldn't find jit_load entry point"; @@ -141,23 +140,15 @@ bool Jit::LoadCompiler(std::string* error_msg) { *error_msg = "JIT couldn't find jit_types_loaded entry point"; return false; } - CompilerCallbacks* callbacks = nullptr; bool will_generate_debug_symbols = false; VLOG(jit) << "Calling JitLoad interpreter_only=" << Runtime::Current()->GetInstrumentation()->InterpretOnly(); - jit_compiler_handle_ = (jit_load_)(&callbacks, &will_generate_debug_symbols); + jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols); if (jit_compiler_handle_ == nullptr) { dlclose(jit_library_handle_); *error_msg = "JIT couldn't load compiler"; return false; } - if (callbacks == nullptr) { - dlclose(jit_library_handle_); - *error_msg = "JIT compiler callbacks were not set"; - jit_compiler_handle_ = nullptr; - return false; - } - compiler_callbacks_ = callbacks; generate_debug_info_ = will_generate_debug_symbols; return true; } @@ -290,7 +281,15 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, } if (kRuntimeISA == kMips || kRuntimeISA == kMips64) { - VLOG(jit) << "OSR not supported on this platform"; + VLOG(jit) << "OSR not supported on this platform: " << kRuntimeISA; + return false; + } + + if (UNLIKELY(__builtin_frame_address(0) < thread->GetStackEnd())) { + // Don't attempt to do an OSR if we are close to the stack limit. Since + // the interpreter frames are still on stack, OSR has the potential + // to stack overflow even for a simple loop. + // b/27094810. return false; } @@ -304,8 +303,9 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, return false; } - // Fetch some data before looking up for an OSR method, as we don't want thread - // suspension once we hold an OSR method. + // Fetch some data before looking up for an OSR method. We don't want thread + // suspension once we hold an OSR method, as the JIT code cache could delete the OSR + // method while we are being suspended. const size_t number_of_vregs = method->GetCodeItem()->registers_size_; const char* shorty = method->GetShorty(); std::string method_name(VLOG_IS_ON(jit) ? PrettyMethod(method) : ""); @@ -360,7 +360,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, DexRegisterLocation::Kind location = vreg_map.GetLocationKind(vreg, number_of_vregs, code_info, encoding); if (location == DexRegisterLocation::Kind::kNone) { - // Dex register is dead or unitialized. + // Dex register is dead or uninitialized. continue; } @@ -369,7 +369,8 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, continue; } - DCHECK(location == DexRegisterLocation::Kind::kInStack); + DCHECK(location == DexRegisterLocation::Kind::kInStack) + << DexRegisterLocation::PrettyDescriptor(location); int32_t vreg_value = shadow_frame->GetVReg(vreg); int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 042da92b3b..109ca3dbd1 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -32,7 +32,6 @@ namespace art { class ArtMethod; -class CompilerCallbacks; struct RuntimeArgumentMap; namespace jit { @@ -55,9 +54,6 @@ class Jit { size_t warmup_threshold, size_t osr_threshold); void CreateThreadPool(); - CompilerCallbacks* GetCompilerCallbacks() { - return compiler_callbacks_; - } const JitCodeCache* GetCodeCache() const { return code_cache_.get(); } @@ -108,7 +104,7 @@ class Jit { // JIT compiler void* jit_library_handle_; void* jit_compiler_handle_; - void* (*jit_load_)(CompilerCallbacks**, bool*); + void* (*jit_load_)(bool*); void (*jit_unload_)(void*); bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool); void (*jit_types_loaded_)(void*, mirror::Class**, size_t count); @@ -119,7 +115,6 @@ class Jit { std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_; std::unique_ptr<jit::JitCodeCache> code_cache_; - CompilerCallbacks* compiler_callbacks_; // Owned by the jit compiler. bool save_profiling_info_; bool generate_debug_info_; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 9111ddf9f5..d5a9d66210 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -232,25 +232,20 @@ static uintptr_t FromCodeToAllocation(const void* code) { void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) { uintptr_t allocation = FromCodeToAllocation(code_ptr); const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - const uint8_t* data = method_header->GetNativeGcMap(); // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr)); - if (data != nullptr) { - mspace_free(data_mspace_, const_cast<uint8_t*>(data)); - } - data = method_header->GetMappingTable(); - if (data != nullptr) { - mspace_free(data_mspace_, const_cast<uint8_t*>(data)); - } + + FreeData(const_cast<uint8_t*>(method_header->GetNativeGcMap())); + FreeData(const_cast<uint8_t*>(method_header->GetMappingTable())); // Use the offset directly to prevent sanity check that the method is // compiled with optimizing. // TODO(ngeoffray): Clean up. if (method_header->vmap_table_offset_ != 0) { - data = method_header->code_ - method_header->vmap_table_offset_; - mspace_free(data_mspace_, const_cast<uint8_t*>(data)); + const uint8_t* data = method_header->code_ - method_header->vmap_table_offset_; + FreeData(const_cast<uint8_t*>(data)); } - mspace_free(code_mspace_, reinterpret_cast<uint8_t*>(allocation)); + FreeCode(reinterpret_cast<uint8_t*>(allocation)); } void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { @@ -269,11 +264,19 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { } } } + for (auto it = osr_code_map_.begin(); it != osr_code_map_.end();) { + if (alloc.ContainsUnsafe(it->first)) { + // Note that the code has already been removed in the loop above. + it = osr_code_map_.erase(it); + } else { + ++it; + } + } for (auto it = profiling_infos_.begin(); it != profiling_infos_.end();) { ProfilingInfo* info = *it; if (alloc.ContainsUnsafe(info->GetMethod())) { info->GetMethod()->SetProfilingInfo(nullptr); - mspace_free(data_mspace_, reinterpret_cast<uint8_t*>(info)); + FreeData(reinterpret_cast<uint8_t*>(info)); it = profiling_infos_.erase(it); } else { ++it; @@ -299,19 +302,18 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, OatQuickMethodHeader* method_header = nullptr; uint8_t* code_ptr = nullptr; + uint8_t* memory = nullptr; { ScopedThreadSuspension sts(self, kSuspended); MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); { ScopedCodeCacheWrite scc(code_map_.get()); - uint8_t* result = reinterpret_cast<uint8_t*>( - mspace_memalign(code_mspace_, alignment, total_size)); - if (result == nullptr) { + memory = AllocateCode(total_size); + if (memory == nullptr) { return nullptr; } - code_ptr = result + header_size; - DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(code_ptr), alignment); + code_ptr = memory + header_size; std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); @@ -368,9 +370,7 @@ size_t JitCodeCache::CodeCacheSize() { } size_t JitCodeCache::CodeCacheSizeLocked() { - size_t bytes_allocated = 0; - mspace_inspect_all(code_mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; + return used_memory_for_code_; } size_t JitCodeCache::DataCacheSize() { @@ -379,9 +379,7 @@ size_t JitCodeCache::DataCacheSize() { } size_t JitCodeCache::DataCacheSizeLocked() { - size_t bytes_allocated = 0; - mspace_inspect_all(data_mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; + return used_memory_for_data_; } size_t JitCodeCache::NumberOfCompiledCode() { @@ -391,7 +389,7 @@ size_t JitCodeCache::NumberOfCompiledCode() { void JitCodeCache::ClearData(Thread* self, void* data) { MutexLock mu(self, lock_); - mspace_free(data_mspace_, data); + FreeData(reinterpret_cast<uint8_t*>(data)); } uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) { @@ -402,7 +400,7 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) { ScopedThreadSuspension sts(self, kSuspended); MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); - result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size)); + result = AllocateData(size); } if (result == nullptr) { @@ -411,7 +409,7 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) { ScopedThreadSuspension sts(self, kSuspended); MutexLock mu(self, lock_); WaitForPotentialCollectionToComplete(self); - result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size)); + result = AllocateData(size); } return result; @@ -544,7 +542,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { // we hold the lock. { MutexLock mu(self, lock_); - if (!garbage_collect_code_) { + if (!garbage_collect_code_ || current_capacity_ < kReservedCapacity) { IncreaseCodeCacheCapacity(); NotifyCollectionDone(self); return; @@ -620,12 +618,11 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - void* data_mspace = data_mspace_; // Free all profiling infos of methods that were not being compiled. auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(), - [data_mspace] (ProfilingInfo* info) { + [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS { if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { - mspace_free(data_mspace, reinterpret_cast<uint8_t*>(info)); + FreeData(reinterpret_cast<uint8_t*>(info)); return true; } return false; @@ -710,7 +707,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self, return info; } - uint8_t* data = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, profile_info_size)); + uint8_t* data = AllocateData(profile_info_size); if (data == nullptr) { return nullptr; } @@ -782,5 +779,51 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { return mspace_usable_size(reinterpret_cast<const void*>(FromCodeToAllocation(ptr))); } +void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, + const OatQuickMethodHeader* header) { + if (method->GetEntryPointFromQuickCompiledCode() == header->GetEntryPoint()) { + // The entrypoint is the one to invalidate, so we just update + // it to the interpreter entry point and clear the counter to get the method + // Jitted again. + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, GetQuickToInterpreterBridge()); + method->ClearCounter(); + } else { + MutexLock mu(Thread::Current(), lock_); + auto it = osr_code_map_.find(method); + if (it != osr_code_map_.end() && OatQuickMethodHeader::FromCodePointer(it->second) == header) { + // Remove the OSR method, to avoid using it again. + osr_code_map_.erase(it); + } + } +} + +uint8_t* JitCodeCache::AllocateCode(size_t code_size) { + size_t alignment = GetInstructionSetAlignment(kRuntimeISA); + uint8_t* result = reinterpret_cast<uint8_t*>( + mspace_memalign(code_mspace_, alignment, code_size)); + size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); + // Ensure the header ends up at expected instruction alignment. + DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(result + header_size), alignment); + used_memory_for_code_ += mspace_usable_size(result); + return result; +} + +void JitCodeCache::FreeCode(uint8_t* code) { + used_memory_for_code_ -= mspace_usable_size(code); + mspace_free(code_mspace_, code); +} + +uint8_t* JitCodeCache::AllocateData(size_t data_size) { + void* result = mspace_malloc(data_mspace_, data_size); + used_memory_for_data_ += mspace_usable_size(result); + return reinterpret_cast<uint8_t*>(result); +} + +void JitCodeCache::FreeData(uint8_t* data) { + used_memory_for_data_ -= mspace_usable_size(data); + mspace_free(data_mspace_, data); +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 048f8d064e..74ce7b57fd 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -49,7 +49,10 @@ class JitCodeCache { static constexpr size_t kMaxCapacity = 64 * MB; // Put the default to a very low amount for debug builds to stress the code cache // collection. - static constexpr size_t kInitialCapacity = kIsDebugBuild ? 16 * KB : 64 * KB; + static constexpr size_t kInitialCapacity = kIsDebugBuild ? 8 * KB : 64 * KB; + + // By default, do not GC until reaching 256KB. + static constexpr size_t kReservedCapacity = kInitialCapacity * 4; // Create the code cache with a code + data capacity equal to "capacity", error message is passed // in the out arg error_msg. @@ -172,6 +175,10 @@ class JitCodeCache { size_t GetMemorySizeOfCodePointer(const void* ptr) REQUIRES(!lock_); + void InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* code) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, @@ -272,6 +279,17 @@ class JitCodeCache { // Whether we can do garbage collection. const bool garbage_collect_code_; + // The size in bytes of used memory for the data portion of the code cache. + size_t used_memory_for_data_ GUARDED_BY(lock_); + + // The size in bytes of used memory for the code portion of the code cache. + size_t used_memory_for_code_ GUARDED_BY(lock_); + + void FreeCode(uint8_t* code) REQUIRES(lock_); + uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_); + void FreeData(uint8_t* data) REQUIRES(lock_); + uint8_t* AllocateData(size_t data_size) REQUIRES(lock_); + // Number of compilations done throughout the lifetime of the JIT. size_t number_of_compilations_ GUARDED_BY(lock_); diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index 0aff1f7ec3..747b112f57 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -125,8 +125,8 @@ static constexpr const char kLineSeparator = '\n'; * app.apk,131232145,11,23,454,54 * app.apk:classes5.dex,218490184,39,13,49,1 **/ -bool ProfileCompilationInfo::Save(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Save(int fd) { + DCHECK_GE(fd, 0); // TODO(calin): Profile this and see how much memory it takes. If too much, // write to file directly. std::ostringstream os; @@ -232,8 +232,8 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; } -bool ProfileCompilationInfo::Load(uint32_t fd) { - DCHECK_GE(fd, 0u); +bool ProfileCompilationInfo::Load(int fd) { + DCHECK_GE(fd, 0); std::string current_line; const int kBufferSize = 1024; @@ -343,7 +343,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* return os.str(); } -bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) { +bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { return info_.Equals(other.info_); } diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index c388c4a42f..edc591c2eb 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -46,11 +46,11 @@ class ProfileCompilationInfo { const std::vector<ArtMethod*>& methods); // Loads profile information from the given file descriptor. - bool Load(uint32_t fd); + bool Load(int fd); // Loads the data from another ProfileCompilationInfo object. bool Load(const ProfileCompilationInfo& info); // Saves the profile data to the given file descriptor. - bool Save(uint32_t fd); + bool Save(int fd); // Returns the number of methods that were profiled. uint32_t GetNumberOfMethods() const; @@ -65,8 +65,7 @@ class ProfileCompilationInfo { bool print_full_dex_location = true) const; // For testing purposes. - bool Equals(ProfileCompilationInfo& other); - // Exposed for testing purpose. + bool Equals(const ProfileCompilationInfo& other); static std::string GetProfileDexFileKey(const std::string& dex_location); private: diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 422832e03c..19584edf7f 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -502,7 +502,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho if (method->IsDirect()) { return method; } - if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) { + if (method->GetDeclaringClass()->IsInterface() && !method->IsCopied()) { return FindVirtualMethodForInterface(method, pointer_size); } return FindVirtualMethodForVirtual(method, pointer_size); @@ -532,8 +532,9 @@ inline LengthPrefixedArray<ArtField>* Class::GetIFieldsPtr() { return GetFieldPtr<LengthPrefixedArray<ArtField>*>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_)); } +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline MemberOffset Class::GetFirstReferenceInstanceFieldOffset() { - Class* super_class = GetSuperClass(); + Class* super_class = GetSuperClass<kVerifyFlags, kReadBarrierOption>(); return (super_class != nullptr) ? MemberOffset(RoundUp(super_class->GetObjectSize(), sizeof(mirror::HeapReference<mirror::Object>))) diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index cdc6204665..9190e44144 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -1048,5 +1048,11 @@ uint32_t Class::Depth() { return depth; } +uint32_t Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) { + std::string temp; + const DexFile::TypeId* type_id = dex_file.FindTypeId(GetDescriptor(&temp)); + return (type_id == nullptr) ? DexFile::kDexNoIndex : dex_file.GetIndexForTypeId(*type_id); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 388a231cdc..6e3463c25c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1006,6 +1006,8 @@ class MANAGED Class FINAL : public Object { SHARED_REQUIRES(Locks::mutator_lock_); // Get the offset of the first reference instance field. Other reference instance fields follow. + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> MemberOffset GetFirstReferenceInstanceFieldOffset() SHARED_REQUIRES(Locks::mutator_lock_); @@ -1119,6 +1121,9 @@ class MANAGED Class FINAL : public Object { SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx); } + uint32_t FindTypeIndexInOtherDexFile(const DexFile& dex_file) + SHARED_REQUIRES(Locks::mutator_lock_); + static Class* GetJavaLangClass() SHARED_REQUIRES(Locks::mutator_lock_) { DCHECK(HasJavaLangClass()); return java_lang_Class_.Read(); diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index eb391be406..76a36ac893 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -1068,7 +1068,7 @@ inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& v MemberOffset field_offset = kIsStatic ? klass->GetFirstReferenceStaticFieldOffset<kVerifyFlags, kReadBarrierOption>( Runtime::Current()->GetClassLinker()->GetImagePointerSize()) - : klass->GetFirstReferenceInstanceFieldOffset(); + : klass->GetFirstReferenceInstanceFieldOffset<kVerifyFlags, kReadBarrierOption>(); for (size_t i = 0u; i < num_reference_fields; ++i) { // TODO: Do a simpler check? if (field_offset.Uint32Value() != ClassOffset().Uint32Value()) { diff --git a/runtime/modifiers.h b/runtime/modifiers.h index ed4c5fc76c..c31b22ee89 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -50,6 +50,11 @@ static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (de // Used by a class to denote that the verifier has attempted to check it at least once. static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime) static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only) +// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent +// that it was copied from its declaring class into another class. All methods marked kAccMiranda +// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ +// array of a concrete class will also have this bit set. +static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime) static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only) static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime) // This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know diff --git a/runtime/oat_file.h b/runtime/oat_file.h index bcc2d33333..910163c787 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -40,6 +40,12 @@ class OatMethodOffsets; class OatHeader; class OatDexFile; +namespace gc { +namespace collector { +class DummyOatFile; +} // namespace collector +} // namespace gc + class OatFile { public: typedef art::OatDexFile OatDexFile; @@ -312,6 +318,7 @@ class OatFile { // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't. mutable std::list<std::string> string_cache_ GUARDED_BY(secondary_lookup_lock_); + friend class gc::collector::DummyOatFile; // For modifying begin_ and end_. friend class OatClass; friend class art::OatDexFile; friend class OatDumper; // For GetBase and GetLimit diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index f9d916a92e..d64aa432fc 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -394,6 +394,7 @@ bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options, // Intended for local changes only. static void MaybeOverrideVerbosity() { // gLogVerbosity.class_linker = true; // TODO: don't check this in! + // gLogVerbosity.collector = true; // TODO: don't check this in! // gLogVerbosity.compiler = true; // TODO: don't check this in! // gLogVerbosity.deopt = true; // TODO: don't check this in! // gLogVerbosity.gc = true; // TODO: don't check this in! diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 786cf06e2d..2dfa860dcb 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -23,6 +23,8 @@ #include "entrypoints/quick/quick_entrypoints_enum.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "handle_scope-inl.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/throwable.h" @@ -288,13 +290,18 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { stacked_shadow_frame_pushed_(false), single_frame_deopt_(single_frame), single_frame_done_(false), - single_frame_deopt_method_(nullptr) { + single_frame_deopt_method_(nullptr), + single_frame_deopt_quick_method_header_(nullptr) { } ArtMethod* GetSingleFrameDeoptMethod() const { return single_frame_deopt_method_; } + const OatQuickMethodHeader* GetSingleFrameDeoptQuickMethodHeader() const { + return single_frame_deopt_quick_method_header_; + } + bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { exception_handler_->SetHandlerFrameDepth(GetFrameDepth()); ArtMethod* method = GetMethod(); @@ -366,6 +373,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { exception_handler_->SetHandlerQuickArg0(reinterpret_cast<uintptr_t>(method)); single_frame_done_ = true; single_frame_deopt_method_ = method; + single_frame_deopt_quick_method_header_ = GetCurrentOatQuickMethodHeader(); } return true; } @@ -601,6 +609,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const bool single_frame_deopt_; bool single_frame_done_; ArtMethod* single_frame_deopt_method_; + const OatQuickMethodHeader* single_frame_deopt_quick_method_header_; DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor); }; @@ -629,13 +638,17 @@ void QuickExceptionHandler::DeoptimizeSingleFrame() { DeoptimizeStackVisitor visitor(self_, context_, this, true); visitor.WalkStack(true); - // Compiled code made an explicit deoptimization. Transfer the code - // to interpreter and clear the counter to JIT the method again. + // Compiled code made an explicit deoptimization. ArtMethod* deopt_method = visitor.GetSingleFrameDeoptMethod(); DCHECK(deopt_method != nullptr); - deopt_method->ClearCounter(); - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - deopt_method, GetQuickToInterpreterBridge()); + if (Runtime::Current()->UseJit()) { + Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor( + deopt_method, visitor.GetSingleFrameDeoptQuickMethodHeader()); + } else { + // Transfer the code to interpreter. + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + deopt_method, GetQuickToInterpreterBridge()); + } // PC needs to be of the quick-to-interpreter bridge. int32_t offset; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 2aeb7921ce..eb5455a4cd 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1300,6 +1300,10 @@ void Runtime::InitNativeMethods() { VLOG(startup) << "Runtime::InitNativeMethods exiting"; } +void Runtime::ReclaimArenaPoolMemory() { + arena_pool_->LockReclaimMemory(); +} + void Runtime::InitThreadGroups(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); ScopedJniEnvLocalRefState env_state(env); @@ -1887,7 +1891,6 @@ void Runtime::CreateJit() { std::string error_msg; jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg)); if (jit_.get() != nullptr) { - compiler_callbacks_ = jit_->GetCompilerCallbacks(); jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(), jit_options_->GetWarmupThreshold(), jit_options_->GetOsrThreshold()); diff --git a/runtime/runtime.h b/runtime/runtime.h index 1956bae52a..8aac4ce9b4 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -94,8 +94,6 @@ struct TraceConfig; class Transaction; typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions; -typedef SafeMap<MethodReference, SafeMap<uint32_t, std::set<uint32_t>>, - MethodReferenceComparator> MethodRefToStringInitRegMap; // Not all combinations of flags are valid. You may not visit all roots as well as the new roots // (no logical reason to do this). You also may not start logging new roots and stop logging new @@ -566,6 +564,9 @@ class Runtime { const ArenaPool* GetArenaPool() const { return arena_pool_.get(); } + + void ReclaimArenaPoolMemory(); + LinearAlloc* GetLinearAlloc() { return linear_alloc_.get(); } @@ -574,10 +575,6 @@ class Runtime { return jit_options_.get(); } - MethodRefToStringInitRegMap& GetStringInitMap() { - return method_ref_string_init_reg_map_; - } - bool IsDebuggable() const; // Returns the build fingerprint, if set. Otherwise an empty string is returned. @@ -803,8 +800,6 @@ class Runtime { // Experimental opcodes should not be used by other production code. ExperimentalFlags experimental_flags_; - MethodRefToStringInitRegMap method_ref_string_init_reg_map_; - // Contains the build fingerprint, if given as a parameter. std::string fingerprint_; diff --git a/runtime/thread.h b/runtime/thread.h index 2726e91130..97c47e1490 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -852,6 +852,22 @@ class Thread { tls32_.weak_ref_access_enabled = enabled; } + uint32_t GetDisableThreadFlipCount() const { + CHECK(kUseReadBarrier); + return tls32_.disable_thread_flip_count; + } + + void IncrementDisableThreadFlipCount() { + CHECK(kUseReadBarrier); + ++tls32_.disable_thread_flip_count; + } + + void DecrementDisableThreadFlipCount() { + CHECK(kUseReadBarrier); + DCHECK_GT(tls32_.disable_thread_flip_count, 0U); + --tls32_.disable_thread_flip_count; + } + // Activates single step control for debugging. The thread takes the // ownership of the given SingleStepControl*. It is deleted by a call // to DeactivateSingleStepControl or upon thread destruction. @@ -1214,7 +1230,8 @@ class Thread { daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0), thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false), ready_for_debug_invoke(false), - debug_method_entry_(false), is_gc_marking(false), weak_ref_access_enabled(true) { + debug_method_entry_(false), is_gc_marking(false), weak_ref_access_enabled(true), + disable_thread_flip_count(0) { } union StateAndFlags state_and_flags; @@ -1281,6 +1298,11 @@ class Thread { // pause, this is not an issue.) Other collectors use Runtime::DisallowNewSystemWeaks() and // ReferenceProcessor::EnableSlowPath(). bool32_t weak_ref_access_enabled; + + // A thread local version of Heap::disable_thread_flip_count_. This keeps track of how many + // levels of (nested) JNI critical sections the thread is in and is used to detect a nested JNI + // critical section enter. + uint32_t disable_thread_flip_count; } tls32_; struct PACKED(8) tls_64bit_sized_values { diff --git a/runtime/utils.cc b/runtime/utils.cc index 07f94c0766..13564a6a0f 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1392,9 +1392,8 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is return filename; } -bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { +int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) { const std::string command_line(Join(arg_vector, ' ')); - CHECK_GE(arg_vector.size(), 1U) << command_line; // Convert the args to char pointers. @@ -1417,7 +1416,6 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { setpgid(0, 0); execv(program, &args[0]); - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; // _exit to avoid atexit handlers in child. _exit(1); @@ -1425,23 +1423,32 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { if (pid == -1) { *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", command_line.c_str(), strerror(errno)); - return false; + return -1; } // wait for subprocess to finish - int status; + int status = -1; pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (got_pid != pid) { *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " "wanted %d, got %d: %s", command_line.c_str(), pid, got_pid, strerror(errno)); - return false; + return -1; } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", - command_line.c_str()); - return false; + if (WIFEXITED(status)) { + return WEXITSTATUS(status); } + return -1; + } +} + +bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) { + int status = ExecAndReturnCode(arg_vector, error_msg); + if (status != 0) { + const std::string command_line(Join(arg_vector, ' ')); + *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", + command_line.c_str()); + return false; } return true; } diff --git a/runtime/utils.h b/runtime/utils.h index c00db11c16..83ac0b870e 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -111,6 +111,11 @@ template <typename T> T AbsOrMin(T value) { : std::abs(value); } +template <typename T> +inline typename std::make_unsigned<T>::type MakeUnsigned(T x) { + return static_cast<typename std::make_unsigned<T>::type>(x); +} + std::string PrintableChar(uint16_t ch); // Returns an ASCII string corresponding to the given UTF-8 string. @@ -287,6 +292,7 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa); // Wrapper on fork/execv to run a command in a subprocess. bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg); +int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg); // Returns true if the file exists. bool FileExists(const std::string& filename); @@ -343,7 +349,7 @@ static void ParseUintOption(const StringPiece& option, UsageFn Usage, bool is_long_option = true) { std::string option_prefix = option_name + (is_long_option ? "=" : ""); - DCHECK(option.starts_with(option_prefix)); + DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix; const char* value_string = option.substr(option_prefix.size()).data(); int64_t parsed_integer_value = 0; if (!ParseInt(value_string, &parsed_integer_value)) { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 1d31408cf0..0c6060e4e8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -617,23 +617,6 @@ ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { return GetQuickInvokedMethod(inst, register_line, is_range, false); } -SafeMap<uint32_t, std::set<uint32_t>> MethodVerifier::FindStringInitMap(ArtMethod* m) { - Thread* self = Thread::Current(); - StackHandleScope<2> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache())); - Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader())); - MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(), - m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), - true, true, false, true); - // Avoid copying: The map is moved out of the verifier before the verifier is destroyed. - return std::move(verifier.FindStringInitMap()); -} - -SafeMap<uint32_t, std::set<uint32_t>>& MethodVerifier::FindStringInitMap() { - Verify(); - return GetStringInitPcRegMap(); -} - bool MethodVerifier::Verify() { // Some older code doesn't correctly mark constructors as such. Test for this case by looking at // the name. @@ -1960,8 +1943,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // We need to ensure the work line is consistent while performing validation. When we spot a // peephole pattern we compute a new line for either the fallthrough instruction or the // branch target. - ArenaUniquePtr<RegisterLine> branch_line; - ArenaUniquePtr<RegisterLine> fallthrough_line; + RegisterLineArenaUniquePtr branch_line; + RegisterLineArenaUniquePtr fallthrough_line; switch (inst->Opcode()) { case Instruction::NOP: @@ -2865,8 +2848,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * Replace the uninitialized reference with an initialized one. We need to do this for all * registers that have the same object instance in them, not just the "this" register. */ - const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c(); - work_line_->MarkRefsAsInitialized(this, this_type, this_reg, work_insn_idx_); + work_line_->MarkRefsAsInitialized(this, this_type); } if (return_type == nullptr) { return_type = ®_types_.FromDescriptor(GetClassLoader(), return_type_descriptor, false); @@ -4824,7 +4806,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin AdjustReturnLine(this, ret_inst, target_line); } } else { - ArenaUniquePtr<RegisterLine> copy; + RegisterLineArenaUniquePtr copy; if (kDebugVerify) { copy.reset(RegisterLine::Create(target_line->NumRegs(), this)); copy->CopyFromLine(target_line); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index c7d1e6bc90..6d8e1ab6ee 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -30,6 +30,7 @@ #include "handle.h" #include "instruction_flags.h" #include "method_reference.h" +#include "register_line.h" #include "reg_type_cache.h" namespace art { @@ -45,6 +46,7 @@ namespace verifier { class DexPcToReferenceMap; class MethodVerifier; class RegisterLine; +using RegisterLineArenaUniquePtr = std::unique_ptr<RegisterLine, RegisterLineArenaDelete>; class RegType; /* @@ -127,7 +129,7 @@ class PcToRegisterLineTable { } private: - ScopedArenaVector<ArenaUniquePtr<RegisterLine>> register_lines_; + ScopedArenaVector<RegisterLineArenaUniquePtr> register_lines_; DISALLOW_COPY_AND_ASSIGN(PcToRegisterLineTable); }; @@ -211,9 +213,6 @@ class MethodVerifier { static ArtMethod* FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc) SHARED_REQUIRES(Locks::mutator_lock_); - static SafeMap<uint32_t, std::set<uint32_t>> FindStringInitMap(ArtMethod* m) - SHARED_REQUIRES(Locks::mutator_lock_); - static void Init() SHARED_REQUIRES(Locks::mutator_lock_); static void Shutdown(); @@ -292,10 +291,6 @@ class MethodVerifier { ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) SHARED_REQUIRES(Locks::mutator_lock_); - SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() { - return string_init_pc_reg_map_; - } - uint32_t GetEncounteredFailureTypes() { return encountered_failure_types_; } @@ -771,14 +766,14 @@ class MethodVerifier { PcToRegisterLineTable reg_table_; // Storage for the register status we're currently working on. - ArenaUniquePtr<RegisterLine> work_line_; + RegisterLineArenaUniquePtr work_line_; // The address of the instruction we're currently working on, note that this is in 2 byte // quantities uint32_t work_insn_idx_; // Storage for the register status we're saving for later. - ArenaUniquePtr<RegisterLine> saved_line_; + RegisterLineArenaUniquePtr saved_line_; const uint32_t dex_method_idx_; // The method we're working on. // Its object representation if known. @@ -873,11 +868,6 @@ class MethodVerifier { friend class art::Thread; - // Map of dex pcs of invocations of java.lang.String.<init> to the set of other registers that - // contain the uninitialized this pointer to that invoke. Will contain no entry if there are - // no other registers. - SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_; - DISALLOW_COPY_AND_ASSIGN(MethodVerifier); }; std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs); diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h index 330c06ab46..29d87c4433 100644 --- a/runtime/verifier/register_line-inl.h +++ b/runtime/verifier/register_line-inl.h @@ -185,9 +185,12 @@ inline void RegisterLine::VerifyMonitorStackEmpty(MethodVerifier* verifier) cons } } +inline size_t RegisterLine::ComputeSize(size_t num_regs) { + return OFFSETOF_MEMBER(RegisterLine, line_) + num_regs * sizeof(uint16_t); +} + inline RegisterLine* RegisterLine::Create(size_t num_regs, MethodVerifier* verifier) { - void* memory = verifier->GetArena().Alloc(OFFSETOF_MEMBER(RegisterLine, line_) + - (num_regs * sizeof(uint16_t))); + void* memory = verifier->GetArena().Alloc(ComputeSize(num_regs)); return new (memory) RegisterLine(num_regs, verifier); } @@ -200,6 +203,13 @@ inline RegisterLine::RegisterLine(size_t num_regs, MethodVerifier* verifier) SetResultTypeToUnknown(verifier); } +inline void RegisterLineArenaDelete::operator()(RegisterLine* ptr) const { + if (ptr != nullptr) { + ptr->~RegisterLine(); + ProtectMemory(ptr, RegisterLine::ComputeSize(ptr->NumRegs())); + } +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index b7cde995c7..82c371dec5 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -91,25 +91,14 @@ bool RegisterLine::VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsr return true; } -void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type, - uint32_t this_reg, uint32_t dex_pc) { +void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) { DCHECK(uninit_type.IsUninitializedTypes()); - bool is_string = !uninit_type.IsUnresolvedTypes() && uninit_type.GetClass()->IsStringClass(); const RegType& init_type = verifier->GetRegTypeCache()->FromUninitialized(uninit_type); size_t changed = 0; for (uint32_t i = 0; i < num_regs_; i++) { if (GetRegisterType(verifier, i).Equals(uninit_type)) { line_[i] = init_type.GetId(); changed++; - if (is_string && i != this_reg) { - auto it = verifier->GetStringInitPcRegMap().find(dex_pc); - if (it != verifier->GetStringInitPcRegMap().end()) { - it->second.insert(i); - } else { - std::set<uint32_t> reg_set = { i }; - verifier->GetStringInitPcRegMap().Put(dex_pc, reg_set); - } - } } } // Is this initializing "this"? diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index b2f5555d8b..15ae202301 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -99,11 +99,14 @@ class RegisterLine { // available now. An example is sharpening types after a check-cast. Note that when given kKeep, // the new_type is dchecked to be a reference type. template <LockOp kLockOp> - ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier, uint32_t vdst, + ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier, + uint32_t vdst, const RegType& new_type) SHARED_REQUIRES(Locks::mutator_lock_); - bool SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst, const RegType& new_type1, + bool SetRegisterTypeWide(MethodVerifier* verifier, + uint32_t vdst, + const RegType& new_type1, const RegType& new_type2) SHARED_REQUIRES(Locks::mutator_lock_); @@ -117,11 +120,14 @@ class RegisterLine { // Get the type of register vsrc. const RegType& GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const; - ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc, + ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier, + uint32_t vsrc, const RegType& check_type) SHARED_REQUIRES(Locks::mutator_lock_); - bool VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsrc, const RegType& check_type1, + bool VerifyRegisterTypeWide(MethodVerifier* verifier, + uint32_t vsrc, + const RegType& check_type1, const RegType& check_type2) SHARED_REQUIRES(Locks::mutator_lock_); @@ -155,8 +161,7 @@ class RegisterLine { * reference type. This is called when an appropriate constructor is invoked -- all copies of * the reference must be marked as initialized. */ - void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type, - uint32_t this_reg, uint32_t dex_pc) + void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) SHARED_REQUIRES(Locks::mutator_lock_); /* @@ -197,6 +202,9 @@ class RegisterLine { return num_regs_; } + // Return how many bytes of memory a register line uses. + ALWAYS_INLINE static size_t ComputeSize(size_t num_regs); + /* * Get the "this" pointer from a non-static method invocation. This returns the RegType so the * caller can decide whether it needs the reference to be initialized or not. (Can also return @@ -207,31 +215,42 @@ class RegisterLine { * allow_failure will return Conflict() instead of causing a verification failure if there is an * error. */ - const RegType& GetInvocationThis(MethodVerifier* verifier, const Instruction* inst, - bool is_range, bool allow_failure = false) + const RegType& GetInvocationThis(MethodVerifier* verifier, + const Instruction* inst, + bool is_range, + bool allow_failure = false) SHARED_REQUIRES(Locks::mutator_lock_); /* * Verify types for a simple two-register instruction (e.g. "neg-int"). * "dst_type" is stored into vA, and "src_type" is verified against vB. */ - void CheckUnaryOp(MethodVerifier* verifier, const Instruction* inst, const RegType& dst_type, + void CheckUnaryOp(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type, const RegType& src_type) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckUnaryOpWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, - const RegType& src_type1, const RegType& src_type2) + void CheckUnaryOpWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, + const RegType& src_type1, + const RegType& src_type2) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckUnaryOpToWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, + void CheckUnaryOpToWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, const RegType& src_type) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckUnaryOpFromWide(MethodVerifier* verifier, const Instruction* inst, + void CheckUnaryOpFromWide(MethodVerifier* verifier, + const Instruction* inst, const RegType& dst_type, - const RegType& src_type1, const RegType& src_type2) + const RegType& src_type1, + const RegType& src_type2) SHARED_REQUIRES(Locks::mutator_lock_); /* @@ -239,19 +258,28 @@ class RegisterLine { * "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified * against vB/vC. */ - void CheckBinaryOp(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type, const RegType& src_type1, const RegType& src_type2, + void CheckBinaryOp(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type, + const RegType& src_type1, + const RegType& src_type2, bool check_boolean_op) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOpWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, - const RegType& src_type1_1, const RegType& src_type1_2, - const RegType& src_type2_1, const RegType& src_type2_2) + void CheckBinaryOpWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, + const RegType& src_type1_1, + const RegType& src_type1_2, + const RegType& src_type2_1, + const RegType& src_type2_2) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOpWideShift(MethodVerifier* verifier, const Instruction* inst, - const RegType& long_lo_type, const RegType& long_hi_type, + void CheckBinaryOpWideShift(MethodVerifier* verifier, + const Instruction* inst, + const RegType& long_lo_type, + const RegType& long_hi_type, const RegType& int_type) SHARED_REQUIRES(Locks::mutator_lock_); @@ -259,20 +287,28 @@ class RegisterLine { * Verify types for a binary "2addr" operation. "src_type1"/"src_type2" * are verified against vA/vB, then "dst_type" is stored into vA. */ - void CheckBinaryOp2addr(MethodVerifier* verifier, const Instruction* inst, + void CheckBinaryOp2addr(MethodVerifier* verifier, + const Instruction* inst, const RegType& dst_type, - const RegType& src_type1, const RegType& src_type2, + const RegType& src_type1, + const RegType& src_type2, bool check_boolean_op) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOp2addrWide(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type1, const RegType& dst_type2, - const RegType& src_type1_1, const RegType& src_type1_2, - const RegType& src_type2_1, const RegType& src_type2_2) + void CheckBinaryOp2addrWide(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type1, + const RegType& dst_type2, + const RegType& src_type1_1, + const RegType& src_type1_2, + const RegType& src_type2_1, + const RegType& src_type2_2) SHARED_REQUIRES(Locks::mutator_lock_); - void CheckBinaryOp2addrWideShift(MethodVerifier* verifier, const Instruction* inst, - const RegType& long_lo_type, const RegType& long_hi_type, + void CheckBinaryOp2addrWideShift(MethodVerifier* verifier, + const Instruction* inst, + const RegType& long_lo_type, + const RegType& long_hi_type, const RegType& int_type) SHARED_REQUIRES(Locks::mutator_lock_); @@ -282,9 +318,12 @@ class RegisterLine { * * If "check_boolean_op" is set, we use the constant value in vC. */ - void CheckLiteralOp(MethodVerifier* verifier, const Instruction* inst, - const RegType& dst_type, const RegType& src_type, - bool check_boolean_op, bool is_lit16) + void CheckLiteralOp(MethodVerifier* verifier, + const Instruction* inst, + const RegType& dst_type, + const RegType& src_type, + bool check_boolean_op, + bool is_lit16) SHARED_REQUIRES(Locks::mutator_lock_); // Verify/push monitor onto the monitor stack, locking the value in reg_idx at location insn_idx. @@ -401,6 +440,11 @@ class RegisterLine { DISALLOW_COPY_AND_ASSIGN(RegisterLine); }; +class RegisterLineArenaDelete : public ArenaDelete<RegisterLine> { + public: + void operator()(RegisterLine* ptr) const; +}; + } // namespace verifier } // namespace art diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build index faa298337c..56e87844c0 100644 --- a/test/003-omnibus-opcodes/build +++ b/test/003-omnibus-opcodes/build @@ -23,8 +23,8 @@ rm classes/UnresClass.class ${JAVAC} -d classes `find src2 -name '*.java'` if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt index 86ab37e1e5..155c6ae5f3 100644 --- a/test/004-JniTest/expected.txt +++ b/test/004-JniTest/expected.txt @@ -28,3 +28,30 @@ Subclass.<init> RUNNING sub object, sub class, sub nonstatic Subclass.nonstaticMethod PASSED sub object, sub class, sub nonstatic +Calling method ConcreteClass->JniCallNonOverridenDefaultMethod on object of type ConcreteClass +DefaultInterface.JniCallNonOverridenDefaultMethod +Calling method ConcreteClass->JniCallOverridenDefaultMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenDefaultMethod +Calling method ConcreteClass->JniCallOverridenDefaultMethodWithSuper on object of type ConcreteClass +ConcreteClass.JniCallOverridenDefaultMethodWithSuper +DefaultInterface.JniCallOverridenDefaultMethod +Calling method ConcreteClass->JniCallOverridenAbstractMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenAbstractMethod +Calling method ConcreteClass->JniCallConflictDefaultMethod on object of type ConcreteClass +EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() +Calling method ConcreteClass->JniCallSoftConflictMethod on object of type ConcreteClass +DefaultInterface.JniCallSoftConflictMethod +Calling method DefaultInterface->JniCallNonOverridenDefaultMethod on object of type ConcreteClass +DefaultInterface.JniCallNonOverridenDefaultMethod +Calling method DefaultInterface->JniCallOverridenDefaultMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenDefaultMethod +Calling method DefaultInterface->JniCallOverridenAbstractMethod on object of type ConcreteClass +ConcreteClass.JniCallOverridenAbstractMethod +Calling method DefaultInterface->JniCallConflictDefaultMethod on object of type ConcreteClass +EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() +Calling method DefaultInterface->JniCallSoftConflictMethod on object of type ConcreteClass +DefaultInterface.JniCallSoftConflictMethod +Calling method AbstractInterface->JniCallSoftConflictMethod on object of type ConcreteClass +DefaultInterface.JniCallSoftConflictMethod +Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass +EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod() diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index be7888b04a..f632331fe3 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -639,3 +639,85 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jcl extern "C" JNIEXPORT jlong JNICALL Java_Main_testGetMethodID(JNIEnv* env, jclass, jclass c) { return reinterpret_cast<jlong>(env->GetMethodID(c, "a", "()V")); } + +extern "C" JNIEXPORT void JNICALL Java_Main_enterJniCriticalSection(JNIEnv* env, jclass, + jint arraySize, + jbyteArray array0, + jbyteArray array1) { + for (int i = 0; i < 50000; ++i) { + char* data0 = reinterpret_cast<char*>(env->GetPrimitiveArrayCritical(array0, nullptr)); + char* data1 = reinterpret_cast<char*>(env->GetPrimitiveArrayCritical(array1, nullptr)); + bool up = i % 2 == 0; + for (int j = 0; j < arraySize; ++j) { + if (up) { + data1[j] = data0[j] + 1; + } else { + data0[j] = data1[j] + 1; + } + } + env->ReleasePrimitiveArrayCritical(array1, data1, 0); + env->ReleasePrimitiveArrayCritical(array0, data0, 0); + } +} + +class JniCallDefaultMethodsTest { + public: + explicit JniCallDefaultMethodsTest(JNIEnv* env) + : env_(env), concrete_class_(env_->FindClass("ConcreteClass")) { + assert(!env_->ExceptionCheck()); + assert(concrete_class_ != nullptr); + } + + void Test() { + TestCalls("ConcreteClass", { "JniCallNonOverridenDefaultMethod", + "JniCallOverridenDefaultMethod", + "JniCallOverridenDefaultMethodWithSuper", + "JniCallOverridenAbstractMethod", + "JniCallConflictDefaultMethod", + "JniCallSoftConflictMethod" }); + TestCalls("DefaultInterface", { "JniCallNonOverridenDefaultMethod", + "JniCallOverridenDefaultMethod", + "JniCallOverridenAbstractMethod", + "JniCallConflictDefaultMethod", + "JniCallSoftConflictMethod" }); + TestCalls("AbstractInterface", { "JniCallSoftConflictMethod" }); + TestCalls("ConflictInterface", { "JniCallConflictDefaultMethod" }); + } + + private: + void TestCalls(const char* declaring_class, std::vector<const char*> methods) { + jmethodID new_method = env_->GetMethodID(concrete_class_, "<init>", "()V"); + jobject obj = env_->NewObject(concrete_class_, new_method); + assert(!env_->ExceptionCheck()); + assert(obj != nullptr); + jclass decl_class = env_->FindClass(declaring_class); + assert(!env_->ExceptionCheck()); + assert(decl_class != nullptr); + for (const char* method : methods) { + jmethodID method_id = env_->GetMethodID(decl_class, method, "()V"); + assert(!env_->ExceptionCheck()); + printf("Calling method %s->%s on object of type ConcreteClass\n", declaring_class, method); + env_->CallVoidMethod(obj, method_id); + if (env_->ExceptionCheck()) { + jthrowable thrown = env_->ExceptionOccurred(); + env_->ExceptionClear(); + jmethodID to_string = env_->GetMethodID( + env_->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;"); + jstring exception_string = (jstring) env_->CallObjectMethod(thrown, to_string); + assert(!env_->ExceptionCheck()); + const char* exception_string_utf8 = env_->GetStringUTFChars(exception_string, nullptr); + assert(!env_->ExceptionCheck()); + assert(exception_string_utf8 != nullptr); + printf("EXCEPTION OCCURED: %s\n", exception_string_utf8); + env_->ReleaseStringUTFChars(exception_string, exception_string_utf8); + } + } + } + + JNIEnv* env_; + jclass concrete_class_; +}; + +extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) { + JniCallDefaultMethodsTest(env).Test(); +} diff --git a/test/004-JniTest/smali/AbstractInterface.smali b/test/004-JniTest/smali/AbstractInterface.smali new file mode 100644 index 0000000000..52b2fc537e --- /dev/null +++ b/test/004-JniTest/smali/AbstractInterface.smali @@ -0,0 +1,26 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public interface LAbstractInterface; +.super Ljava/lang/Object; + +# public interface AbstractInterface { +# public void JniCallSoftConflictMethod(); +# } + +.method public abstract JniCallSoftConflictMethod()V +.end method + diff --git a/test/004-JniTest/smali/ConcreteClass.smali b/test/004-JniTest/smali/ConcreteClass.smali new file mode 100644 index 0000000000..a9c072fc2f --- /dev/null +++ b/test/004-JniTest/smali/ConcreteClass.smali @@ -0,0 +1,72 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public LConcreteClass; +.super Ljava/lang/Object; +.implements LDefaultInterface; +.implements LConflictInterface; +.implements LAbstractInterface; + +# public class ConcreteClass implements DefaultInterface, ConflictInterface, AbstractInterface { +# public void JniCallOverridenAbstractMethod() { +# System.out.println("ConcreteClass.JniCallOverridenAbstractMethod"); +# } +# +# public void JniCallOverridenDefaultMethod() { +# System.out.println("ConcreteClass.JniCallOverridenDefaultMethod"); +# } +# +# public void JniCallOverridenDefaultMethodWithSuper() { +# System.out.println("ConcreteClass.JniCallOverridenDefaultMethodWithSuper"); +# DefaultInterface.super.JniCallOverridenDefaultMethod(); +# } +# } + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public JniCallOverridenAbstractMethod()V + .locals 2 + + const-string v0, "ConcreteClass.JniCallOverridenAbstractMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallOverridenDefaultMethod()V + .locals 2 + + const-string v0, "ConcreteClass.JniCallOverridenDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallOverridenDefaultMethodWithSuper()V + .locals 2 + + const-string v0, "ConcreteClass.JniCallOverridenDefaultMethodWithSuper" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + invoke-super {p0}, LDefaultInterface;->JniCallOverridenDefaultMethod()V + + return-void +.end method diff --git a/test/004-JniTest/smali/ConflictInterface.smali b/test/004-JniTest/smali/ConflictInterface.smali new file mode 100644 index 0000000000..fc3d474df0 --- /dev/null +++ b/test/004-JniTest/smali/ConflictInterface.smali @@ -0,0 +1,35 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public interface LConflictInterface; +.super Ljava/lang/Object; + +# public interface ConflictInterface { +# public default void JniCallConflictDefaultMethod() { +# System.out.println("ConflictInterface.JniCallConflictDefaultMethod"); +# } +# +# } + +.method public JniCallConflictDefaultMethod()V + .locals 2 + + const-string v0, "ConflictInterface.JniCallConflictDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + diff --git a/test/004-JniTest/smali/DefaultInterface.smali b/test/004-JniTest/smali/DefaultInterface.smali new file mode 100644 index 0000000000..1ee872154b --- /dev/null +++ b/test/004-JniTest/smali/DefaultInterface.smali @@ -0,0 +1,77 @@ +# /* +# * Copyright 2016 The Android Open Source Project +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# */ + +.class public interface LDefaultInterface; +.super Ljava/lang/Object; + +# public interface DefaultInterface { +# public default void JniCallNonOverridenDefaultMethod() { +# System.out.println("DefaultInterface.JniCallNonOverridenDefaultMethod"); +# } +# +# public default void JniCallOverridenDefaultMethod() { +# System.out.println("DefaultInterface.JniCallOverridenDefaultMethod"); +# } +# +# public void JniCallOverridenAbstractMethod(); +# +# public default void JniCallConflictDefaultMethod() { +# System.out.println("DefaultInterface.JniCallConflictDefaultMethod"); +# } +# +# public default void JniCallSoftConflictMethod() { +# System.out.println("DefaultInterface.JniCallSoftConflictMethod"); +# } +# } + +.method public JniCallNonOverridenDefaultMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallNonOverridenDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallOverridenDefaultMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallOverridenDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public abstract JniCallOverridenAbstractMethod()V +.end method + +.method public JniCallConflictDefaultMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallConflictDefaultMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method public JniCallSoftConflictMethod()V + .locals 2 + + const-string v0, "DefaultInterface.JniCallSoftConflictMethod" + sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index ee3a3b9830..9f4a8522e7 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -38,8 +38,12 @@ public class Main { testNewStringObject(); testRemoveLocalObject(); testProxyGetMethodID(); + testJniCriticalSectionAndGc(); + testCallDefaultMethods(); } + private static native void testCallDefaultMethods(); + private static native void testFindClassOnAttachedNativeThread(); private static boolean testFindFieldOnAttachedNativeThreadField; @@ -120,7 +124,7 @@ public class Main { private static void testRemoveLocalObject() { removeLocalObject(new Object()); } - + private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7, short s8, short s9, short s10); @@ -222,6 +226,35 @@ public class Main { } private static native long testGetMethodID(Class<?> c); + + // Exercise GC and JNI critical sections in parallel. + private static void testJniCriticalSectionAndGc() { + Thread runGcThread = new Thread(new Runnable() { + @Override + public void run() { + for (int i = 0; i < 10; ++i) { + Runtime.getRuntime().gc(); + } + } + }); + Thread jniCriticalThread = new Thread(new Runnable() { + @Override + public void run() { + final int arraySize = 32; + byte[] array0 = new byte[arraySize]; + byte[] array1 = new byte[arraySize]; + enterJniCriticalSection(arraySize, array0, array1); + } + }); + jniCriticalThread.start(); + runGcThread.start(); + try { + jniCriticalThread.join(); + runGcThread.join(); + } catch (InterruptedException ignored) {} + } + + private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array); } class JniCallNonvirtualTest { diff --git a/test/005-annotations/build b/test/005-annotations/build index 057b351dab..93bee507df 100644 --- a/test/005-annotations/build +++ b/test/005-annotations/build @@ -29,8 +29,8 @@ rm 'classes/android/test/anno/MissingAnnotation.class' rm 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class' if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/022-interface/build b/test/022-interface/build index 3f8915c27e..5cfc7f25b7 100644 --- a/test/022-interface/build +++ b/test/022-interface/build @@ -20,8 +20,8 @@ set -e # Use classes that are compiled with ecj that exposes an invokeinterface # issue when interfaces override methods in Object if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index af25d9bc54..e5c9dba63f 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -804,6 +804,7 @@ public class Main { Assert.assertEquals(Math.round(-2.9d), -3l); Assert.assertEquals(Math.round(-3.0d), -3l); Assert.assertEquals(Math.round(0.49999999999999994d), 0l); + Assert.assertEquals(Math.round(9007199254740991.0d), 9007199254740991l); // 2^53 - 1 Assert.assertEquals(Math.round(Double.NaN), (long)+0.0d); Assert.assertEquals(Math.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE); Assert.assertEquals(Math.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE); @@ -825,6 +826,7 @@ public class Main { Assert.assertEquals(Math.round(-2.5f), -2); Assert.assertEquals(Math.round(-2.9f), -3); Assert.assertEquals(Math.round(-3.0f), -3); + Assert.assertEquals(Math.round(16777215.0f), 16777215); // 2^24 - 1 Assert.assertEquals(Math.round(Float.NaN), (int)+0.0f); Assert.assertEquals(Math.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE); Assert.assertEquals(Math.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE); diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build index 6f50a76863..21dc66269d 100644 --- a/test/085-old-style-inner-class/build +++ b/test/085-old-style-inner-class/build @@ -23,8 +23,8 @@ mkdir classes ${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'` if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else # Suppress stderr to keep the inner class warnings out of the expected output. ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build index 5a340dcf6d..073a4ba9bc 100755 --- a/test/091-override-package-private-method/build +++ b/test/091-override-package-private-method/build @@ -24,14 +24,12 @@ mkdir classes-ex mv classes/OverridePackagePrivateMethodSuper.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/097-duplicate-method/build b/test/097-duplicate-method/build index a8558739de..4525549905 100644 --- a/test/097-duplicate-method/build +++ b/test/097-duplicate-method/build @@ -23,10 +23,10 @@ if [ ${USE_JACK} = "true" ]; then ${JACK} --output-jack src.jack src ${JASMIN} -d classes src/*.j - ${JILL} classes --output jasmin.jack + jar cf jasmin.jill.jar -C classes . # We set jack.import.type.policy=keep-first to consider class definitions from jasmin first. - ${JACK} --import jasmin.jack --import src.jack -D jack.import.type.policy=keep-first --output-dex . + ${JACK} --import jasmin.jill.jar --import src.jack -D jack.import.type.policy=keep-first --output-dex . else ${JAVAC} -d classes src/*.java ${JASMIN} -d classes src/*.j diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build index e772fb812f..58ac26d836 100644 --- a/test/111-unresolvable-exception/build +++ b/test/111-unresolvable-exception/build @@ -22,8 +22,8 @@ ${JAVAC} -d classes `find src -name '*.java'` rm classes/TestException.class if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/113-multidex/build b/test/113-multidex/build index 8ef5c0eb0f..4557ccd22a 100644 --- a/test/113-multidex/build +++ b/test/113-multidex/build @@ -28,14 +28,12 @@ ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Second.class classes2/FillerA.class classes2/FillerB.class classes2/Inf*.class if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes2 --output classes2.jack + jar cf classes.jill.jar -C classes . + jar cf classes2.jill.jar -C classes2 . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . mv classes.dex classes-1.dex - ${JACK} --import classes2.jack --output-dex . + ${JACK} --import classes2.jill.jar --output-dex . mv classes.dex classes2.dex mv classes-1.dex classes.dex else diff --git a/test/121-modifiers/build b/test/121-modifiers/build index 85b69e92a6..771dd51829 100644 --- a/test/121-modifiers/build +++ b/test/121-modifiers/build @@ -31,9 +31,9 @@ set -e # mv Main.class A.class A\$B.class A\$C.class classes/ if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack + jar cf classes.jill.jar -C classes . # Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers. - ${JACK} --sanity-checks off --import classes.jack --output-dex . + ${JACK} --sanity-checks off --import classes.jill.jar --output-dex . else ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes fi diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build index b92ecf9382..0a340a26d6 100644 --- a/test/124-missing-classes/build +++ b/test/124-missing-classes/build @@ -27,8 +27,8 @@ rm 'classes/MissingClass.class' rm 'classes/Main$MissingInnerClass.class' if [ ${USE_JACK} = "true" ]; then - ${JILL} classes --output classes.jack - ${JACK} --import classes.jack --output-dex . + jar cf classes.jill.jar -C classes . + ${JACK} --import classes.jill.jar --output-dex . else ${DX} -JXmx256m --debug --dex --output=classes.dex classes fi diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build index b7f2118d2f..00b9ba0ac2 100644 --- a/test/126-miranda-multidex/build +++ b/test/126-miranda-multidex/build @@ -28,14 +28,12 @@ ${JAVAC} -d classes2 `find src -name '*.java'` rm classes2/Main.class classes2/MirandaAbstract.class classes2/MirandaClass*.class classes2/MirandaInterface2*.class if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes2 --output classes2.jack + jar cf classes.jill.jar -C classes . + jar cf classes2.jill.jar -C classes2 . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . mv classes.dex classes-1.dex - ${JACK} --import classes2.jack --output-dex . + ${JACK} --import classes2.jill.jar --output-dex . mv classes.dex classes2.dex mv classes-1.dex classes.dex else diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build index 0d9f4d6291..7ce46acfed 100755 --- a/test/127-checker-secondarydex/build +++ b/test/127-checker-secondarydex/build @@ -24,14 +24,12 @@ mkdir classes-ex mv classes/Super.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/130-hprof/src-ex/Allocator.java b/test/130-hprof/src-ex/Allocator.java new file mode 100644 index 0000000000..ee75a14f30 --- /dev/null +++ b/test/130-hprof/src-ex/Allocator.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +// Simple allocator that returns a boot class path object. +public class Allocator { + public static Object allocObject() { + return new Object(); + } +} diff --git a/test/130-hprof/src/Main.java b/test/130-hprof/src/Main.java index 67e52323dd..9868c617f5 100644 --- a/test/130-hprof/src/Main.java +++ b/test/130-hprof/src/Main.java @@ -16,6 +16,7 @@ import java.io.File; import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; @@ -34,24 +35,21 @@ public class Main { } } - public static void main(String[] args) { - // Create some data. - Object data[] = new Object[TEST_LENGTH]; - for (int i = 0; i < data.length; i++) { - if (makeArray(i)) { - data[i] = new Object[TEST_LENGTH]; - } else { - data[i] = String.valueOf(i); - } + private static Object allocInDifferentLoader() throws Exception { + final String DEX_FILE = System.getenv("DEX_LOCATION") + "/130-hprof-ex.jar"; + Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); + if (pathClassLoader == null) { + throw new AssertionError("Couldn't find path class loader class"); } - for (int i = 0; i < data.length; i++) { - if (makeArray(i)) { - Object data2[] = (Object[]) data[i]; - fillArray(data, data2, i); - } - } - System.out.println("Generated data."); + Constructor constructor = + pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); + ClassLoader loader = (ClassLoader)constructor.newInstance( + DEX_FILE, ClassLoader.getSystemClassLoader()); + Class allocator = loader.loadClass("Allocator"); + return allocator.getDeclaredMethod("allocObject", null).invoke(null); + } + private static void createDumpAndConv() throws RuntimeException { File dumpFile = null; File convFile = null; @@ -88,6 +86,43 @@ public class Main { } } + public static void main(String[] args) throws Exception { + // Create some data. + Object data[] = new Object[TEST_LENGTH]; + for (int i = 0; i < data.length; i++) { + if (makeArray(i)) { + data[i] = new Object[TEST_LENGTH]; + } else { + data[i] = String.valueOf(i); + } + } + for (int i = 0; i < data.length; i++) { + if (makeArray(i)) { + Object data2[] = (Object[]) data[i]; + fillArray(data, data2, i); + } + } + System.out.println("Generated data."); + + createDumpAndConv(); + Class klass = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal"); + if (klass == null) { + throw new AssertionError("Couldn't find path class loader class"); + } + Method enableMethod = klass.getDeclaredMethod("enableRecentAllocations", + Boolean.TYPE); + if (enableMethod == null) { + throw new AssertionError("Couldn't find path class loader class"); + } + enableMethod.invoke(null, true); + Object o = allocInDifferentLoader(); + // Run GC to cause class unloading. + Runtime.getRuntime().gc(); + createDumpAndConv(); + // TODO: Somehow check contents of hprof file. + enableMethod.invoke(null, false); + } + private static File getHprofConf() { // Use the java.library.path. It points to the lib directory. File libDir = new File(System.getProperty("java.library.path")); diff --git a/test/442-checker-constant-folding/smali/TestCmp.smali b/test/442-checker-constant-folding/smali/TestCmp.smali new file mode 100644 index 0000000000..df631bc202 --- /dev/null +++ b/test/442-checker-constant-folding/smali/TestCmp.smali @@ -0,0 +1,332 @@ +# 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. + +.class public LTestCmp; + +.super Ljava/lang/Object; + + +## CHECK-START: int TestCmp.$opt$CmpLongConstants() constant_folding (before) +## CHECK-DAG: <<Const13:j\d+>> LongConstant 13 +## CHECK-DAG: <<Const7:j\d+>> LongConstant 7 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const13>>,<<Const7>>] +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLongConstants() constant_folding (after) +## CHECK-DAG: LongConstant 13 +## CHECK-DAG: LongConstant 7 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: Return [<<Const1>>] + +## CHECK-START: int TestCmp.$opt$CmpLongConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLongConstants()I + .registers 5 + const-wide v1, 13 + const-wide v3, 7 + cmp-long v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstants() constant_folding (before) +## CHECK-DAG: <<Const11:f\d+>> FloatConstant 11 +## CHECK-DAG: <<Const22:f\d+>> FloatConstant 22 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const11>>,<<Const22>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstants() constant_folding (after) +## CHECK-DAG: FloatConstant 11 +## CHECK-DAG: FloatConstant 22 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtFloatConstants()I + .registers 3 + const v1, 11.f + const v2, 22.f + cmpg-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstants() constant_folding (before) +## CHECK-DAG: <<Const33:f\d+>> FloatConstant 33 +## CHECK-DAG: <<Const44:f\d+>> FloatConstant 44 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const33>>,<<Const44>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstants() constant_folding (after) +## CHECK-DAG: FloatConstant 33 +## CHECK-DAG: FloatConstant 44 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtFloatConstants()I + .registers 3 + const v1, 33.f + const v2, 44.f + cmpl-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstants() constant_folding (before) +## CHECK-DAG: <<Const55:d\d+>> DoubleConstant 55 +## CHECK-DAG: <<Const66:d\d+>> DoubleConstant 66 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const55>>,<<Const66>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstants() constant_folding (after) +## CHECK-DAG: DoubleConstant 55 +## CHECK-DAG: DoubleConstant 66 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtDoubleConstants()I + .registers 5 + const-wide v1, 55. + const-wide v3, 66. + cmpg-double v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstants() constant_folding (before) +## CHECK-DAG: <<Const77:d\d+>> DoubleConstant 77 +## CHECK-DAG: <<Const88:d\d+>> DoubleConstant 88 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const77>>,<<Const88>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstants() constant_folding (after) +## CHECK-DAG: DoubleConstant 77 +## CHECK-DAG: DoubleConstant 88 +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstants() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtDoubleConstants()I + .registers 5 + const-wide v1, 77. + const-wide v3, 88. + cmpl-double v0, v1, v3 + return v0 +.end method + + +## CHECK-START: int TestCmp.$opt$CmpLongSameConstant() constant_folding (before) +## CHECK-DAG: <<Const100:j\d+>> LongConstant 100 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const100>>,<<Const100>>] +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLongSameConstant() constant_folding (after) +## CHECK-DAG: LongConstant 100 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpLongSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLongSameConstant()I + .registers 5 + const-wide v1, 100 + const-wide v3, 100 + cmp-long v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtFloatSameConstant() constant_folding (before) +## CHECK-DAG: <<Const200:f\d+>> FloatConstant 200 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const200>>,<<Const200>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatSameConstant() constant_folding (after) +## CHECK-DAG: FloatConstant 200 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtFloatSameConstant()I + .registers 3 + const v1, 200.f + const v2, 200.f + cmpg-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtFloatSameConstant() constant_folding (before) +## CHECK-DAG: <<Const300:f\d+>> FloatConstant 300 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const300>>,<<Const300>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatSameConstant() constant_folding (after) +## CHECK-DAG: FloatConstant 300 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtFloatSameConstant()I + .registers 3 + const v1, 300.f + const v2, 300.f + cmpl-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleSameConstant() constant_folding (before) +## CHECK-DAG: <<Const400:d\d+>> DoubleConstant 400 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const400>>,<<Const400>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleSameConstant() constant_folding (after) +## CHECK-DAG: DoubleConstant 400 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtDoubleSameConstant()I + .registers 5 + const-wide v1, 400. + const-wide v3, 400. + cmpg-double v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleSameConstant() constant_folding (before) +## CHECK-DAG: <<Const500:d\d+>> DoubleConstant 500 +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const500>>,<<Const500>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleSameConstant() constant_folding (after) +## CHECK-DAG: DoubleConstant 500 +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: Return [<<Const0>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleSameConstant() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtDoubleSameConstant()I + .registers 5 + const-wide v1, 500. + const-wide v3, 500. + cmpl-double v0, v1, v3 + return v0 +.end method + + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const44:f\d+>> FloatConstant 44 +## CHECK-DAG: <<ConstNan:f\d+>> FloatConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const44>>,<<ConstNan>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstantWithNaN() constant_folding (after) +## CHECK-DAG: FloatConstant 44 +## CHECK-DAG: FloatConstant nan +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: Return [<<Const1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtFloatConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtFloatConstantWithNaN()I + .registers 3 + const v1, 44.f + const v2, NaNf + cmpg-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const44:f\d+>> FloatConstant 44 +## CHECK-DAG: <<ConstNan:f\d+>> FloatConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const44>>,<<ConstNan>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstantWithNaN() constant_folding (after) +## CHECK-DAG: FloatConstant 44 +## CHECK-DAG: FloatConstant nan +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtFloatConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtFloatConstantWithNaN()I + .registers 3 + const v1, 44.f + const v2, NaNf + cmpl-float v0, v1, v2 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const45:d\d+>> DoubleConstant 45 +## CHECK-DAG: <<ConstNan:d\d+>> DoubleConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const45>>,<<ConstNan>>] bias:gt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstantWithNaN() constant_folding (after) +## CHECK-DAG: DoubleConstant 45 +## CHECK-DAG: DoubleConstant nan +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: Return [<<Const1>>] + +## CHECK-START: int TestCmp.$opt$CmpGtDoubleConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpGtDoubleConstantWithNaN()I + .registers 5 + const-wide v1, 45. + const-wide v3, NaN + cmpg-double v0, v1, v3 + return v0 +.end method + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstantWithNaN() constant_folding (before) +## CHECK-DAG: <<Const46:d\d+>> DoubleConstant 46 +## CHECK-DAG: <<ConstNan:d\d+>> DoubleConstant nan +## CHECK-DAG: <<Cmp:i\d+>> Compare [<<Const46>>,<<ConstNan>>] bias:lt +## CHECK-DAG: Return [<<Cmp>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstantWithNaN() constant_folding (after) +## CHECK-DAG: DoubleConstant 46 +## CHECK-DAG: DoubleConstant nan +## CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1 +## CHECK-DAG: Return [<<ConstM1>>] + +## CHECK-START: int TestCmp.$opt$CmpLtDoubleConstantWithNaN() constant_folding (after) +## CHECK-NOT: Compare + +.method public static $opt$CmpLtDoubleConstantWithNaN()I + .registers 5 + const-wide v1, 46. + const-wide v3, NaN + cmpl-double v0, v1, v3 + return v0 +.end method diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 5479818ae7..93fe397273 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -14,8 +14,13 @@ * limitations under the License. */ +import java.lang.reflect.Method; + public class Main { + // Workaround for b/18051191. + class InnerClass {} + public static void assertFalse(boolean condition) { if (condition) { throw new Error(); @@ -47,6 +52,68 @@ public class Main { } + // Wrappers around methods located in file TestCmp.smali. + + public int smaliCmpLongConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLongConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtFloatConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtFloatConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtFloatConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtFloatConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtDoubleConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtDoubleConstants"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtDoubleConstants() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtDoubleConstants"); + return (Integer)m.invoke(null); + } + + public int smaliCmpLongSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLongSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtFloatSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtFloatSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtFloatSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtFloatSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtDoubleSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtDoubleSameConstant"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtDoubleSameConstant() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtDoubleSameConstant"); + return (Integer)m.invoke(null); + } + + public int smaliCmpGtFloatConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtFloatConstantWithNaN"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtFloatConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtFloatConstantWithNaN"); + return (Integer)m.invoke(null); + } + public int smaliCmpGtDoubleConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpGtDoubleConstantWithNaN"); + return (Integer)m.invoke(null); + } + public int smaliCmpLtDoubleConstantWithNaN() throws Exception { + Method m = testCmp.getMethod("$opt$CmpLtDoubleConstantWithNaN"); + return (Integer)m.invoke(null); + } + + /** * Exercise constant folding on negation. */ @@ -89,6 +156,44 @@ public class Main { return y; } + /// CHECK-START: float Main.FloatNegation() constant_folding (before) + /// CHECK-DAG: <<Const42:f\d+>> FloatConstant 42 + /// CHECK-DAG: <<Neg:f\d+>> Neg [<<Const42>>] + /// CHECK-DAG: Return [<<Neg>>] + + /// CHECK-START: float Main.FloatNegation() constant_folding (after) + /// CHECK-DAG: <<ConstN42:f\d+>> FloatConstant -42 + /// CHECK-DAG: Return [<<ConstN42>>] + + /// CHECK-START: float Main.FloatNegation() constant_folding (after) + /// CHECK-NOT: Neg + + public static float FloatNegation() { + float x, y; + x = 42F; + y = -x; + return y; + } + + /// CHECK-START: double Main.DoubleNegation() constant_folding (before) + /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42 + /// CHECK-DAG: <<Neg:d\d+>> Neg [<<Const42>>] + /// CHECK-DAG: Return [<<Neg>>] + + /// CHECK-START: double Main.DoubleNegation() constant_folding (after) + /// CHECK-DAG: <<ConstN42:d\d+>> DoubleConstant -42 + /// CHECK-DAG: Return [<<ConstN42>>] + + /// CHECK-START: double Main.DoubleNegation() constant_folding (after) + /// CHECK-NOT: Neg + + public static double DoubleNegation() { + double x, y; + x = 42D; + y = -x; + return y; + } + /** * Exercise constant folding on addition. @@ -166,6 +271,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatAddition() constant_folding (before) + /// CHECK-DAG: <<Const1:f\d+>> FloatConstant 1 + /// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2 + /// CHECK-DAG: <<Add:f\d+>> Add [<<Const1>>,<<Const2>>] + /// CHECK-DAG: Return [<<Add>>] + + /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-DAG: <<Const3:f\d+>> FloatConstant 3 + /// CHECK-DAG: Return [<<Const3>>] + + /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-NOT: Add + + public static float FloatAddition() { + float a, b, c; + a = 1F; + b = 2F; + c = a + b; + return c; + } + + /// CHECK-START: double Main.DoubleAddition() constant_folding (before) + /// CHECK-DAG: <<Const1:d\d+>> DoubleConstant 1 + /// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2 + /// CHECK-DAG: <<Add:d\d+>> Add [<<Const1>>,<<Const2>>] + /// CHECK-DAG: Return [<<Add>>] + + /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-DAG: <<Const3:d\d+>> DoubleConstant 3 + /// CHECK-DAG: Return [<<Const3>>] + + /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-NOT: Add + + public static double DoubleAddition() { + double a, b, c; + a = 1D; + b = 2D; + c = a + b; + return c; + } + /** * Exercise constant folding on subtraction. @@ -213,6 +360,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatSubtraction() constant_folding (before) + /// CHECK-DAG: <<Const6:f\d+>> FloatConstant 6 + /// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2 + /// CHECK-DAG: <<Sub:f\d+>> Sub [<<Const6>>,<<Const2>>] + /// CHECK-DAG: Return [<<Sub>>] + + /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-DAG: <<Const4:f\d+>> FloatConstant 4 + /// CHECK-DAG: Return [<<Const4>>] + + /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-NOT: Sub + + public static float FloatSubtraction() { + float a, b, c; + a = 6F; + b = 2F; + c = a - b; + return c; + } + + /// CHECK-START: double Main.DoubleSubtraction() constant_folding (before) + /// CHECK-DAG: <<Const6:d\d+>> DoubleConstant 6 + /// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2 + /// CHECK-DAG: <<Sub:d\d+>> Sub [<<Const6>>,<<Const2>>] + /// CHECK-DAG: Return [<<Sub>>] + + /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-DAG: <<Const4:d\d+>> DoubleConstant 4 + /// CHECK-DAG: Return [<<Const4>>] + + /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-NOT: Sub + + public static double DoubleSubtraction() { + double a, b, c; + a = 6D; + b = 2D; + c = a - b; + return c; + } + /** * Exercise constant folding on multiplication. @@ -260,6 +449,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatMultiplication() constant_folding (before) + /// CHECK-DAG: <<Const7:f\d+>> FloatConstant 7 + /// CHECK-DAG: <<Const3:f\d+>> FloatConstant 3 + /// CHECK-DAG: <<Mul:f\d+>> Mul [<<Const7>>,<<Const3>>] + /// CHECK-DAG: Return [<<Mul>>] + + /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-DAG: <<Const21:f\d+>> FloatConstant 21 + /// CHECK-DAG: Return [<<Const21>>] + + /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-NOT: Mul + + public static float FloatMultiplication() { + float a, b, c; + a = 7F; + b = 3F; + c = a * b; + return c; + } + + /// CHECK-START: double Main.DoubleMultiplication() constant_folding (before) + /// CHECK-DAG: <<Const7:d\d+>> DoubleConstant 7 + /// CHECK-DAG: <<Const3:d\d+>> DoubleConstant 3 + /// CHECK-DAG: <<Mul:d\d+>> Mul [<<Const7>>,<<Const3>>] + /// CHECK-DAG: Return [<<Mul>>] + + /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-DAG: <<Const21:d\d+>> DoubleConstant 21 + /// CHECK-DAG: Return [<<Const21>>] + + /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-NOT: Mul + + public static double DoubleMultiplication() { + double a, b, c; + a = 7D; + b = 3D; + c = a * b; + return c; + } + /** * Exercise constant folding on division. @@ -311,6 +542,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatDivision() constant_folding (before) + /// CHECK-DAG: <<Const8:f\d+>> FloatConstant 8 + /// CHECK-DAG: <<Const2P5:f\d+>> FloatConstant 2.5 + /// CHECK-DAG: <<Div:f\d+>> Div [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Div>>] + + /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-DAG: <<Const3P2:f\d+>> FloatConstant 3.2 + /// CHECK-DAG: Return [<<Const3P2>>] + + /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-NOT: Div + + public static float FloatDivision() { + float a, b, c; + a = 8F; + b = 2.5F; + c = a / b; + return c; + } + + /// CHECK-START: double Main.DoubleDivision() constant_folding (before) + /// CHECK-DAG: <<Const8:d\d+>> DoubleConstant 8 + /// CHECK-DAG: <<Const2P5:d\d+>> DoubleConstant 2.5 + /// CHECK-DAG: <<Div:d\d+>> Div [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Div>>] + + /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-DAG: <<Const3P2:d\d+>> DoubleConstant 3.2 + /// CHECK-DAG: Return [<<Const3P2>>] + + /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-NOT: Div + + public static double DoubleDivision() { + double a, b, c; + a = 8D; + b = 2.5D; + c = a / b; + return c; + } + /** * Exercise constant folding on remainder. @@ -362,6 +635,48 @@ public class Main { return c; } + /// CHECK-START: float Main.FloatRemainder() constant_folding (before) + /// CHECK-DAG: <<Const8:f\d+>> FloatConstant 8 + /// CHECK-DAG: <<Const2P5:f\d+>> FloatConstant 2.5 + /// CHECK-DAG: <<Rem:f\d+>> Rem [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Rem>>] + + /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-DAG: <<Const0P5:f\d+>> FloatConstant 0.5 + /// CHECK-DAG: Return [<<Const0P5>>] + + /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-NOT: Rem + + public static float FloatRemainder() { + float a, b, c; + a = 8F; + b = 2.5F; + c = a % b; + return c; + } + + /// CHECK-START: double Main.DoubleRemainder() constant_folding (before) + /// CHECK-DAG: <<Const8:d\d+>> DoubleConstant 8 + /// CHECK-DAG: <<Const2P5:d\d+>> DoubleConstant 2.5 + /// CHECK-DAG: <<Rem:d\d+>> Rem [<<Const8>>,<<Const2P5>>] + /// CHECK-DAG: Return [<<Rem>>] + + /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-DAG: <<Const0P5:d\d+>> DoubleConstant 0.5 + /// CHECK-DAG: Return [<<Const0P5>>] + + /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-NOT: Rem + + public static double DoubleRemainder() { + double a, b, c; + a = 8D; + b = 2.5D; + c = a % b; + return c; + } + /** * Exercise constant folding on left shift. @@ -1197,25 +1512,37 @@ public class Main { } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { assertIntEquals(-42, IntNegation()); assertLongEquals(-42L, LongNegation()); + assertFloatEquals(-42F, FloatNegation()); + assertDoubleEquals(-42D, DoubleNegation()); assertIntEquals(3, IntAddition1()); assertIntEquals(14, IntAddition2()); assertLongEquals(3L, LongAddition()); + assertFloatEquals(3F, FloatAddition()); + assertDoubleEquals(3D, DoubleAddition()); assertIntEquals(4, IntSubtraction()); assertLongEquals(4L, LongSubtraction()); + assertFloatEquals(4F, FloatSubtraction()); + assertDoubleEquals(4D, DoubleSubtraction()); assertIntEquals(21, IntMultiplication()); assertLongEquals(21L, LongMultiplication()); + assertFloatEquals(21F, FloatMultiplication()); + assertDoubleEquals(21D, DoubleMultiplication()); assertIntEquals(2, IntDivision()); assertLongEquals(2L, LongDivision()); + assertFloatEquals(3.2F, FloatDivision()); + assertDoubleEquals(3.2D, DoubleDivision()); assertIntEquals(2, IntRemainder()); assertLongEquals(2L, LongRemainder()); + assertFloatEquals(0.5F, FloatRemainder()); + assertDoubleEquals(0.5D, DoubleRemainder()); assertIntEquals(4, ShlIntLong()); assertLongEquals(12L, ShlLongInt()); @@ -1259,6 +1586,24 @@ public class Main { assertFalse(CmpFloatGreaterThanNaN(arbitrary)); assertFalse(CmpDoubleLessThanNaN(arbitrary)); + Main main = new Main(); + assertIntEquals(1, main.smaliCmpLongConstants()); + assertIntEquals(-1, main.smaliCmpGtFloatConstants()); + assertIntEquals(-1, main.smaliCmpLtFloatConstants()); + assertIntEquals(-1, main.smaliCmpGtDoubleConstants()); + assertIntEquals(-1, main.smaliCmpLtDoubleConstants()); + + assertIntEquals(0, main.smaliCmpLongSameConstant()); + assertIntEquals(0, main.smaliCmpGtFloatSameConstant()); + assertIntEquals(0, main.smaliCmpLtFloatSameConstant()); + assertIntEquals(0, main.smaliCmpGtDoubleSameConstant()); + assertIntEquals(0, main.smaliCmpLtDoubleSameConstant()); + + assertIntEquals(1, main.smaliCmpGtFloatConstantWithNaN()); + assertIntEquals(-1, main.smaliCmpLtFloatConstantWithNaN()); + assertIntEquals(1, main.smaliCmpGtDoubleConstantWithNaN()); + assertIntEquals(-1, main.smaliCmpLtDoubleConstantWithNaN()); + assertIntEquals(33, ReturnInt33()); assertIntEquals(2147483647, ReturnIntMax()); assertIntEquals(0, ReturnInt0()); @@ -1275,4 +1620,10 @@ public class Main { assertDoubleEquals(34, ReturnDouble34()); assertDoubleEquals(99.25, ReturnDouble99P25()); } + + Main() throws ClassNotFoundException { + testCmp = Class.forName("TestCmp"); + } + + private Class<?> testCmp; } diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 31bb94cb8c..32bbc5b61d 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -391,6 +391,36 @@ public class Main { array[base + 1] = 1; } + /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (before) + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (after) + /// CHECK: Deoptimize + /// CHECK: Deoptimize + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + /// CHECK-NOT: BoundsCheck + /// CHECK: ArraySet + + static void constantIndexing10(int[] array, int base) { + // Offset hidden in incremented base. + array[base] = 1; + array[++base] = 2; + array[++base] = 3; + array[++base] = 4; + } + static void runAllConstantIndices() { int[] a1 = { 0 }; int[] a6 = { 0, 0, 0, 0, 0, 0 }; @@ -502,6 +532,12 @@ public class Main { a6[3] != 3 || a6[4] != 40 || a6[5] != 10) { System.out.println("constant indices 9 failed!"); } + + constantIndexing10(a6, 0); + if (a6[0] != 1 || a6[1] != 2 || a6[2] != 3 || + a6[3] != 4 || a6[4] != 40 || a6[5] != 10) { + System.out.println("constant indices 10 failed!"); + } } // A helper into which the actual throwing function should be inlined. diff --git a/test/529-checker-unresolved/build b/test/529-checker-unresolved/build index 8c3c4f8952..d85035b669 100644 --- a/test/529-checker-unresolved/build +++ b/test/529-checker-unresolved/build @@ -29,14 +29,12 @@ mv classes/UnresolvedInterface.class classes-ex mv classes/UnresolvedSuperClass.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java index 3a1b3fcf85..e215ab0309 100644 --- a/test/566-checker-codegen-select/src/Main.java +++ b/test/566-checker-codegen-select/src/Main.java @@ -20,13 +20,6 @@ public class Main { /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}] /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] - // Condition must be materialized on X86 because it would need too many - // registers otherwise. - /// CHECK-START-X86: long Main.$noinline$longSelect(long) disassembly (after) - /// CHECK: LessThanOrEqual - /// CHECK-NEXT: cmp - /// CHECK: Select - public long $noinline$longSelect(long param) { if (doThrow) { throw new Error(); } long val_true = longB; diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc index 4c58b39319..09e97ea751 100644 --- a/test/570-checker-osr/osr.cc +++ b/test/570-checker-osr/osr.cc @@ -41,7 +41,8 @@ class OsrVisitor : public StackVisitor { (m_name.compare("$noinline$returnDouble") == 0) || (m_name.compare("$noinline$returnLong") == 0) || (m_name.compare("$noinline$deopt") == 0) || - (m_name.compare("$noinline$inlineCache") == 0)) { + (m_name.compare("$noinline$inlineCache") == 0) || + (m_name.compare("$noinline$stackOverflow") == 0)) { const OatQuickMethodHeader* header = Runtime::Current()->GetJit()->GetCodeCache()->LookupOsrMethodHeader(m); if (header != nullptr && header == GetCurrentOatQuickMethodHeader()) { @@ -91,7 +92,8 @@ class ProfilingInfoVisitor : public StackVisitor { ArtMethod* m = GetMethod(); std::string m_name(m->GetName()); - if (m_name.compare("$noinline$inlineCache") == 0) { + if ((m_name.compare("$noinline$inlineCache") == 0) || + (m_name.compare("$noinline$stackOverflow") == 0)) { ProfilingInfo::Create(Thread::Current(), m, /* retry_allocation */ true); return false; } @@ -119,7 +121,8 @@ class OsrCheckVisitor : public StackVisitor { std::string m_name(m->GetName()); jit::Jit* jit = Runtime::Current()->GetJit(); - if (m_name.compare("$noinline$inlineCache") == 0 && jit != nullptr) { + if ((m_name.compare("$noinline$inlineCache") == 0) || + (m_name.compare("$noinline$stackOverflow") == 0)) { while (jit->GetCodeCache()->LookupOsrMethodHeader(m) == nullptr) { // Sleep to yield to the compiler thread. sleep(0); diff --git a/test/570-checker-osr/run b/test/570-checker-osr/run new file mode 100755 index 0000000000..24d69b4b3b --- /dev/null +++ b/test/570-checker-osr/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Ensure this test is not subject to code collection. +exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M diff --git a/test/570-checker-osr/src/Main.java b/test/570-checker-osr/src/Main.java index 828908a582..1142d49eed 100644 --- a/test/570-checker-osr/src/Main.java +++ b/test/570-checker-osr/src/Main.java @@ -40,6 +40,9 @@ public class Main { if ($noinline$inlineCache(new SubMain(), /* isSecondInvocation */ true) != SubMain.class) { throw new Error("Unexpected return value"); } + + $noinline$stackOverflow(new Main(), /* isSecondInvocation */ false); + $noinline$stackOverflow(new SubMain(), /* isSecondInvocation */ true); } public static int $noinline$returnInt() { @@ -129,7 +132,32 @@ public class Main { return Main.class; } - public static int[] array = new int[4]; + public void otherInlineCache() { + return; + } + + public static void $noinline$stackOverflow(Main m, boolean isSecondInvocation) { + // If we are running in non-JIT mode, or were unlucky enough to get this method + // already JITted, just return the expected value. + if (!ensureInInterpreter()) { + return; + } + + // We need a ProfilingInfo object to populate the 'otherInlineCache' call. + ensureHasProfilingInfo(); + + if (isSecondInvocation) { + // Ensure we have an OSR code and we jump to it. + while (!ensureInOsrCode()) {} + } + + for (int i = 0; i < (isSecondInvocation ? 10000000 : 1); ++i) { + // The first invocation of $noinline$stackOverflow will populate the inline + // cache with Main. The second invocation of the method, will see a SubMain + // and will therefore trigger deoptimization. + m.otherInlineCache(); + } + } public static native boolean ensureInInterpreter(); public static native boolean ensureInOsrCode(); @@ -147,4 +175,8 @@ class SubMain extends Main { public Main inlineCache() { return new SubMain(); } + + public void otherInlineCache() { + return; + } } diff --git a/test/570-checker-select/src/Main.java b/test/570-checker-select/src/Main.java index 8a4cf603af..59741d6d05 100644 --- a/test/570-checker-select/src/Main.java +++ b/test/570-checker-select/src/Main.java @@ -29,6 +29,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovnz/ne + /// CHECK-START-X86: int Main.BoolCond_IntVarVar(boolean, int, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + public static int BoolCond_IntVarVar(boolean cond, int x, int y) { return cond ? x : y; } @@ -46,6 +51,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovnz/ne + /// CHECK-START-X86: int Main.BoolCond_IntVarCst(boolean, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + public static int BoolCond_IntVarCst(boolean cond, int x) { return cond ? x : 1; } @@ -63,6 +73,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovnz/ne + /// CHECK-START-X86: int Main.BoolCond_IntCstVar(boolean, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + public static int BoolCond_IntCstVar(boolean cond, int y) { return cond ? 1 : y; } @@ -80,6 +95,12 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.BoolCond_LongVarVar(boolean, long, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long BoolCond_LongVarVar(boolean cond, long x, long y) { return cond ? x : y; } @@ -97,6 +118,12 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.BoolCond_LongVarCst(boolean, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long BoolCond_LongVarCst(boolean cond, long x) { return cond ? x : 1L; } @@ -114,6 +141,12 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.BoolCond_LongCstVar(boolean, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> ParameterValue + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long BoolCond_LongCstVar(boolean cond, long y) { return cond ? 1L : y; } @@ -168,6 +201,11 @@ public class Main { /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovle/ng + /// CHECK-START-X86: int Main.IntNonmatCond_IntVarVar(int, int, int, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovle/ng + public static int IntNonmatCond_IntVarVar(int a, int b, int x, int y) { return a > b ? x : y; } @@ -189,6 +227,11 @@ public class Main { /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK: cmovle/ng + /// CHECK-START-X86: int Main.IntMatCond_IntVarVar(int, int, int, int) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK: cmovle/ng + public static int IntMatCond_IntVarVar(int a, int b, int x, int y) { int result = (a > b ? x : y); return result + (a > b ? 0 : 1); @@ -208,6 +251,12 @@ public class Main { /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovle/ngq + /// CHECK-START-X86: long Main.IntNonmatCond_LongVarVar(int, int, long, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovle/ng + /// CHECK-NEXT: cmovle/ng + public static long IntNonmatCond_LongVarVar(int a, int b, long x, long y) { return a > b ? x : y; } @@ -232,6 +281,15 @@ public class Main { /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: cmovnz/neq + /// CHECK-START-X86: long Main.IntMatCond_LongVarVar(int, int, long, long) disassembly (after) + /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK-NEXT: cmovle/ng + /// CHECK-NEXT: cmovle/ng + /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK: cmovnz/ne + /// CHECK-NEXT: cmovnz/ne + public static long IntMatCond_LongVarVar(int a, int b, long x, long y) { long result = (a > b ? x : y); return result + (a > b ? 0L : 1L); diff --git a/test/574-irreducible-and-constant-area/expected.txt b/test/574-irreducible-and-constant-area/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/574-irreducible-and-constant-area/expected.txt diff --git a/test/574-irreducible-and-constant-area/info.txt b/test/574-irreducible-and-constant-area/info.txt new file mode 100644 index 0000000000..e957a5ae6b --- /dev/null +++ b/test/574-irreducible-and-constant-area/info.txt @@ -0,0 +1,3 @@ +Regression test for intrinsics on x86, which used to wrongly assume +a HInvokeStaticOrDirect must have a special input (does not apply for irreducible +loops). diff --git a/test/574-irreducible-and-constant-area/run b/test/574-irreducible-and-constant-area/run new file mode 100755 index 0000000000..ffdbcc9cf2 --- /dev/null +++ b/test/574-irreducible-and-constant-area/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Don't do relocation, as this affects this test. +exec ${RUN} "$@" --no-relocate diff --git a/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali new file mode 100644 index 0000000000..d7d43466d1 --- /dev/null +++ b/test/574-irreducible-and-constant-area/smali/IrreducibleLoop.smali @@ -0,0 +1,35 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LIrreducibleLoop; + +.super Ljava/lang/Object; + +.method public static simpleLoop(I)I + .registers 5 + const/16 v0, 42 + const/16 v1, 42 + const-wide/high16 v2, 0x4000000000000000L + if-eq p0, v0, :other_loop_entry + :loop_entry + invoke-static {v1, v1}, LMain;->$inline$foo(FF)V + invoke-static {v2, v3, v2, v3}, LMain;->$inline$foo(DD)V + if-ne p0, v0, :exit + add-int v0, v0, v0 + :other_loop_entry + add-int v0, v0, v0 + goto :loop_entry + :exit + return v0 +.end method diff --git a/test/574-irreducible-and-constant-area/src/Main.java b/test/574-irreducible-and-constant-area/src/Main.java new file mode 100644 index 0000000000..3cdd92456c --- /dev/null +++ b/test/574-irreducible-and-constant-area/src/Main.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("IrreducibleLoop"); + Method m = c.getMethod("simpleLoop", int.class); + Object[] arguments = { 42 }; + m.invoke(null, arguments); + } + + public static void $inline$foo(float a, float b) { + Math.abs(a); + Math.max(a, b); + Math.min(a, b); + } + + public static void $inline$foo(double a, double b) { + Math.abs(a); + Math.max(a, b); + Math.min(a, b); + } +} diff --git a/test/575-checker-isnan/expected.txt b/test/575-checker-isnan/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/575-checker-isnan/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/575-checker-isnan/info.txt b/test/575-checker-isnan/info.txt new file mode 100644 index 0000000000..5c48a6a877 --- /dev/null +++ b/test/575-checker-isnan/info.txt @@ -0,0 +1 @@ +Unit test for float/double isNaN() operation. diff --git a/test/575-checker-isnan/src/Main.java b/test/575-checker-isnan/src/Main.java new file mode 100644 index 0000000000..cc71e5e27d --- /dev/null +++ b/test/575-checker-isnan/src/Main.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (before) + /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (after) + /// CHECK-DAG: <<Result:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + private static boolean isNaN32(float x) { + return Float.isNaN(x); + } + + /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (before) + /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (after) + /// CHECK-DAG: <<Result:z\d+>> NotEqual + /// CHECK-DAG: Return [<<Result>>] + // + /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + private static boolean isNaN64(double x) { + return Double.isNaN(x); + } + + public static void main(String args[]) { + // A few distinct numbers. + expectFalse(isNaN32(Float.NEGATIVE_INFINITY)); + expectFalse(isNaN32(-1.0f)); + expectFalse(isNaN32(-0.0f)); + expectFalse(isNaN32(0.0f)); + expectFalse(isNaN32(1.0f)); + expectFalse(isNaN32(Float.POSITIVE_INFINITY)); + + // A few distinct subnormal numbers. + expectFalse(isNaN32(Float.intBitsToFloat(0x00400000))); + expectFalse(isNaN32(Float.intBitsToFloat(0x80400000))); + expectFalse(isNaN32(Float.intBitsToFloat(0x00000001))); + expectFalse(isNaN32(Float.intBitsToFloat(0x80000001))); + + // A few NaN numbers. + expectTrue(isNaN32(Float.NaN)); + expectTrue(isNaN32(0.0f / 0.0f)); + expectTrue(isNaN32((float)Math.sqrt(-1.0f))); + float[] fvals = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fvals.length; i++) { + expectTrue(isNaN32(fvals[i])); + } + + // A few distinct numbers. + expectFalse(isNaN64(Double.NEGATIVE_INFINITY)); + expectFalse(isNaN32(-1.0f)); + expectFalse(isNaN64(-0.0d)); + expectFalse(isNaN64(0.0d)); + expectFalse(isNaN64(1.0d)); + expectFalse(isNaN64(Double.POSITIVE_INFINITY)); + + // A few distinct subnormal numbers. + expectFalse(isNaN64(Double.longBitsToDouble(0x0008000000000000l))); + expectFalse(isNaN64(Double.longBitsToDouble(0x8008000000000000l))); + expectFalse(isNaN64(Double.longBitsToDouble(0x0000000000000001l))); + expectFalse(isNaN64(Double.longBitsToDouble(0x8000000000000001l))); + + // A few NaN numbers. + expectTrue(isNaN64(Double.NaN)); + expectTrue(isNaN64(0.0d / 0.0d)); + expectTrue(isNaN64(Math.sqrt(-1.0d))); + double[] dvals = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dvals.length; i++) { + expectTrue(isNaN64(dvals[i])); + } + + System.out.println("passed"); + } + + private static void expectTrue(boolean value) { + if (!value) { + throw new Error("Expected True"); + } + } + + private static void expectFalse(boolean value) { + if (value) { + throw new Error("Expected False"); + } + } +} diff --git a/test/575-checker-string-init-alias/expected.txt b/test/575-checker-string-init-alias/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/575-checker-string-init-alias/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/575-checker-string-init-alias/info.txt b/test/575-checker-string-init-alias/info.txt new file mode 100644 index 0000000000..a91ea645e7 --- /dev/null +++ b/test/575-checker-string-init-alias/info.txt @@ -0,0 +1,2 @@ +Test for the String.<init> change and deoptimization: make +sure the compiler knows how to handle dex aliases. diff --git a/test/575-checker-string-init-alias/smali/TestCase.smali b/test/575-checker-string-init-alias/smali/TestCase.smali new file mode 100644 index 0000000000..ff04b278a4 --- /dev/null +++ b/test/575-checker-string-init-alias/smali/TestCase.smali @@ -0,0 +1,72 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.field public static staticField:Ljava/lang/String; + +## CHECK-START: void TestCase.testNoAlias(int[], java.lang.String) register (after) +## CHECK: <<Null:l\d+>> NullConstant +## CHECK: Deoptimize env:[[<<Null>>,{{.*]]}} +## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> +.method public static testNoAlias([ILjava/lang/String;)V + .registers 6 + const v1, 0 + const v2, 1 + new-instance v0, Ljava/lang/String; + + # Will deoptimize. + aget v3, p0, v1 + + # Check that we're being executed by the interpreter. + invoke-static {}, LMain;->assertIsInterpreted()V + + invoke-direct {v0, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V + + sput-object v0, LTestCase;->staticField:Ljava/lang/String; + + # Will throw AIOOBE. + aget v3, p0, v2 + + return-void +.end method + +## CHECK-START: void TestCase.testAlias(int[], java.lang.String) register (after) +## CHECK: <<New:l\d+>> NewInstance +## CHECK: Deoptimize env:[[<<New>>,<<New>>,{{.*]]}} +## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init> +.method public static testAlias([ILjava/lang/String;)V + .registers 7 + const v2, 0 + const v3, 1 + new-instance v0, Ljava/lang/String; + move-object v1, v0 + + # Will deoptimize. + aget v4, p0, v2 + + # Check that we're being executed by the interpreter. + invoke-static {}, LMain;->assertIsInterpreted()V + + invoke-direct {v1, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V + + sput-object v1, LTestCase;->staticField:Ljava/lang/String; + + # Will throw AIOOBE. + aget v4, p0, v3 + + return-void +.end method diff --git a/test/575-checker-string-init-alias/src/Main.java b/test/575-checker-string-init-alias/src/Main.java new file mode 100644 index 0000000000..1ab320748a --- /dev/null +++ b/test/575-checker-string-init-alias/src/Main.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +public class Main { + // Workaround for b/18051191. + class Inner {} + + public static native void assertIsInterpreted(); + + private static void assertEqual(String expected, String actual) { + if (!expected.equals(actual)) { + throw new Error("Assertion failed: " + expected + " != " + actual); + } + } + + public static void main(String[] args) throws Throwable { + System.loadLibrary(args[0]); + Class<?> c = Class.forName("TestCase"); + int[] array = new int[1]; + + { + Method m = c.getMethod("testNoAlias", int[].class, String.class); + try { + m.invoke(null, new Object[] { array , "foo" }); + throw new Error("Expected AIOOBE"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) { + throw new Error("Expected AIOOBE"); + } + // Ignore + } + Field field = c.getField("staticField"); + assertEqual("foo", (String)field.get(null)); + } + + { + Method m = c.getMethod("testAlias", int[].class, String.class); + try { + m.invoke(null, new Object[] { array, "bar" }); + throw new Error("Expected AIOOBE"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) { + throw new Error("Expected AIOOBE"); + } + // Ignore + } + Field field = c.getField("staticField"); + assertEqual("bar", (String)field.get(null)); + } + } +} diff --git a/test/576-polymorphic-inlining/expected.txt b/test/576-polymorphic-inlining/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/576-polymorphic-inlining/expected.txt diff --git a/test/576-polymorphic-inlining/info.txt b/test/576-polymorphic-inlining/info.txt new file mode 100644 index 0000000000..b3ef0c8fba --- /dev/null +++ b/test/576-polymorphic-inlining/info.txt @@ -0,0 +1 @@ +Test for polymorphic inlining. diff --git a/test/576-polymorphic-inlining/src/Main.java b/test/576-polymorphic-inlining/src/Main.java new file mode 100644 index 0000000000..d8d09aff87 --- /dev/null +++ b/test/576-polymorphic-inlining/src/Main.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + for (int i = 0; i < 20000; ++i) { + $noinline$testVoid(new Main()); + $noinline$testVoid(new SubMain()); + $noinline$testVoid(new SubSubMain()); + + $noinline$testWithReturnValue(new Main()); + $noinline$testWithReturnValue(new SubMain()); + $noinline$testWithReturnValue(new SubSubMain()); + + $noinline$testWithBackEdge(new Main()); + $noinline$testWithBackEdge(new SubMain()); + $noinline$testWithBackEdge(new SubSubMain()); + } + } + + public static void assertIdentical(Object expected, Object actual) { + if (expected != actual) { + throw new Error("Expected " + expected + ", got " + actual); + } + } + + public static void $noinline$testVoid(Main m) { + if (doThrow) throw new Error(""); + m.willInlineVoid(); + m.willOnlyInlineForMainVoid(); + } + + public static void $noinline$testWithReturnValue(Main m) { + if (doThrow) throw new Error(""); + assertIdentical(m.getClass(), m.willInlineWithReturnValue()); + assertIdentical(m.getClass(), m.willOnlyInlineForMainWithReturnValue()); + } + + public static void $noinline$testWithBackEdge(Main m) { + if (doThrow) throw new Error(""); + for (int i = 0; i < 10; ++i) { + m.willInlineVoid(); + } + for (int i = 0; i < 10; ++i) { + m.willOnlyInlineForMainVoid(); + } + } + + public void willInlineVoid() { + } + + public void willOnlyInlineForMainVoid() { + } + + public Class willInlineWithReturnValue() { + return Main.class; + } + + public Class willOnlyInlineForMainWithReturnValue() { + return Main.class; + } + public static boolean doThrow; +} + +class SubMain extends Main { + public void willOnlyInlineForMainVoid() { + if (doThrow) throw new Error(""); + } + + public void willInlineVoid() { + } + + public Class willInlineWithReturnValue() { + return SubMain.class; + } + + public Class willOnlyInlineForMainWithReturnValue() { + return SubMain.class; + } +} + +class SubSubMain extends SubMain { + public Class willInlineWithReturnValue() { + return SubSubMain.class; + } + + public Class willOnlyInlineForMainWithReturnValue() { + return SubSubMain.class; + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index b3560b634b..364be59919 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -42,8 +42,7 @@ TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES := ifeq ($(ANDROID_COMPILE_WITH_JACK),true) TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(JACK) \ - $(JILL_JAR) + $(JACK) TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES += setup-jack-server endif @@ -72,8 +71,8 @@ $$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(TARGET_JACK_CLASSPATH_DEPE DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \ - JILL_JAR=$(abspath $(JILL_JAR)) \ $(LOCAL_PATH)/run-test $$(PRIVATE_RUN_TEST_OPTIONS) --output-path $$(abspath $$(dir $$@)) $(1) $(hide) touch $$@ @@ -962,8 +961,8 @@ $$(run_test_rule_name): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(HOST_OUT_EXECUTABLES DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \ - JILL_JAR=$(abspath $(JILL_JAR)) \ art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(12) \ && $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ diff --git a/test/run-test b/test/run-test index faa597e1ca..f1875d71a5 100755 --- a/test/run-test +++ b/test/run-test @@ -88,13 +88,7 @@ if [ -z "$JACK_CLASSPATH" ]; then export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack" fi -# If JILL_JAR is not set, assume it is located in the prebuilts directory. -if [ -z "$JILL_JAR" ]; then - export JILL_JAR="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/jill.jar" -fi - export JACK="$JACK -g -cp $JACK_CLASSPATH" -export JILL="java -jar $JILL_JAR" info="info.txt" build="build" diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt index da5225c066..d9b26bcf58 100644 --- a/tools/ahat/README.txt +++ b/tools/ahat/README.txt @@ -78,6 +78,7 @@ Things to move to perflib: Release History: 0.4 Pending + Annotate char[] objects with their string values. Show registered native allocations for heap dumps that support it. 0.3 Dec 15, 2015 diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java index 8b7f9ea41a..d7b64e2afd 100644 --- a/tools/ahat/src/InstanceUtils.java +++ b/tools/ahat/src/InstanceUtils.java @@ -76,11 +76,15 @@ class InstanceUtils { * If maxChars is negative, the returned string is not truncated. */ public static String asString(Instance inst, int maxChars) { - if (!isInstanceOfClass(inst, "java.lang.String")) { - return null; + // The inst object could either be a java.lang.String or a char[]. If it + // is a char[], use that directly as the value, otherwise use the value + // field of the string object. The field accesses for count and offset + // later on will work okay regardless of what type the inst object is. + Object value = inst; + if (isInstanceOfClass(inst, "java.lang.String")) { + value = getField(inst, "value"); } - Object value = getField(inst, "value"); if (!(value instanceof ArrayInstance)) { return null; } diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java index 701d60ef1f..d61a98da54 100644 --- a/tools/ahat/test-dump/Main.java +++ b/tools/ahat/test-dump/Main.java @@ -35,6 +35,7 @@ public class Main { // class and reading the desired field. public static class DumpedStuff { public String basicString = "hello, world"; + public char[] charArray = "char thing".toCharArray(); public String nullString = null; public Object anObject = new Object(); public ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); diff --git a/tools/ahat/test/InstanceUtilsTest.java b/tools/ahat/test/InstanceUtilsTest.java index 32f48ce560..59b1c90e10 100644 --- a/tools/ahat/test/InstanceUtilsTest.java +++ b/tools/ahat/test/InstanceUtilsTest.java @@ -32,6 +32,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArray() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str)); + } + + @Test public void asStringTruncated() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -39,6 +46,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayTruncated() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char ", InstanceUtils.asString(str, 5)); + } + + @Test public void asStringExactMax() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -46,6 +60,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayExactMax() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str, 10)); + } + + @Test public void asStringNotTruncated() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -53,6 +74,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayNotTruncated() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str, 50)); + } + + @Test public void asStringNegativeMax() throws IOException { TestDump dump = TestDump.getTestDump(); Instance str = (Instance)dump.getDumpedThing("basicString"); @@ -60,6 +88,13 @@ public class InstanceUtilsTest { } @Test + public void asStringCharArrayNegativeMax() throws IOException { + TestDump dump = TestDump.getTestDump(); + Instance str = (Instance)dump.getDumpedThing("charArray"); + assertEquals("char thing", InstanceUtils.asString(str, -3)); + } + + @Test public void asStringNull() throws IOException { TestDump dump = TestDump.getTestDump(); Instance obj = (Instance)dump.getDumpedThing("nullString"); diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 44206df089..e6394a999f 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -272,5 +272,10 @@ "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndNoSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNullArguments"] +}, +{ + description: "Only work with --mode=activity", + result: EXEC_FAILED, + names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ] } ] diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index d8ef9baa64..19a61dc8cb 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -24,19 +24,6 @@ bug: 26155567 }, { - description: "TimeoutException on host-{x86,x86-64}-concurrent-collector", - result: EXEC_FAILED, - modes: [host], - names: ["libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushEnabled", - "libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushDisabled", - "libcore.java.util.zip.GZIPOutputStreamTest#testSyncFlushEnabled", - "libcore.java.util.zip.OldAndroidGZIPStreamTest#testGZIPStream", - "libcore.java.util.zip.OldAndroidZipStreamTest#testZipStream", - "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries", - "libcore.java.util.zip.ZipInputStreamTest#testLongMessage"], - bug: 26507762 -}, -{ description: "TimeoutException on hammerhead-concurrent-collector", result: EXEC_FAILED, modes: [device], diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index f29e51f047..e4af9fa0d7 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -20,9 +20,9 @@ if [ ! -d libcore ]; then fi # Jar containing all the tests. -test_jar=${OUT_DIR-out}/host/linux-x86/framework/apache-harmony-jdwp-tests-hostdex.jar +test_jack=${OUT_DIR-out}/host/common/obj/JAVA_LIBRARIES/apache-harmony-jdwp-tests-hostdex_intermediates/classes.jack -if [ ! -f $test_jar ]; then +if [ ! -f $test_jack ]; then echo "Before running, you must build jdwp tests and vogar:" \ "make apache-harmony-jdwp-tests-hostdex vogar vogar.jar" exit 1 @@ -117,6 +117,9 @@ if [[ $verbose == "yes" ]]; then art_debugee="$art_debugee -verbose:jdwp" fi +# Use Jack with "1.8" configuration. +export JACK_VERSION=`basename prebuilts/sdk/tools/jacks/*ALPHA* | sed 's/^jack-//' | sed 's/.jar$//'` + # Run the tests using vogar. vogar $vm_command \ $vm_args \ @@ -129,7 +132,8 @@ vogar $vm_command \ --vm-arg -Djpda.settings.syncPort=34016 \ --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \ --vm-arg -Djpda.settings.debuggeeJavaPath="$art_debugee $image $debuggee_args" \ - --classpath $test_jar \ + --classpath $test_jack \ + --toolchain jack --language JN \ --vm-arg -Xcompiler-option --vm-arg --debuggable \ $test diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index d5b89897e5..45b60dc647 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -34,6 +34,9 @@ adb shell getprop echo -e "${green}Uptime${nc}" adb shell uptime +echo -e "${green}Battery info${nc}" +adb shell dumpsys battery + echo -e "${green}Kill stalled dalvikvm processes${nc}" processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') for i in $processes; do adb shell kill -9 $i; done |