diff options
243 files changed, 7746 insertions, 3804 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..b3832ac3b6 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 \ @@ -251,6 +258,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/elf_writer_test.cc \ compiler/image_test.cc \ compiler/jni/jni_compiler_test.cc \ + compiler/linker/multi_oat_relative_patcher_test.cc \ compiler/linker/output_stream_test.cc \ compiler/oat_test.cc \ compiler/optimizing/bounds_check_elimination_test.cc \ @@ -270,7 +278,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 \ @@ -571,9 +578,6 @@ define define-art-gtest LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64) LOCAL_MULTILIB := both LOCAL_CLANG_CFLAGS += -Wno-used-but-marked-unused -Wno-deprecated -Wno-missing-noreturn # gtest issue - # clang fails to compile art/runtime/arch/stub_test.cc for arm64 without -O1 - # b/26275713 - LOCAL_CLANG_CFLAGS_arm64 += -O1 include $$(BUILD_EXECUTABLE) library_path := 2nd_library_path := 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 159e9cfb5e..2cbafea573 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -61,6 +61,7 @@ LIBART_COMPILER_SRC_FILES := \ driver/dex_compilation_unit.cc \ linker/buffered_output_stream.cc \ linker/file_output_stream.cc \ + linker/multi_oat_relative_patcher.cc \ linker/output_stream.cc \ linker/vector_output_stream.cc \ linker/relative_patcher.cc \ @@ -107,8 +108,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 \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index e4bfac9ee7..239bc590e9 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -194,16 +194,15 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe kind, isa, instruction_set_features_.get(), - true, + /* boot_image */ true, GetImageClasses(), GetCompiledClasses(), GetCompiledMethods(), - 2, - true, - true, + /* thread_count */ 2, + /* dump_stats */ true, + /* dump_passes */ true, timer_.get(), - -1, - /* dex_to_oat_map */ nullptr, + /* swap_fd */ -1, GetProfileCompilationInfo())); // We typically don't generate an image in unit tests, disable this optimization by default. compiler_driver_->SetSupportBootImageFixup(false); diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc index 0cd41bbf4c..6c6c9cfb1e 100644 --- a/compiler/dex/quick/quick_cfi_test.cc +++ b/compiler/dex/quick/quick_cfi_test.cc @@ -84,17 +84,16 @@ class QuickCFITest : public CFITest { Compiler::kQuick, isa, isa_features.get(), - false, - nullptr, - nullptr, - nullptr, - 0, - false, - false, - 0, - -1, - nullptr, - nullptr); + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 0, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr); ClassLinker* linker = nullptr; CompilationUnit cu(&pool, isa, &driver, linker); DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } }; // NOLINT diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc index efdc333261..ff0ecea94c 100644 --- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc +++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc @@ -64,18 +64,17 @@ class QuickAssembleX86TestBase : public testing::Test { method_inliner_map_.get(), Compiler::kQuick, isa_, - nullptr, - false, - nullptr, - nullptr, - nullptr, - 0, - false, - false, - 0, - -1, - nullptr, - nullptr)); + /* instruction_set_features*/ nullptr, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 0, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr)); cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr)); DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>( cu_->arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index 2e2d1f99f3..0695cb56b3 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -32,19 +32,19 @@ TEST(CompiledMethodStorage, Deduplicate) { CompilerDriver driver(&compiler_options, &verification_results, &method_inliner_map, - Compiler::kOptimizing, kNone, - nullptr, - false, - nullptr, - nullptr, - nullptr, - 1u, - false, - false, - nullptr, - -1, - nullptr, - nullptr); + Compiler::kOptimizing, + /* instruction_set_ */ kNone, + /* instruction_set_features */ nullptr, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 1u, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr); CompiledMethodStorage* storage = driver.GetCompiledMethodStorage(); ASSERT_TRUE(storage->DedupeEnabled()); // The default. 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 670fe94988..a9fec30bfe 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -342,12 +342,15 @@ CompilerDriver::CompilerDriver( Compiler::Kind compiler_kind, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, - bool boot_image, std::unordered_set<std::string>* image_classes, + bool boot_image, + std::unordered_set<std::string>* image_classes, std::unordered_set<std::string>* compiled_classes, std::unordered_set<std::string>* compiled_methods, - size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer, int swap_fd, - const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map, + size_t thread_count, + bool dump_stats, + bool dump_passes, + CumulativeLogger* timer, + int swap_fd, const ProfileCompilationInfo* profile_compilation_info) : compiler_options_(compiler_options), verification_results_(verification_results), @@ -374,11 +377,9 @@ CompilerDriver::CompilerDriver( compiler_context_(nullptr), support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64), dex_files_for_oat_file_(nullptr), - dex_file_oat_filename_map_(dex_to_oat_map), 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 +1079,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); @@ -1678,12 +1677,6 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType use_dex_cache = true; } } - if (!use_dex_cache && IsBootImage()) { - if (!AreInSameOatFile(&(const_cast<mirror::Class*>(referrer_class)->GetDexFile()), - &declaring_class->GetDexFile())) { - use_dex_cache = true; - } - } // The method is defined not within this dex file. We need a dex cache slot within the current // dex file or direct pointers. bool must_use_direct_pointers = false; @@ -2493,6 +2486,7 @@ void CompilerDriver::Compile(jobject class_loader, parallel_thread_pool_.get(), parallel_thread_count_, timings); + Runtime::Current()->ReclaimArenaPoolMemory(); } VLOG(compiler) << "Compile: " << GetMemoryUsageString(false); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 5e35cbb309..42a5bc15e4 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -94,9 +94,11 @@ class CompilerDriver { bool boot_image, std::unordered_set<std::string>* image_classes, std::unordered_set<std::string>* compiled_classes, std::unordered_set<std::string>* compiled_methods, - size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer, int swap_fd, - const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map, + size_t thread_count, + bool dump_stats, + bool dump_passes, + CumulativeLogger* timer, + int swap_fd, const ProfileCompilationInfo* profile_compilation_info); ~CompilerDriver(); @@ -113,20 +115,6 @@ class CompilerDriver { : ArrayRef<const DexFile* const>(); } - // Are the given dex files compiled into the same oat file? Should only be called after - // GetDexFilesForOatFile, as the conservative answer (when we don't have a map) is true. - bool AreInSameOatFile(const DexFile* d1, const DexFile* d2) { - if (dex_file_oat_filename_map_ == nullptr) { - // TODO: Check for this wrt/ apps and boot image calls. - return true; - } - auto it1 = dex_file_oat_filename_map_->find(d1); - DCHECK(it1 != dex_file_oat_filename_map_->end()); - auto it2 = dex_file_oat_filename_map_->find(d2); - DCHECK(it2 != dex_file_oat_filename_map_->end()); - return it1->second == it2->second; - } - void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) @@ -138,6 +126,7 @@ class CompilerDriver { REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_); VerificationResults* GetVerificationResults() const { + DCHECK(Runtime::Current()->IsAotCompiler()); return verification_results_; } @@ -700,9 +689,6 @@ class CompilerDriver { // List of dex files that will be stored in the oat file. const std::vector<const DexFile*>* dex_files_for_oat_file_; - // Map from dex files to the oat file (name) they will be compiled into. - const std::unordered_map<const DexFile*, const char*>* dex_file_oat_filename_map_; - CompiledMethodStorage compiled_method_storage_; // Info for profile guided compilation. diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index b673eeb3b6..f7da609e5d 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -86,12 +86,24 @@ class ElfBuilder FINAL { // Base class of all sections. class Section : public OutputStream { public: - Section(ElfBuilder<ElfTypes>* owner, const std::string& name, - Elf_Word type, Elf_Word flags, const Section* link, - Elf_Word info, Elf_Word align, Elf_Word entsize) - : OutputStream(name), owner_(owner), header_(), - section_index_(0), name_(name), link_(link), - started_(false), finished_(false), phdr_flags_(PF_R), phdr_type_(0) { + Section(ElfBuilder<ElfTypes>* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + const Section* link, + Elf_Word info, + Elf_Word align, + Elf_Word entsize) + : OutputStream(name), + owner_(owner), + header_(), + section_index_(0), + name_(name), + link_(link), + started_(false), + finished_(false), + phdr_flags_(PF_R), + phdr_type_(0) { DCHECK_GE(align, 1u); header_.sh_type = type; header_.sh_flags = flags; @@ -228,12 +240,84 @@ class ElfBuilder FINAL { DISALLOW_COPY_AND_ASSIGN(Section); }; - // Writer of .dynstr .strtab and .shstrtab sections. + class CachedSection : public Section { + public: + CachedSection(ElfBuilder<ElfTypes>* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + const Section* link, + Elf_Word info, + Elf_Word align, + Elf_Word entsize) + : Section(owner, name, type, flags, link, info, align, entsize), cache_() { } + + Elf_Word Add(const void* data, size_t length) { + Elf_Word offset = cache_.size(); + const uint8_t* d = reinterpret_cast<const uint8_t*>(data); + cache_.insert(cache_.end(), d, d + length); + return offset; + } + + Elf_Word GetCacheSize() { + return cache_.size(); + } + + void Write() { + this->WriteFully(cache_.data(), cache_.size()); + cache_.clear(); + cache_.shrink_to_fit(); + } + + void WriteCachedSection() { + this->Start(); + Write(); + this->End(); + } + + private: + std::vector<uint8_t> cache_; + }; + + // Writer of .dynstr section. + class CachedStringSection FINAL : public CachedSection { + public: + CachedStringSection(ElfBuilder<ElfTypes>* owner, + const std::string& name, + Elf_Word flags, + Elf_Word align) + : CachedSection(owner, + name, + SHT_STRTAB, + flags, + /* link */ nullptr, + /* info */ 0, + align, + /* entsize */ 0) { } + + Elf_Word Add(const std::string& name) { + if (CachedSection::GetCacheSize() == 0u) { + DCHECK(name.empty()); + } + return CachedSection::Add(name.c_str(), name.length() + 1); + } + }; + + // Writer of .strtab and .shstrtab sections. class StringSection FINAL : public Section { public: - StringSection(ElfBuilder<ElfTypes>* owner, const std::string& name, - Elf_Word flags, Elf_Word align) - : Section(owner, name, SHT_STRTAB, flags, nullptr, 0, align, 0), + StringSection(ElfBuilder<ElfTypes>* owner, + const std::string& name, + Elf_Word flags, + Elf_Word align) + : Section(owner, + name, + SHT_STRTAB, + flags, + /* link */ nullptr, + /* info */ 0, + align, + /* entsize */ 0), current_offset_(0) { } @@ -252,42 +336,60 @@ class ElfBuilder FINAL { }; // Writer of .dynsym and .symtab sections. - class SymbolSection FINAL : public Section { + class SymbolSection FINAL : public CachedSection { public: - SymbolSection(ElfBuilder<ElfTypes>* owner, const std::string& name, - Elf_Word type, Elf_Word flags, StringSection* strtab) - : Section(owner, name, type, flags, strtab, 0, - sizeof(Elf_Off), sizeof(Elf_Sym)) { + SymbolSection(ElfBuilder<ElfTypes>* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + Section* strtab) + : CachedSection(owner, + name, + type, + flags, + strtab, + /* info */ 0, + sizeof(Elf_Off), + sizeof(Elf_Sym)) { + // The symbol table always has to start with NULL symbol. + Elf_Sym null_symbol = Elf_Sym(); + CachedSection::Add(&null_symbol, sizeof(null_symbol)); } // Buffer symbol for this section. It will be written later. // If the symbol's section is null, it will be considered absolute (SHN_ABS). // (we use this in JIT to reference code which is stored outside the debug ELF file) - void Add(Elf_Word name, const Section* section, - Elf_Addr addr, bool is_relative, Elf_Word size, - uint8_t binding, uint8_t type, uint8_t other = 0) { + void Add(Elf_Word name, + const Section* section, + Elf_Addr addr, + bool is_relative, + Elf_Word size, + uint8_t binding, + uint8_t type, + uint8_t other = 0) { + DCHECK(section != nullptr || !is_relative); + Elf_Addr abs_addr = addr + (is_relative ? section->GetAddress() : 0); + Elf_Word section_index = + (section != nullptr) ? section->GetSectionIndex() : static_cast<Elf_Word>(SHN_ABS); + Add(name, section_index, abs_addr, size, binding, type, other); + } + + void Add(Elf_Word name, + Elf_Word section_index, + Elf_Addr addr, + Elf_Word size, + uint8_t binding, + uint8_t type, + uint8_t other = 0) { Elf_Sym sym = Elf_Sym(); sym.st_name = name; - sym.st_value = addr + (is_relative ? section->GetAddress() : 0); + sym.st_value = addr; sym.st_size = size; sym.st_other = other; - sym.st_shndx = (section != nullptr ? section->GetSectionIndex() - : static_cast<Elf_Word>(SHN_ABS)); + sym.st_shndx = section_index; sym.st_info = (binding << 4) + (type & 0xf); - symbols_.push_back(sym); - } - - void Write() { - // The symbol table always has to start with NULL symbol. - Elf_Sym null_symbol = Elf_Sym(); - this->WriteFully(&null_symbol, sizeof(null_symbol)); - this->WriteFully(symbols_.data(), symbols_.size() * sizeof(symbols_[0])); - symbols_.clear(); - symbols_.shrink_to_fit(); + CachedSection::Add(&sym, sizeof(sym)); } - - private: - std::vector<Elf_Sym> symbols_; }; ElfBuilder(InstructionSet isa, OutputStream* output) @@ -309,6 +411,8 @@ class ElfBuilder FINAL { debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0), shstrtab_(this, ".shstrtab", 0, 1), started_(false), + write_program_headers_(false), + loaded_size_(0u), virtual_address_(0) { text_.phdr_flags_ = PF_R | PF_X; bss_.phdr_flags_ = PF_R | PF_W; @@ -380,6 +484,14 @@ class ElfBuilder FINAL { void End() { DCHECK(started_); + // Note: loaded_size_ == 0 for tests that don't write .rodata, .text, .bss, + // .dynstr, dynsym, .hash and .dynamic. These tests should not read loaded_size_. + // TODO: Either refactor the .eh_frame creation so that it counts towards loaded_size_, + // or remove all support for .eh_frame. (The currently unused .eh_frame counts towards + // the virtual_address_ but we don't consider it for loaded_size_.) + CHECK(loaded_size_ == 0 || loaded_size_ == RoundUp(virtual_address_, kPageSize)) + << loaded_size_ << " " << virtual_address_; + // Write section names and finish the section headers. shstrtab_.Start(); shstrtab_.Write(""); @@ -434,45 +546,58 @@ class ElfBuilder FINAL { // information like the address and size of .rodata and .text. // It also contains other metadata like the SONAME. // The .dynamic section is found using the PT_DYNAMIC program header. - void WriteDynamicSection(const std::string& elf_file_path) { + void PrepareDynamicSection(const std::string& elf_file_path, + Elf_Word rodata_size, + Elf_Word text_size, + Elf_Word bss_size) { std::string soname(elf_file_path); size_t directory_separator_pos = soname.rfind('/'); if (directory_separator_pos != std::string::npos) { soname = soname.substr(directory_separator_pos + 1); } - dynstr_.Start(); - dynstr_.Write(""); // dynstr should start with empty string. - dynsym_.Add(dynstr_.Write("oatdata"), &rodata_, 0, true, - rodata_.GetSize(), STB_GLOBAL, STT_OBJECT); - if (text_.GetSize() != 0u) { - dynsym_.Add(dynstr_.Write("oatexec"), &text_, 0, true, - text_.GetSize(), STB_GLOBAL, STT_OBJECT); - dynsym_.Add(dynstr_.Write("oatlastword"), &text_, text_.GetSize() - 4, - true, 4, STB_GLOBAL, STT_OBJECT); - } else if (rodata_.GetSize() != 0) { + // Calculate addresses of .text, .bss and .dynstr. + DCHECK_EQ(rodata_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); + DCHECK_EQ(text_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); + DCHECK_EQ(bss_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); + DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); + Elf_Word rodata_address = rodata_.GetAddress(); + Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize); + Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize); + Elf_Word dynstr_address = RoundUp(bss_address + bss_size, kPageSize); + + // Cache .dynstr, .dynsym and .hash data. + dynstr_.Add(""); // dynstr should start with empty string. + Elf_Word rodata_index = rodata_.GetSectionIndex(); + Elf_Word oatdata = dynstr_.Add("oatdata"); + dynsym_.Add(oatdata, rodata_index, rodata_address, rodata_size, STB_GLOBAL, STT_OBJECT); + if (text_size != 0u) { + Elf_Word text_index = rodata_index + 1u; + Elf_Word oatexec = dynstr_.Add("oatexec"); + dynsym_.Add(oatexec, text_index, text_address, text_size, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword = dynstr_.Add("oatlastword"); + Elf_Word oatlastword_address = text_address + text_size - 4; + dynsym_.Add(oatlastword, text_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); + } else if (rodata_size != 0) { // rodata_ can be size 0 for dwarf_test. - dynsym_.Add(dynstr_.Write("oatlastword"), &rodata_, rodata_.GetSize() - 4, - true, 4, STB_GLOBAL, STT_OBJECT); + Elf_Word oatlastword = dynstr_.Add("oatlastword"); + Elf_Word oatlastword_address = rodata_address + rodata_size - 4; + dynsym_.Add(oatlastword, rodata_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT); } - if (bss_.finished_) { - dynsym_.Add(dynstr_.Write("oatbss"), &bss_, - 0, true, bss_.GetSize(), STB_GLOBAL, STT_OBJECT); - dynsym_.Add(dynstr_.Write("oatbsslastword"), &bss_, - bss_.GetSize() - 4, true, 4, STB_GLOBAL, STT_OBJECT); + if (bss_size != 0u) { + Elf_Word bss_index = rodata_index + 1u + (text_size != 0 ? 1u : 0u); + Elf_Word oatbss = dynstr_.Add("oatbss"); + dynsym_.Add(oatbss, bss_index, bss_address, bss_size, STB_GLOBAL, STT_OBJECT); + Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword"); + Elf_Word bsslastword_address = bss_address + bss_size - 4; + dynsym_.Add(oatbsslastword, bss_index, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); } - Elf_Word soname_offset = dynstr_.Write(soname); - dynstr_.End(); - - dynsym_.Start(); - dynsym_.Write(); - dynsym_.End(); + Elf_Word soname_offset = dynstr_.Add(soname); // We do not really need a hash-table since there is so few entries. // However, the hash-table is the only way the linker can actually // determine the number of symbols in .dynsym so it is required. - hash_.Start(); - int count = dynsym_.GetSize() / sizeof(Elf_Sym); // Includes NULL. + int count = dynsym_.GetCacheSize() / sizeof(Elf_Sym); // Includes NULL. std::vector<Elf_Word> hash; hash.push_back(1); // Number of buckets. hash.push_back(count); // Number of chains. @@ -484,21 +609,44 @@ class ElfBuilder FINAL { hash.push_back(i + 1); // Each symbol points to the next one. } hash.push_back(0); // Last symbol terminates the chain. - hash_.WriteFully(hash.data(), hash.size() * sizeof(hash[0])); - hash_.End(); + hash_.Add(hash.data(), hash.size() * sizeof(hash[0])); + + // Calculate addresses of .dynsym, .hash and .dynamic. + DCHECK_EQ(dynstr_.header_.sh_flags, dynsym_.header_.sh_flags); + DCHECK_EQ(dynsym_.header_.sh_flags, hash_.header_.sh_flags); + Elf_Word dynsym_address = + RoundUp(dynstr_address + dynstr_.GetCacheSize(), dynsym_.header_.sh_addralign); + Elf_Word hash_address = + RoundUp(dynsym_address + dynsym_.GetCacheSize(), hash_.header_.sh_addralign); + DCHECK_EQ(dynamic_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize)); + Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize); - dynamic_.Start(); Elf_Dyn dyns[] = { - { DT_HASH, { hash_.GetAddress() } }, - { DT_STRTAB, { dynstr_.GetAddress() } }, - { DT_SYMTAB, { dynsym_.GetAddress() } }, + { DT_HASH, { hash_address } }, + { DT_STRTAB, { dynstr_address } }, + { DT_SYMTAB, { dynsym_address } }, { DT_SYMENT, { sizeof(Elf_Sym) } }, - { DT_STRSZ, { dynstr_.GetSize() } }, + { DT_STRSZ, { dynstr_.GetCacheSize() } }, { DT_SONAME, { soname_offset } }, { DT_NULL, { 0 } }, }; - dynamic_.WriteFully(&dyns, sizeof(dyns)); - dynamic_.End(); + dynamic_.Add(&dyns, sizeof(dyns)); + + loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize); + } + + void WriteDynamicSection() { + dynstr_.WriteCachedSection(); + dynsym_.WriteCachedSection(); + hash_.WriteCachedSection(); + dynamic_.WriteCachedSection(); + + CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize)); + } + + Elf_Word GetLoadedSize() { + CHECK_NE(loaded_size_, 0u); + return loaded_size_; } // Returns true if all writes and seeks on the output stream succeeded. @@ -676,10 +824,10 @@ class ElfBuilder FINAL { Section rodata_; Section text_; Section bss_; - StringSection dynstr_; + CachedStringSection dynstr_; SymbolSection dynsym_; - Section hash_; - Section dynamic_; + CachedSection hash_; + CachedSection dynamic_; Section eh_frame_; Section eh_frame_hdr_; StringSection strtab_; @@ -694,12 +842,14 @@ class ElfBuilder FINAL { std::vector<Section*> sections_; bool started_; + bool write_program_headers_; + + // The size of the memory taken by the ELF file when loaded. + size_t loaded_size_; // Used for allocation of virtual address space. Elf_Addr virtual_address_; - size_t write_program_headers_; - DISALLOW_COPY_AND_ASSIGN(ElfBuilder); }; diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h index d50a08cb20..c9ea0083d5 100644 --- a/compiler/elf_writer.h +++ b/compiler/elf_writer.h @@ -52,14 +52,12 @@ class ElfWriter { virtual ~ElfWriter() {} virtual void Start() = 0; - virtual void PrepareDebugInfo(size_t rodata_section_size, - size_t text_section_size, - const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0; + virtual void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) = 0; + virtual void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0; virtual OutputStream* StartRoData() = 0; virtual void EndRoData(OutputStream* rodata) = 0; virtual OutputStream* StartText() = 0; virtual void EndText(OutputStream* text) = 0; - virtual void SetBssSize(size_t bss_size) = 0; virtual void WriteDynamicSection() = 0; virtual void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0; virtual void WritePatchLocations(const ArrayRef<const uintptr_t>& patch_locations) = 0; @@ -70,6 +68,9 @@ class ElfWriter { // should Seek() back to the position where the stream was before this operation. virtual OutputStream* GetStream() = 0; + // Get the size that the loaded ELF file will occupy in memory. + virtual size_t GetLoadedSize() = 0; + protected: ElfWriter() = default; }; diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 1d71e572d7..19346ecc2b 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -88,14 +88,12 @@ class ElfWriterQuick FINAL : public ElfWriter { ~ElfWriterQuick(); void Start() OVERRIDE; - void PrepareDebugInfo(size_t rodata_section_size, - size_t text_section_size, - const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE; + void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) OVERRIDE; + void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE; OutputStream* StartRoData() OVERRIDE; void EndRoData(OutputStream* rodata) OVERRIDE; OutputStream* StartText() OVERRIDE; void EndText(OutputStream* text) OVERRIDE; - void SetBssSize(size_t bss_size) OVERRIDE; void WriteDynamicSection() OVERRIDE; void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE; void WritePatchLocations(const ArrayRef<const uintptr_t>& patch_locations) OVERRIDE; @@ -103,12 +101,17 @@ class ElfWriterQuick FINAL : public ElfWriter { virtual OutputStream* GetStream() OVERRIDE; + size_t GetLoadedSize() OVERRIDE; + static void EncodeOatPatches(const std::vector<uintptr_t>& locations, std::vector<uint8_t>* buffer); private: const CompilerOptions* const compiler_options_; File* const elf_file_; + size_t rodata_size_; + size_t text_size_; + size_t bss_size_; std::unique_ptr<BufferedOutputStream> output_stream_; std::unique_ptr<ElfBuilder<ElfTypes>> builder_; std::unique_ptr<DebugInfoTask> debug_info_task_; @@ -134,6 +137,9 @@ ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set, : ElfWriter(), compiler_options_(compiler_options), elf_file_(elf_file), + rodata_size_(0u), + text_size_(0u), + bss_size_(0u), output_stream_(MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file))), builder_(new ElfBuilder<ElfTypes>(instruction_set, output_stream_.get())) {} @@ -146,6 +152,19 @@ void ElfWriterQuick<ElfTypes>::Start() { } template <typename ElfTypes> +void ElfWriterQuick<ElfTypes>::SetLoadedSectionSizes(size_t rodata_size, + size_t text_size, + size_t bss_size) { + DCHECK_EQ(rodata_size_, 0u); + rodata_size_ = rodata_size; + DCHECK_EQ(text_size_, 0u); + text_size_ = text_size; + DCHECK_EQ(bss_size_, 0u); + bss_size_ = bss_size; + builder_->PrepareDynamicSection(elf_file_->GetPath(), rodata_size_, text_size_, bss_size_); +} + +template <typename ElfTypes> OutputStream* ElfWriterQuick<ElfTypes>::StartRoData() { auto* rodata = builder_->GetRoData(); rodata->Start(); @@ -172,31 +191,21 @@ void ElfWriterQuick<ElfTypes>::EndText(OutputStream* text) { } template <typename ElfTypes> -void ElfWriterQuick<ElfTypes>::SetBssSize(size_t bss_size) { - auto* bss = builder_->GetBss(); - if (bss_size != 0u) { - bss->WriteNoBitsSection(bss_size); - } -} - -template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::WriteDynamicSection() { - builder_->WriteDynamicSection(elf_file_->GetPath()); + if (bss_size_ != 0u) { + builder_->GetBss()->WriteNoBitsSection(bss_size_); + } + builder_->WriteDynamicSection(); } template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::PrepareDebugInfo( - size_t rodata_section_size, - size_t text_section_size, const ArrayRef<const debug::MethodDebugInfo>& method_infos) { if (!method_infos.empty() && compiler_options_->GetGenerateMiniDebugInfo()) { // Prepare the mini-debug-info in background while we do other I/O. Thread* self = Thread::Current(); debug_info_task_ = std::unique_ptr<DebugInfoTask>( - new DebugInfoTask(builder_->GetIsa(), - rodata_section_size, - text_section_size, - method_infos)); + new DebugInfoTask(builder_->GetIsa(), rodata_size_, text_size_, method_infos)); debug_info_thread_pool_ = std::unique_ptr<ThreadPool>( new ThreadPool("Mini-debug-info writer", 1)); debug_info_thread_pool_->AddTask(self, debug_info_task_.get()); @@ -245,6 +254,11 @@ OutputStream* ElfWriterQuick<ElfTypes>::GetStream() { return builder_->GetStream(); } +template <typename ElfTypes> +size_t ElfWriterQuick<ElfTypes>::GetLoadedSize() { + return builder_->GetLoadedSize(); +} + // Explicit instantiations template class ElfWriterQuick<ElfTypes32>; template class ElfWriterQuick<ElfTypes64>; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 4920f9baa5..992af29545 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -28,6 +28,7 @@ #include "elf_writer_quick.h" #include "gc/space/image_space.h" #include "image_writer.h" +#include "linker/multi_oat_relative_patcher.h" #include "lock_word.h" #include "mirror/object-inl.h" #include "oat_writer.h" @@ -72,10 +73,10 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str())); const uintptr_t requested_image_base = ART_BASE_ADDRESS; - std::unordered_map<const DexFile*, const char*> dex_file_to_oat_filename_map; + std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map; std::vector<const char*> oat_filename_vector(1, oat_filename.c_str()); for (const DexFile* dex_file : class_linker->GetBootClassPath()) { - dex_file_to_oat_filename_map.emplace(dex_file, oat_filename.c_str()); + dex_file_to_oat_index_map.emplace(dex_file, 0); } std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_, requested_image_base, @@ -83,7 +84,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { /*compile_app_image*/false, storage_mode, oat_filename_vector, - dex_file_to_oat_filename_map)); + dex_file_to_oat_index_map)); // TODO: compile_pic should be a test argument. { { @@ -123,10 +124,22 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { &opened_dex_files_map, &opened_dex_files); ASSERT_TRUE(dex_files_ok); - oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files); + bool image_space_ok = writer->PrepareImageAddressSpace(); ASSERT_TRUE(image_space_ok); + linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), + instruction_set_features_.get()); + oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files, &patcher); + size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); + size_t text_size = oat_writer.GetSize() - rodata_size; + elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize()); + + writer->UpdateOatFileLayout(/* oat_index */ 0u, + elf_writer->GetLoadedSize(), + oat_writer.GetOatDataOffset(), + oat_writer.GetSize()); + bool rodata_ok = oat_writer.WriteRodata(rodata); ASSERT_TRUE(rodata_ok); elf_writer->EndRoData(rodata); @@ -139,13 +152,13 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { bool header_ok = oat_writer.WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); ASSERT_TRUE(header_ok); - elf_writer->SetBssSize(oat_writer.GetBssSize()); + writer->UpdateOatFileHeader(/* oat_index */ 0u, oat_writer.GetOatHeader()); + elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations()); bool success = elf_writer->End(); - ASSERT_TRUE(success); } } @@ -158,12 +171,10 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { std::vector<const char*> dup_image_filename(1, image_file.GetFilename().c_str()); bool success_image = writer->Write(kInvalidFd, dup_image_filename, - kInvalidFd, - dup_oat_filename, - dup_oat_filename[0]); + dup_oat_filename); ASSERT_TRUE(success_image); bool success_fixup = ElfWriter::Fixup(dup_oat.get(), - writer->GetOatDataBegin(dup_oat_filename[0])); + writer->GetOatDataBegin(0)); ASSERT_TRUE(success_fixup); ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file " diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 73574ba673..5eff8f37ec 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. @@ -159,9 +162,7 @@ bool ImageWriter::PrepareImageAddressSpace() { bool ImageWriter::Write(int image_fd, const std::vector<const char*>& image_filenames, - int oat_fd, - const std::vector<const char*>& oat_filenames, - const std::string& oat_location) { + const std::vector<const char*>& oat_filenames) { // If image_fd or oat_fd are not kInvalidFd then we may have empty strings in image_filenames or // oat_filenames. CHECK(!image_filenames.empty()); @@ -169,95 +170,13 @@ bool ImageWriter::Write(int image_fd, CHECK_EQ(image_filenames.size(), 1u); } CHECK(!oat_filenames.empty()); - if (oat_fd != kInvalidFd) { - CHECK_EQ(oat_filenames.size(), 1u); - } CHECK_EQ(image_filenames.size(), oat_filenames.size()); - size_t oat_file_offset = 0; - - for (size_t i = 0; i < oat_filenames.size(); ++i) { - const char* oat_filename = oat_filenames[i]; - std::unique_ptr<File> oat_file; - - if (oat_fd != -1) { - if (strlen(oat_filename) == 0u) { - oat_file.reset(new File(oat_fd, false)); - } else { - oat_file.reset(new File(oat_fd, oat_filename, false)); - } - int length = oat_file->GetLength(); - if (length < 0) { - PLOG(ERROR) << "Oat file has negative length " << length; - return false; - } else { - // Leave the fd open since dex2oat still needs to write out the oat file with the fd. - oat_file->DisableAutoClose(); - } - } else { - oat_file.reset(OS::OpenFileReadWrite(oat_filename)); - } - if (oat_file == nullptr) { - PLOG(ERROR) << "Failed to open oat file " << oat_filename; - return false; - } - std::string error_msg; - oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_filename, nullptr, &error_msg); - if (oat_file_ == nullptr) { - PLOG(ERROR) << "Failed to open writable oat file " << oat_filename; - oat_file->Erase(); - return false; - } - Runtime::Current()->GetOatFileManager().RegisterOatFile( - std::unique_ptr<const OatFile>(oat_file_)); - - const OatHeader& oat_header = oat_file_->GetOatHeader(); - ImageInfo& image_info = GetImageInfo(oat_filename); - - size_t oat_loaded_size = 0; - size_t oat_data_offset = 0; - ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset); - - DCHECK_EQ(image_info.oat_offset_, oat_file_offset); - oat_file_offset += oat_loaded_size; - - if (i == 0) { - // Primary oat file, read the trampolines. - image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = - oat_header.GetInterpreterToInterpreterBridgeOffset(); - image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = - oat_header.GetInterpreterToCompiledCodeBridgeOffset(); - image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = - oat_header.GetJniDlsymLookupOffset(); - image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = - oat_header.GetQuickGenericJniTrampolineOffset(); - image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = - oat_header.GetQuickImtConflictTrampolineOffset(); - image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = - oat_header.GetQuickResolutionTrampolineOffset(); - image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = - oat_header.GetQuickToInterpreterBridgeOffset(); - } - - - { - ScopedObjectAccess soa(Thread::Current()); - CreateHeader(oat_loaded_size, oat_data_offset); - CopyAndFixupNativeData(); - } - - SetOatChecksumFromElfFile(oat_file.get()); - - if (oat_fd != -1) { - // Leave fd open for caller. - if (oat_file->Flush() != 0) { - LOG(ERROR) << "Failed to flush oat file " << oat_filename << " for " << oat_location; - return false; - } - } else if (oat_file->FlushCloseOrErase() != 0) { - LOG(ERROR) << "Failed to flush and close oat file " << oat_filename - << " for " << oat_location; - return false; + { + ScopedObjectAccess soa(Thread::Current()); + for (size_t i = 0; i < oat_filenames.size(); ++i) { + CreateHeader(i); + CopyAndFixupNativeData(i); } } @@ -270,8 +189,7 @@ bool ImageWriter::Write(int image_fd, for (size_t i = 0; i < image_filenames.size(); ++i) { const char* image_filename = image_filenames[i]; - const char* oat_filename = oat_filenames[i]; - ImageInfo& image_info = GetImageInfo(oat_filename); + ImageInfo& image_info = GetImageInfo(i); std::unique_ptr<File> image_file; if (image_fd != kInvalidFd) { if (strlen(image_filename) == 0u) { @@ -393,8 +311,8 @@ void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot DCHECK(object != nullptr); DCHECK_NE(image_objects_offset_begin_, 0u); - const char* oat_filename = GetOatFilename(object); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + ImageInfo& image_info = GetImageInfo(oat_index); size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()]; size_t new_offset = bin_slot_offset + bin_slot.GetIndex(); DCHECK_ALIGNED(new_offset, kObjectAlignment); @@ -414,8 +332,8 @@ size_t ImageWriter::GetImageOffset(mirror::Object* object) const { DCHECK(IsImageOffsetAssigned(object)); LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); DCHECK_LT(offset, image_info.image_end_); return offset; } @@ -458,8 +376,8 @@ void ImageWriter::PrepareDexCacheArraySlots() { // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned() // when AssignImageBinSlot() assigns their indexes out or order. for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) { - auto it = dex_file_oat_filename_map_.find(dex_file); - DCHECK(it != dex_file_oat_filename_map_.end()) << dex_file->GetLocation(); + auto it = dex_file_oat_index_map_.find(dex_file); + DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); ImageInfo& image_info = GetImageInfo(it->second); image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]); DexCacheArraysLayout layout(target_ptr_size_, dex_file); @@ -478,8 +396,8 @@ void ImageWriter::PrepareDexCacheArraySlots() { const DexFile* dex_file = dex_cache->GetDexFile(); DexCacheArraysLayout layout(target_ptr_size_, dex_file); DCHECK(layout.Valid()); - const char* oat_filename = GetOatFilenameForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndexForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_index); uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file); DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr); AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), @@ -501,9 +419,9 @@ void ImageWriter::PrepareDexCacheArraySlots() { void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset, DexCache* dex_cache) { if (array != nullptr) { DCHECK(!IsInBootImage(array)); - const char* oat_filename = GetOatFilenameForDexCache(dex_cache); + size_t oat_index = GetOatIndexForDexCache(dex_cache); native_object_relocations_.emplace(array, - NativeObjectRelocation { oat_filename, offset, kNativeObjectRelocationTypeDexCacheArray }); + NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeDexCacheArray }); } } @@ -618,8 +536,8 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object) { } // else bin = kBinRegular } - const char* oat_filename = GetOatFilename(object); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + ImageInfo& image_info = GetImageInfo(oat_index); size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). @@ -655,8 +573,8 @@ bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); BinSlot bin_slot(offset); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]) << "bin slot offset should not exceed the size of that bin"; } @@ -672,16 +590,15 @@ ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const DCHECK_LE(offset, std::numeric_limits<uint32_t>::max()); BinSlot bin_slot(static_cast<uint32_t>(offset)); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]); return bin_slot; } bool ImageWriter::AllocMemory() { - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { ImageSection unused_sections[ImageHeader::kSectionCount]; const size_t length = RoundUp( image_info.CreateImageSections(target_ptr_size_, unused_sections), @@ -735,20 +652,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 +679,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 +696,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 +717,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 +751,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 +793,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 +812,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 +828,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, @@ -970,8 +891,7 @@ void ImageWriter::DumpImageClasses() { mirror::String* ImageWriter::FindInternedString(mirror::String* string) { Thread* const self = Thread::Current(); - for (auto& pair : image_info_map_) { - const ImageInfo& image_info = pair.second; + for (const ImageInfo& image_info : image_infos_) { mirror::String* const found = image_info.intern_table_->LookupStrong(self, string); DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr) << string->ToModifiedUtf8(); @@ -998,8 +918,8 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) { DCHECK(obj != nullptr); // if it is a string, we want to intern it if its not interned. if (obj->GetClass()->IsStringClass()) { - const char* oat_filename = GetOatFilename(obj); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(obj); + ImageInfo& image_info = GetImageInfo(oat_index); // we must be an interned string that was forward referenced and already assigned if (IsImageBinSlotAssigned(obj)) { @@ -1028,7 +948,7 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) { AssignImageBinSlot(obj); } -ObjectArray<Object>* ImageWriter::CreateImageRoots(const char* oat_filename) const { +ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); Thread* self = Thread::Current(); @@ -1037,10 +957,10 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(const char* oat_filename) con class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); std::unordered_set<const DexFile*> image_dex_files; - for (auto& pair : dex_file_oat_filename_map_) { + for (auto& pair : dex_file_oat_index_map_) { const DexFile* image_dex_file = pair.first; - const char* image_oat_filename = pair.second; - if (strcmp(oat_filename, image_oat_filename) == 0) { + size_t image_oat_index = pair.second; + if (oat_index == image_oat_index) { image_dex_files.insert(image_dex_file); } } @@ -1165,8 +1085,8 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { LengthPrefixedArray<ArtField>* fields[] = { as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(), }; - const char* oat_file = GetOatFilenameForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_file); + size_t oat_index = GetOatIndexForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_index); { // Note: This table is only accessed from the image writer, so the lock is technically // unnecessary. @@ -1184,8 +1104,11 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { << " already forwarded"; size_t& offset = image_info.bin_slot_sizes_[kBinArtField]; DCHECK(!IsInBootImage(cur_fields)); - native_object_relocations_.emplace(cur_fields, - NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtFieldArray }); + native_object_relocations_.emplace( + cur_fields, + NativeObjectRelocation { + oat_index, offset, kNativeObjectRelocationTypeArtFieldArray + }); offset += header_size; // Forward individual fields so that we can quickly find where they belong. for (size_t i = 0, count = cur_fields->size(); i < count; ++i) { @@ -1195,8 +1118,9 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i << " already assigned " << PrettyField(field) << " static=" << field->IsStatic(); DCHECK(!IsInBootImage(field)); - native_object_relocations_.emplace(field, - NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtField }); + native_object_relocations_.emplace( + field, + NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeArtField }); offset += sizeof(ArtField); } } @@ -1229,13 +1153,13 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { DCHECK(!IsInBootImage(array)); native_object_relocations_.emplace(array, NativeObjectRelocation { - oat_file, + oat_index, offset, any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty : kNativeObjectRelocationTypeArtMethodArrayClean }); offset += header_size; for (auto& m : as_klass->GetMethods(target_ptr_size_)) { - AssignMethodOffset(&m, type, oat_file); + AssignMethodOffset(&m, type, oat_index); } (any_dirty ? dirty_methods_ : clean_methods_) += num_methods; } @@ -1263,14 +1187,14 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { void ImageWriter::AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type, - const char* oat_filename) { + size_t oat_index) { DCHECK(!IsInBootImage(method)); auto it = native_object_relocations_.find(method); CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned " << PrettyMethod(method); - ImageInfo& image_info = GetImageInfo(oat_filename); + ImageInfo& image_info = GetImageInfo(oat_index); size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)]; - native_object_relocations_.emplace(method, NativeObjectRelocation { oat_filename, offset, type }); + native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type }); offset += ArtMethod::Size(target_ptr_size_); } @@ -1305,9 +1229,8 @@ void ImageWriter::CalculateNewObjectOffsets() { Thread* const self = Thread::Current(); StackHandleScopeCollection handles(self); std::vector<Handle<ObjectArray<Object>>> image_roots; - for (const char* oat_filename : oat_filenames_) { - std::string image_filename = oat_filename; - image_roots.push_back(handles.NewHandle(CreateImageRoots(image_filename.c_str()))); + for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { + image_roots.push_back(handles.NewHandle(CreateImageRoots(i))); } auto* runtime = Runtime::Current(); @@ -1333,12 +1256,12 @@ void ImageWriter::CalculateNewObjectOffsets() { const auto image_method_type = kNativeObjectRelocationTypeArtMethodArrayClean; auto it = native_object_relocations_.find(&image_method_array_); CHECK(it == native_object_relocations_.end()); - ImageInfo& default_image_info = GetImageInfo(default_oat_filename_); + ImageInfo& default_image_info = GetImageInfo(GetDefaultOatIndex()); size_t& offset = default_image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)]; if (!compile_app_image_) { native_object_relocations_.emplace(&image_method_array_, - NativeObjectRelocation { default_oat_filename_, offset, image_method_type }); + NativeObjectRelocation { GetDefaultOatIndex(), offset, image_method_type }); } size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); const size_t array_size = LengthPrefixedArray<ArtMethod>::ComputeSize( @@ -1350,15 +1273,14 @@ void ImageWriter::CalculateNewObjectOffsets() { CHECK(m->IsRuntimeMethod()); DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; if (!IsInBootImage(m)) { - AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, default_oat_filename_); + AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, GetDefaultOatIndex()); } } // Calculate size of the dex cache arrays slot and prepare offsets. PrepareDexCacheArraySlots(); // Calculate the sizes of the intern tables and class tables. - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { // Calculate how big the intern table will be after being serialized. InternTable* const intern_table = image_info.intern_table_.get(); CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings"; @@ -1369,8 +1291,7 @@ void ImageWriter::CalculateNewObjectOffsets() { } // Calculate bin slot offsets. - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { size_t bin_offset = image_objects_offset_begin_; for (size_t i = 0; i != kBinSize; ++i) { image_info.bin_slot_offsets_[i] = bin_offset; @@ -1390,8 +1311,7 @@ void ImageWriter::CalculateNewObjectOffsets() { // Calculate image offsets. size_t image_offset = 0; - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { image_info.image_begin_ = global_image_begin_ + image_offset; image_info.image_offset_ = image_offset; ImageSection unused_sections[ImageHeader::kSectionCount]; @@ -1408,8 +1328,7 @@ void ImageWriter::CalculateNewObjectOffsets() { // DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_); size_t i = 0; - for (const char* oat_filename : oat_filenames_) { - ImageInfo& image_info = GetImageInfo(oat_filename); + for (ImageInfo& image_info : image_infos_) { image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get())); i++; } @@ -1418,7 +1337,7 @@ void ImageWriter::CalculateNewObjectOffsets() { for (auto& pair : native_object_relocations_) { NativeObjectRelocation& relocation = pair.second; Bin bin_type = BinTypeForNativeRelocationType(relocation.type); - ImageInfo& image_info = GetImageInfo(relocation.oat_filename); + ImageInfo& image_info = GetImageInfo(relocation.oat_index); relocation.offset += image_info.bin_slot_offsets_[bin_type]; } @@ -1467,15 +1386,11 @@ size_t ImageWriter::ImageInfo::CreateImageSections(size_t target_ptr_size, return cur_pos; } -void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { - CHECK_NE(0U, oat_loaded_size); - const char* oat_filename = oat_file_->GetLocation().c_str(); - ImageInfo& image_info = GetImageInfo(oat_filename); - const uint8_t* oat_file_begin = GetOatFileBegin(oat_filename); - const uint8_t* oat_file_end = oat_file_begin + oat_loaded_size; - image_info.oat_data_begin_ = const_cast<uint8_t*>(oat_file_begin) + oat_data_offset; - const uint8_t* oat_data_end = image_info.oat_data_begin_ + oat_file_->Size(); - image_info.oat_size_ = oat_file_->Size(); +void ImageWriter::CreateHeader(size_t oat_index) { + ImageInfo& image_info = GetImageInfo(oat_index); + const uint8_t* oat_file_begin = image_info.oat_file_begin_; + const uint8_t* oat_file_end = oat_file_begin + image_info.oat_loaded_size_; + const uint8_t* oat_data_end = image_info.oat_data_begin_ + image_info.oat_size_; // Create the image sections. ImageSection sections[ImageHeader::kSectionCount]; @@ -1486,7 +1401,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap]; *bitmap_section = ImageSection(RoundUp(image_end, kPageSize), RoundUp(bitmap_bytes, kPageSize)); if (VLOG_IS_ON(compiler)) { - LOG(INFO) << "Creating header for " << oat_filename; + LOG(INFO) << "Creating header for " << oat_filenames_[oat_index]; size_t idx = 0; for (const ImageSection& section : sections) { LOG(INFO) << static_cast<ImageHeader::ImageSections>(idx) << " " << section; @@ -1515,7 +1430,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { image_end, sections, image_info.image_roots_address_, - oat_file_->GetOatHeader().GetChecksum(), + image_info.oat_checksum_, PointerToLowMemUInt32(oat_file_begin), PointerToLowMemUInt32(image_info.oat_data_begin_), PointerToLowMemUInt32(oat_data_end), @@ -1534,8 +1449,8 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { auto it = native_object_relocations_.find(method); CHECK(it != native_object_relocations_.end()) << PrettyMethod(method) << " @ " << method; - const char* oat_filename = GetOatFilename(method->GetDexCache()); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(method->GetDexCache()); + ImageInfo& image_info = GetImageInfo(oat_index); CHECK_GE(it->second.offset, image_info.image_end_) << "ArtMethods should be after Objects"; return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset); } @@ -1564,14 +1479,13 @@ class FixupRootVisitor : public RootVisitor { ImageWriter* const image_writer_; }; -void ImageWriter::CopyAndFixupNativeData() { - const char* oat_filename = oat_file_->GetLocation().c_str(); - ImageInfo& image_info = GetImageInfo(oat_filename); +void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { + ImageInfo& image_info = GetImageInfo(oat_index); // Copy ArtFields and methods to their locations and update the array for convenience. for (auto& pair : native_object_relocations_) { NativeObjectRelocation& relocation = pair.second; // Only work with fields and methods that are in the current oat file. - if (strcmp(relocation.oat_filename, oat_filename) != 0) { + if (relocation.oat_index != oat_index) { continue; } auto* dest = image_info.image_->Begin() + relocation.offset; @@ -1617,7 +1531,7 @@ void ImageWriter::CopyAndFixupNativeData() { ArtMethod* method = image_methods_[i]; CHECK(method != nullptr); // Only place runtime methods in the image of the default oat file. - if (method->IsRuntimeMethod() && strcmp(default_oat_filename_, oat_filename) != 0) { + if (method->IsRuntimeMethod() && oat_index != GetDefaultOatIndex()) { continue; } if (!IsInBootImage(method)) { @@ -1722,7 +1636,7 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, mirror::PointerArray* a } UNREACHABLE(); } else { - ImageInfo& image_info = GetImageInfo(it->second.oat_filename); + ImageInfo& image_info = GetImageInfo(it->second.oat_index); elem = image_info.image_begin_ + it->second.offset; } } @@ -1735,8 +1649,8 @@ void ImageWriter::CopyAndFixupObject(Object* obj) { return; } size_t offset = GetImageOffset(obj); - const char* oat_filename = GetOatFilename(obj); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndex(obj); + ImageInfo& image_info = GetImageInfo(oat_index); auto* dst = reinterpret_cast<Object*>(image_info.image_->Begin() + offset); DCHECK_LT(offset, image_info.image_end_); const auto* src = reinterpret_cast<const uint8_t*>(obj); @@ -1820,12 +1734,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_index); + return reinterpret_cast<T*>(image_info.image_begin_ + relocation.offset); } } @@ -1834,41 +1752,27 @@ T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) { if (obj == nullptr || IsInBootImage(obj)) { return obj; } else { - const char* oat_filename = GetOatFilenameForDexCache(dex_cache); - ImageInfo& image_info = GetImageInfo(oat_filename); + size_t oat_index = GetOatIndexForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_index); return reinterpret_cast<T*>(image_info.image_->Begin() + NativeOffsetInImage(obj)); } } 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 +1856,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 +1867,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 +1875,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_); } } @@ -2055,9 +1951,19 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, // trampoline. // Quick entrypoint: - uint32_t quick_oat_code_offset = PointerToLowMemUInt32( - method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_)); - const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); + const void* quick_oat_entry_point = + method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_); + const uint8_t* quick_code; + + if (UNLIKELY(IsInBootImage(method->GetDeclaringClass()))) { + DCHECK(method->IsCopied()); + // If the code is not in the oat file corresponding to this image (e.g. default methods) + quick_code = reinterpret_cast<const uint8_t*>(quick_oat_entry_point); + } else { + uint32_t quick_oat_code_offset = PointerToLowMemUInt32(quick_oat_entry_point); + quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); + } + *quick_is_interpreted = false; if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) { @@ -2089,20 +1995,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_ @@ -2150,34 +2046,6 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, } } -static OatHeader* GetOatHeaderFromElf(ElfFile* elf) { - uint64_t data_sec_offset; - bool has_data_sec = elf->GetSectionOffsetAndSize(".rodata", &data_sec_offset, nullptr); - if (!has_data_sec) { - return nullptr; - } - return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec_offset); -} - -void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) { - std::string error_msg; - std::unique_ptr<ElfFile> elf(ElfFile::Open(elf_file, - PROT_READ | PROT_WRITE, - MAP_SHARED, - &error_msg)); - if (elf.get() == nullptr) { - LOG(FATAL) << "Unable open oat file: " << error_msg; - return; - } - OatHeader* oat_header = GetOatHeaderFromElf(elf.get()); - CHECK(oat_header != nullptr); - CHECK(oat_header->IsValid()); - - ImageInfo& image_info = GetImageInfo(oat_file_->GetLocation().c_str()); - ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin()); - image_header->SetOatChecksum(oat_header->GetChecksum()); -} - size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const { DCHECK_LE(up_to, kBinSize); return std::accumulate(&image_info.bin_slot_sizes_[0], @@ -2208,19 +2076,6 @@ uint32_t ImageWriter::BinSlot::GetIndex() const { return lockword_ & ~kBinMask; } -uint8_t* ImageWriter::GetOatFileBegin(const char* oat_filename) const { - uintptr_t last_image_end = 0; - for (const char* oat_fn : oat_filenames_) { - const ImageInfo& image_info = GetConstImageInfo(oat_fn); - DCHECK(image_info.image_begin_ != nullptr); - uintptr_t this_end = reinterpret_cast<uintptr_t>(image_info.image_begin_) + - image_info.image_size_; - last_image_end = std::max(this_end, last_image_end); - } - const ImageInfo& image_info = GetConstImageInfo(oat_filename); - return reinterpret_cast<uint8_t*>(last_image_end) + image_info.oat_offset_; -} - ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) { switch (type) { case kNativeObjectRelocationTypeArtField: @@ -2238,92 +2093,113 @@ ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocat UNREACHABLE(); } -const char* ImageWriter::GetOatFilename(mirror::Object* obj) const { +size_t ImageWriter::GetOatIndex(mirror::Object* obj) const { if (compile_app_image_) { - return default_oat_filename_; + return GetDefaultOatIndex(); } else { - return GetOatFilenameForDexCache(obj->IsDexCache() ? obj->AsDexCache() : - obj->IsClass() ? obj->AsClass()->GetDexCache() : obj->GetClass()->GetDexCache()); + mirror::DexCache* dex_cache = + obj->IsDexCache() ? obj->AsDexCache() + : obj->IsClass() ? obj->AsClass()->GetDexCache() + : obj->GetClass()->GetDexCache(); + return GetOatIndexForDexCache(dex_cache); } } -const char* ImageWriter::GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const { - if (compile_app_image_ || dex_cache == nullptr) { - return default_oat_filename_; +size_t ImageWriter::GetOatIndexForDexFile(const DexFile* dex_file) const { + if (compile_app_image_) { + return GetDefaultOatIndex(); } else { - auto it = dex_file_oat_filename_map_.find(dex_cache->GetDexFile()); - DCHECK(it != dex_file_oat_filename_map_.end()) << dex_cache->GetDexFile()->GetLocation(); + auto it = dex_file_oat_index_map_.find(dex_file); + DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); return it->second; } } -ImageWriter::ImageInfo& ImageWriter::GetImageInfo(const char* oat_filename) { - auto it = image_info_map_.find(oat_filename); - DCHECK(it != image_info_map_.end()); - return it->second; +size_t ImageWriter::GetOatIndexForDexCache(mirror::DexCache* dex_cache) const { + if (dex_cache == nullptr) { + return GetDefaultOatIndex(); + } else { + return GetOatIndexForDexFile(dex_cache->GetDexFile()); + } } -const ImageWriter::ImageInfo& ImageWriter::GetConstImageInfo(const char* oat_filename) const { - auto it = image_info_map_.find(oat_filename); - DCHECK(it != image_info_map_.end()); - return it->second; -} +void ImageWriter::UpdateOatFileLayout(size_t oat_index, + size_t oat_loaded_size, + size_t oat_data_offset, + size_t oat_data_size) { + const uint8_t* images_end = image_infos_.back().image_begin_ + image_infos_.back().image_size_; + for (const ImageInfo& info : image_infos_) { + DCHECK_LE(info.image_begin_ + info.image_size_, images_end); + } + DCHECK(images_end != nullptr); // Image space must be ready. -const ImageWriter::ImageInfo& ImageWriter::GetImageInfo(size_t index) const { - DCHECK_LT(index, oat_filenames_.size()); - return GetConstImageInfo(oat_filenames_[index]); -} + ImageInfo& cur_image_info = GetImageInfo(oat_index); + cur_image_info.oat_file_begin_ = images_end + cur_image_info.oat_offset_; + cur_image_info.oat_loaded_size_ = oat_loaded_size; + cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset; + cur_image_info.oat_size_ = oat_data_size; -void ImageWriter::UpdateOatFile(File* oat_file, const char* oat_filename) { - DCHECK(oat_file != nullptr); if (compile_app_image_) { CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image."; return; } - ImageInfo& cur_image_info = GetImageInfo(oat_filename); // Update the oat_offset of the next image info. - auto it = std::find(oat_filenames_.begin(), oat_filenames_.end(), oat_filename); - DCHECK(it != oat_filenames_.end()); - - it++; - if (it != oat_filenames_.end()) { - size_t oat_loaded_size = 0; - size_t oat_data_offset = 0; - ElfWriter::GetOatElfInformation(oat_file, &oat_loaded_size, &oat_data_offset); + if (oat_index + 1u != oat_filenames_.size()) { // There is a following one. - ImageInfo& next_image_info = GetImageInfo(*it); + ImageInfo& next_image_info = GetImageInfo(oat_index + 1u); next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size; } } +void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header) { + ImageInfo& cur_image_info = GetImageInfo(oat_index); + cur_image_info.oat_checksum_ = oat_header.GetChecksum(); + + if (oat_index == GetDefaultOatIndex()) { + // Primary oat file, read the trampolines. + cur_image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = + oat_header.GetInterpreterToInterpreterBridgeOffset(); + cur_image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = + oat_header.GetInterpreterToCompiledCodeBridgeOffset(); + cur_image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = + oat_header.GetJniDlsymLookupOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = + oat_header.GetQuickGenericJniTrampolineOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = + oat_header.GetQuickImtConflictTrampolineOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = + oat_header.GetQuickResolutionTrampolineOffset(); + cur_image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = + oat_header.GetQuickToInterpreterBridgeOffset(); + } +} + ImageWriter::ImageWriter( const CompilerDriver& compiler_driver, uintptr_t image_begin, bool compile_pic, bool compile_app_image, ImageHeader::StorageMode image_storage_mode, - const std::vector<const char*> oat_filenames, - const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map) + const std::vector<const char*>& oat_filenames, + const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map) : compiler_driver_(compiler_driver), global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)), image_objects_offset_begin_(0), - oat_file_(nullptr), compile_pic_(compile_pic), compile_app_image_(compile_app_image), target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())), + image_infos_(oat_filenames.size()), image_method_array_(ImageHeader::kImageMethodsCount), dirty_methods_(0u), clean_methods_(0u), image_storage_mode_(image_storage_mode), - dex_file_oat_filename_map_(dex_file_oat_filename_map), oat_filenames_(oat_filenames), - default_oat_filename_(oat_filenames[0]) { + dex_file_oat_index_map_(dex_file_oat_index_map) { CHECK_NE(image_begin, 0U); - for (const char* oat_filename : oat_filenames) { - 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..dba9dd71fc 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -27,6 +27,7 @@ #include <ostream> #include "base/bit_utils.h" +#include "base/dchecked_vector.h" #include "base/length_prefixed_array.h" #include "base/macros.h" #include "driver/compiler_driver.h" @@ -59,20 +60,19 @@ class ImageWriter FINAL { bool compile_pic, bool compile_app_image, ImageHeader::StorageMode image_storage_mode, - const std::vector<const char*> oat_filenames, - const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map); + const std::vector<const char*>& oat_filenames, + const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map); bool PrepareImageAddressSpace(); bool IsImageAddressSpaceReady() const { - bool ready = !image_info_map_.empty(); - for (auto& pair : image_info_map_) { - const ImageInfo& image_info = pair.second; + DCHECK(!image_infos_.empty()); + for (const ImageInfo& image_info : image_infos_) { if (image_info.image_roots_address_ == 0u) { return false; } } - return ready; + return true; } template <typename T> @@ -80,8 +80,8 @@ class ImageWriter FINAL { if (object == nullptr || IsInBootImage(object)) { return object; } else { - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); return reinterpret_cast<T*>(image_info.image_begin_ + GetImageOffset(object)); } } @@ -91,9 +91,9 @@ class ImageWriter FINAL { template <typename PtrType> PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_) { - auto oat_it = dex_file_oat_filename_map_.find(dex_file); - DCHECK(oat_it != dex_file_oat_filename_map_.end()); - const ImageInfo& image_info = GetConstImageInfo(oat_it->second); + auto oat_it = dex_file_oat_index_map_.find(dex_file); + DCHECK(oat_it != dex_file_oat_index_map_.end()); + const ImageInfo& image_info = GetImageInfo(oat_it->second); auto it = image_info.dex_cache_array_starts_.find(dex_file); DCHECK(it != image_info.dex_cache_array_starts_.end()); return reinterpret_cast<PtrType>( @@ -101,7 +101,13 @@ class ImageWriter FINAL { it->second + offset); } - uint8_t* GetOatFileBegin(const char* oat_filename) const; + size_t GetOatFileOffset(size_t oat_index) const { + return GetImageInfo(oat_index).oat_offset_; + } + + const uint8_t* GetOatFileBegin(size_t oat_index) const { + return GetImageInfo(oat_index).oat_file_begin_; + } // If image_fd is not kInvalidFd, then we use that for the image file. Otherwise we open // the names in image_filenames. @@ -109,21 +115,32 @@ class ImageWriter FINAL { // the names in oat_filenames. bool Write(int image_fd, const std::vector<const char*>& image_filenames, - int oat_fd, - const std::vector<const char*>& oat_filenames, - const std::string& oat_location) + const std::vector<const char*>& oat_filenames) REQUIRES(!Locks::mutator_lock_); - uintptr_t GetOatDataBegin(const char* oat_filename) { - return reinterpret_cast<uintptr_t>(GetImageInfo(oat_filename).oat_data_begin_); + uintptr_t GetOatDataBegin(size_t oat_index) { + return reinterpret_cast<uintptr_t>(GetImageInfo(oat_index).oat_data_begin_); } - const char* GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const + // Get the index of the oat file containing the dex file. + // + // This "oat_index" is used to retrieve information about the the memory layout + // of the oat file and its associated image file, needed for link-time patching + // of references to the image or across oat files. + size_t GetOatIndexForDexFile(const DexFile* dex_file) const; + + // Get the index of the oat file containing the dex file served by the dex cache. + size_t GetOatIndexForDexCache(mirror::DexCache* dex_cache) const SHARED_REQUIRES(Locks::mutator_lock_); - // Update the oat size for the given oat file. This will make the oat_offset for the next oat - // file valid. - void UpdateOatFile(File* oat_file, const char* oat_filename); + // Update the oat layout for the given oat file. + // This will make the oat_offset for the next oat file valid. + void UpdateOatFileLayout(size_t oat_index, + size_t oat_loaded_size, + size_t oat_data_offset, + size_t oat_data_size); + // Update information about the oat header, i.e. checksum and trampoline offsets. + void UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header); private: bool AllocMemory(); @@ -247,10 +264,13 @@ class ImageWriter FINAL { // Offset of the oat file for this image from start of oat files. This is // valid when the previous oat file has been written. size_t oat_offset_ = 0; - // Start of oatdata in the corresponding oat file. This is - // valid when the images have been layed out. - uint8_t* oat_data_begin_ = nullptr; + // Layout of the loaded ELF file containing the oat file, valid after UpdateOatFileLayout(). + const uint8_t* oat_file_begin_ = nullptr; + size_t oat_loaded_size_ = 0; + const uint8_t* oat_data_begin_ = nullptr; size_t oat_size_ = 0; // Size of the corresponding oat data. + // The oat header checksum, valid after UpdateOatFileHeader(). + uint32_t oat_checksum_ = 0u; // Image bitmap which lets us know where the objects inside of the image reside. std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_; @@ -310,8 +330,8 @@ class ImageWriter FINAL { mirror::Object* GetLocalAddress(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_) { size_t offset = GetImageOffset(object); - const char* oat_filename = GetOatFilename(object); - const ImageInfo& image_info = GetConstImageInfo(oat_filename); + size_t oat_index = GetOatIndex(object); + const ImageInfo& image_info = GetImageInfo(oat_index); uint8_t* dst = image_info.image_->Begin() + offset; return reinterpret_cast<mirror::Object*>(dst); } @@ -348,9 +368,9 @@ class ImageWriter FINAL { // Lays out where the image objects will be at runtime. void CalculateNewObjectOffsets() SHARED_REQUIRES(Locks::mutator_lock_); - void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) + void CreateHeader(size_t oat_index) SHARED_REQUIRES(Locks::mutator_lock_); - mirror::ObjectArray<mirror::Object>* CreateImageRoots(const char* oat_filename) const + mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const SHARED_REQUIRES(Locks::mutator_lock_); void CalculateObjectBinSlots(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_); @@ -367,7 +387,7 @@ class ImageWriter FINAL { SHARED_REQUIRES(Locks::mutator_lock_); // Creates the contiguous image in memory and adjusts pointers. - void CopyAndFixupNativeData() SHARED_REQUIRES(Locks::mutator_lock_); + void CopyAndFixupNativeData(size_t oat_index) SHARED_REQUIRES(Locks::mutator_lock_); void CopyAndFixupObjects() SHARED_REQUIRES(Locks::mutator_lock_); static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_); @@ -392,9 +412,6 @@ class ImageWriter FINAL { bool* quick_is_interpreted) SHARED_REQUIRES(Locks::mutator_lock_); - // Patches references in OatFile to expect runtime addresses. - void SetOatChecksumFromElfFile(File* elf_file); - // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const; @@ -404,22 +421,24 @@ class ImageWriter FINAL { // Assign the offset for an ArtMethod. void AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type, - const char* oat_filename) + size_t oat_index) SHARED_REQUIRES(Locks::mutator_lock_); // 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 +447,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> @@ -441,15 +460,21 @@ class ImageWriter FINAL { // Return true if ptr is within the boot oat file. bool IsInBootOatFile(const void* ptr) const; - const char* GetOatFilename(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_); + // Get the index of the oat file associated with the object. + size_t GetOatIndex(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_); - const char* GetDefaultOatFilename() const { - return default_oat_filename_; + // The oat index for shared data in multi-image and all data in single-image compilation. + size_t GetDefaultOatIndex() const { + return 0u; } - ImageInfo& GetImageInfo(const char* oat_filename); - const ImageInfo& GetConstImageInfo(const char* oat_filename) const; - const ImageInfo& GetImageInfo(size_t index) const; + ImageInfo& GetImageInfo(size_t oat_index) { + return image_infos_[oat_index]; + } + + const ImageInfo& GetImageInfo(size_t oat_index) const { + return image_infos_[oat_index]; + } // Find an already strong interned string in the other images or in the boot image. Used to // remove duplicates in the multi image and app image case. @@ -463,9 +488,6 @@ class ImageWriter FINAL { // Offset from image_begin_ to where the first object is in image_. size_t image_objects_offset_begin_; - // oat file with code for this image - OatFile* oat_file_; - // Pointer arrays that need to be updated. Since these are only some int and long arrays, we need // to keep track. These include vtable arrays, iftable arrays, and dex caches. std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_; @@ -481,14 +503,14 @@ class ImageWriter FINAL { // Size of pointers on the target architecture. size_t target_ptr_size_; - // Mapping of oat filename to image data. - std::unordered_map<std::string, ImageInfo> image_info_map_; + // Image data indexed by the oat file index. + dchecked_vector<ImageInfo> image_infos_; // ArtField, ArtMethod relocating map. These are allocated as array of structs but we want to // have one entry per art field for convenience. ArtFields are placed right after the end of the // image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields. struct NativeObjectRelocation { - const char* oat_filename; + size_t oat_index; uintptr_t offset; NativeObjectRelocationType type; @@ -520,10 +542,11 @@ class ImageWriter FINAL { // Which mode the image is stored as, see image.h const ImageHeader::StorageMode image_storage_mode_; - // Map of dex files to the oat filenames that they were compiled into. - const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map_; - const std::vector<const char*> oat_filenames_; - const char* default_oat_filename_; + // The file names of oat files. + const std::vector<const char*>& oat_filenames_; + + // Map of dex files to the indexes of oat files that they were compiled into. + const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_; friend class ContainsBootClassLoaderNonImageClassVisitor; friend class FixupClassVisitor; diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 3fe786141e..23601c39e4 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, @@ -172,7 +163,6 @@ JitCompiler::JitCompiler() : total_time_(0) { /* dump_passes */ false, cumulative_logger_.get(), /* swap_fd */ -1, - /* dex to oat map */ nullptr, /* profile_compilation_info */ nullptr)); // Disable dedupe so we can remove compiled methods. compiler_driver_->SetDedupeEnabled(false); @@ -251,9 +241,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/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 73b0facf4b..682b008219 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -40,6 +40,11 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) { MethodReference(nullptr, 0u), aligned_offset); if (needs_thunk) { + // All remaining patches will be handled by this thunk. + DCHECK(!unprocessed_patches_.empty()); + DCHECK_LE(aligned_offset - unprocessed_patches_.front().second, max_positive_displacement_); + unprocessed_patches_.clear(); + thunk_locations_.push_back(aligned_offset); offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_); } diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h index f80dd962ce..25fd35e1d6 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.h +++ b/compiler/linker/arm/relative_patcher_arm_base.h @@ -27,18 +27,23 @@ namespace linker { class ArmBaseRelativePatcher : public RelativePatcher { public: - uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method, + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, MethodReference method_ref) OVERRIDE; uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; protected: ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider, - InstructionSet instruction_set, std::vector<uint8_t> thunk_code, - uint32_t max_positive_displacement, uint32_t max_negative_displacement); + InstructionSet instruction_set, + std::vector<uint8_t> thunk_code, + uint32_t max_positive_displacement, + uint32_t max_negative_displacement); - uint32_t ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method, - MethodReference method_ref, uint32_t max_extra_space); + uint32_t ReserveSpaceInternal(uint32_t offset, + const CompiledMethod* compiled_method, + MethodReference method_ref, + uint32_t max_extra_space); uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset); private: diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 5f4f760c14..c090dffc55 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -28,8 +28,10 @@ Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* prov kMaxPositiveDisplacement, kMaxNegativeDisplacement) { } -void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { DCHECK_LE(literal_offset + 4u, code->size()); DCHECK_EQ(literal_offset & 1u, 0u); DCHECK_EQ(patch_offset & 1u, 0u); diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h index 006d6fb9d5..0d903c0b41 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.h +++ b/compiler/linker/arm/relative_patcher_thumb2.h @@ -26,10 +26,14 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher { public: explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider); - void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; - void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; private: static std::vector<uint8_t> CompileThunkCode(); diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 3d4c2184f1..a81c85c707 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -131,8 +131,10 @@ uint32_t Arm64RelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) { return ArmBaseRelativePatcher::WriteThunks(out, offset); } -void Arm64RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void Arm64RelativePatcher::PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, uint32_t + target_offset) { DCHECK_LE(literal_offset + 4u, code->size()); DCHECK_EQ(literal_offset & 3u, 0u); DCHECK_EQ(patch_offset & 3u, 0u); diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h index 2d07e75c85..f9b76e6250 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.h +++ b/compiler/linker/arm64/relative_patcher_arm64.h @@ -28,14 +28,19 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher { Arm64RelativePatcher(RelativePatcherTargetProvider* provider, const Arm64InstructionSetFeatures* features); - uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method, + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, MethodReference method_ref) OVERRIDE; uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; - void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; - void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; private: static std::vector<uint8_t> CompileThunkCode(); diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc new file mode 100644 index 0000000000..e9e242b658 --- /dev/null +++ b/compiler/linker/multi_oat_relative_patcher.cc @@ -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. + */ + +#include "multi_oat_relative_patcher.h" + +#include "globals.h" +#include "base/bit_utils.h" +#include "base/logging.h" + +namespace art { +namespace linker { + +MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set, + const InstructionSetFeatures* features) + : method_offset_map_(), + relative_patcher_( + linker::RelativePatcher::Create(instruction_set, features, &method_offset_map_)), + adjustment_(0u), + instruction_set_(instruction_set), + start_size_code_alignment_(0u), + start_size_relative_call_thunks_(0u), + start_size_misc_thunks_(0u) { +} + +void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) { + DCHECK_ALIGNED(adjustment, kPageSize); + adjustment_ = adjustment; + + start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize(); + start_size_relative_call_thunks_ = relative_patcher_->RelativeCallThunksSize(); + start_size_misc_thunks_ = relative_patcher_->MiscThunksSize(); +} + +uint32_t MultiOatRelativePatcher::CodeAlignmentSize() const { + DCHECK_GE(relative_patcher_->CodeAlignmentSize(), start_size_code_alignment_); + return relative_patcher_->CodeAlignmentSize() - start_size_code_alignment_; +} + +uint32_t MultiOatRelativePatcher::RelativeCallThunksSize() const { + DCHECK_GE(relative_patcher_->RelativeCallThunksSize(), start_size_relative_call_thunks_); + return relative_patcher_->RelativeCallThunksSize() - start_size_relative_call_thunks_; +} + +uint32_t MultiOatRelativePatcher::MiscThunksSize() const { + DCHECK_GE(relative_patcher_->MiscThunksSize(), start_size_misc_thunks_); + return relative_patcher_->MiscThunksSize() - start_size_misc_thunks_; +} + +std::pair<bool, uint32_t> MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset( + MethodReference ref) { + auto it = map.find(ref); + if (it == map.end()) { + return std::pair<bool, uint32_t>(false, 0u); + } else { + return std::pair<bool, uint32_t>(true, it->second); + } +} +} // namespace linker +} // namespace art diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h new file mode 100644 index 0000000000..1727d529fc --- /dev/null +++ b/compiler/linker/multi_oat_relative_patcher.h @@ -0,0 +1,146 @@ +/* + * 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_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ +#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ + +#include "arch/instruction_set.h" +#include "method_reference.h" +#include "relative_patcher.h" +#include "safe_map.h" + +namespace art { + +class CompiledMethod; +class LinkerPatch; +class InstructionSetFeatures; + +namespace linker { + +// MultiOatRelativePatcher is a helper class for handling patching across +// any number of oat files. It provides storage for method code offsets +// and wraps RelativePatcher calls, adjusting relative offsets according +// to the value set by SetAdjustment(). +class MultiOatRelativePatcher FINAL { + public: + using const_iterator = + SafeMap<MethodReference, uint32_t, MethodReferenceComparator>::const_iterator; + + MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features); + + // Mark the start of a new oat file (for statistics retrieval) and set the + // adjustment for a new oat file to apply to all relative offsets that are + // passed to the MultiOatRelativePatcher. + // + // The adjustment should be the global offset of the base from which relative + // offsets are calculated, such as the start of .rodata for the current oat file. + // It must must never point directly to a method's code to avoid relative offsets + // with value 0 because this value is used as a missing offset indication in + // GetOffset() and an error indication in WriteThunks(). Additionally, it must be + // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP. + void StartOatFile(uint32_t adjustment); + + // Get relative offset. Returns 0 when the offset has not been set yet. + uint32_t GetOffset(MethodReference method_ref) { + auto it = method_offset_map_.map.find(method_ref); + return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u; + } + + // Set the offset. + void SetOffset(MethodReference method_ref, uint32_t offset) { + method_offset_map_.map.Put(method_ref, offset + adjustment_); + } + + // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment. + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, + MethodReference method_ref) { + offset += adjustment_; + offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref); + offset -= adjustment_; + return offset; + } + + // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment. + uint32_t ReserveSpaceEnd(uint32_t offset) { + offset += adjustment_; + offset = relative_patcher_->ReserveSpaceEnd(offset); + offset -= adjustment_; + return offset; + } + + // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment. + uint32_t WriteThunks(OutputStream* out, uint32_t offset) { + offset += adjustment_; + offset = relative_patcher_->WriteThunks(out, offset); + if (offset != 0u) { // 0u indicates write error. + offset -= adjustment_; + } + return offset; + } + + // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment. + void PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { + patch_offset += adjustment_; + target_offset += adjustment_; + relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset); + } + + // Wrapper around RelativePatcher::PatchDexCacheReference(), doing offset adjustment. + void PatchDexCacheReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) { + patch_offset += adjustment_; + target_offset += adjustment_; + relative_patcher_->PatchDexCacheReference(code, patch, patch_offset, target_offset); + } + + // Wrappers around RelativePatcher for statistics retrieval. + uint32_t CodeAlignmentSize() const; + uint32_t RelativeCallThunksSize() const; + uint32_t MiscThunksSize() const; + + private: + // Map method reference to assigned offset. + // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. + class MethodOffsetMap : public linker::RelativePatcherTargetProvider { + public: + std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE; + SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map; + }; + + MethodOffsetMap method_offset_map_; + std::unique_ptr<RelativePatcher> relative_patcher_; + uint32_t adjustment_; + InstructionSet instruction_set_; + + uint32_t start_size_code_alignment_; + uint32_t start_size_relative_call_thunks_; + uint32_t start_size_misc_thunks_; + + friend class MultiOatRelativePatcherTest; + + DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher); +}; + +} // namespace linker +} // namespace art + +#endif // ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_ diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc new file mode 100644 index 0000000000..792cdfe8e9 --- /dev/null +++ b/compiler/linker/multi_oat_relative_patcher_test.cc @@ -0,0 +1,299 @@ +/* + * 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 "compiled_method.h" +#include "gtest/gtest.h" +#include "multi_oat_relative_patcher.h" +#include "vector_output_stream.h" + +namespace art { +namespace linker { + +static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u); + +static bool EqualRef(MethodReference lhs, MethodReference rhs) { + return lhs.dex_file == rhs.dex_file && lhs.dex_method_index == rhs.dex_method_index; +} + +class MultiOatRelativePatcherTest : public testing::Test { + protected: + class MockPatcher : public RelativePatcher { + public: + MockPatcher() { } + + uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method ATTRIBUTE_UNUSED, + MethodReference method_ref) OVERRIDE { + last_reserve_offset_ = offset; + last_reserve_method_ = method_ref; + offset += next_reserve_adjustment_; + next_reserve_adjustment_ = 0u; + return offset; + } + + uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE { + last_reserve_offset_ = offset; + last_reserve_method_ = kNullMethodRef; + offset += next_reserve_adjustment_; + next_reserve_adjustment_ = 0u; + return offset; + } + + uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE { + last_write_offset_ = offset; + if (next_write_alignment_ != 0u) { + offset += next_write_alignment_; + bool success = WriteCodeAlignment(out, next_write_alignment_); + CHECK(success); + next_write_alignment_ = 0u; + } + if (next_write_call_thunk_ != 0u) { + offset += next_write_call_thunk_; + std::vector<uint8_t> thunk(next_write_call_thunk_, 'c'); + bool success = WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk)); + CHECK(success); + next_write_call_thunk_ = 0u; + } + if (next_write_misc_thunk_ != 0u) { + offset += next_write_misc_thunk_; + std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm'); + bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk)); + CHECK(success); + next_write_misc_thunk_ = 0u; + } + return offset; + } + + void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE { + last_literal_offset_ = literal_offset; + last_patch_offset_ = patch_offset; + last_target_offset_ = target_offset; + } + + void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE { + last_literal_offset_ = patch.LiteralOffset(); + last_patch_offset_ = patch_offset; + last_target_offset_ = target_offset; + } + + uint32_t last_reserve_offset_ = 0u; + MethodReference last_reserve_method_ = kNullMethodRef; + uint32_t next_reserve_adjustment_ = 0u; + + uint32_t last_write_offset_ = 0u; + uint32_t next_write_alignment_ = 0u; + uint32_t next_write_call_thunk_ = 0u; + uint32_t next_write_misc_thunk_ = 0u; + + uint32_t last_literal_offset_ = 0u; + uint32_t last_patch_offset_ = 0u; + uint32_t last_target_offset_ = 0u; + }; + + MultiOatRelativePatcherTest() + : instruction_set_features_(InstructionSetFeatures::FromCppDefines()), + patcher_(kRuntimeISA, instruction_set_features_.get()) { + std::unique_ptr<MockPatcher> mock(new MockPatcher()); + mock_ = mock.get(); + patcher_.relative_patcher_ = std::move(mock); + } + + std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; + MultiOatRelativePatcher patcher_; + MockPatcher* mock_; +}; + +TEST_F(MultiOatRelativePatcherTest, Offsets) { + const DexFile* dex_file = reinterpret_cast<const DexFile*>(1); + MethodReference ref1(dex_file, 1u); + MethodReference ref2(dex_file, 2u); + EXPECT_EQ(0u, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + EXPECT_EQ(0u, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t off1 = 0x1234; + patcher_.SetOffset(ref1, off1); + EXPECT_EQ(off1, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t adjustment2 = 0x30000; + patcher_.StartOatFile(adjustment2); + EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1)); + EXPECT_EQ(0u, patcher_.GetOffset(ref2)); + + uint32_t off2 = 0x4321; + patcher_.SetOffset(ref2, off2); + EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1)); + EXPECT_EQ(off2, patcher_.GetOffset(ref2)); + + uint32_t adjustment3 = 0x78000; + patcher_.StartOatFile(adjustment3); + EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1)); + EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2)); +} + +TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) { + const DexFile* dex_file = reinterpret_cast<const DexFile*>(1); + MethodReference ref1(dex_file, 1u); + MethodReference ref2(dex_file, 2u); + MethodReference ref3(dex_file, 3u); + const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + + uint32_t method1_offset = 0x100; + uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1); + ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(ref1, mock_->last_reserve_method_)); + ASSERT_EQ(method1_offset, method1_offset_check); + + uint32_t method2_offset = 0x1230; + uint32_t method2_reserve_adjustment = 0x10; + mock_->next_reserve_adjustment_ = method2_reserve_adjustment; + uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2); + ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(ref2, mock_->last_reserve_method_)); + ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted); + + uint32_t end1_offset = 0x4320; + uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset); + ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_)); + ASSERT_EQ(end1_offset, end1_offset_check); + + uint32_t adjustment2 = 0xd000; + patcher_.StartOatFile(adjustment2); + + uint32_t method3_offset = 0xf00; + uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3); + ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(ref3, mock_->last_reserve_method_)); + ASSERT_EQ(method3_offset, method3_offset_check); + + uint32_t end2_offset = 0x2400; + uint32_t end2_reserve_adjustment = 0x20; + mock_->next_reserve_adjustment_ = end2_reserve_adjustment; + uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset); + ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_); + ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_)); + ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted); +} + +TEST_F(MultiOatRelativePatcherTest, Write) { + std::vector<uint8_t> output; + VectorOutputStream vos("output", &output); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + + uint32_t method1_offset = 0x100; + uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset); + ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_); + ASSERT_EQ(method1_offset, method1_offset_check); + vos.WriteFully("1", 1); // Mark method1. + + uint32_t method2_offset = 0x1230; + uint32_t method2_alignment_size = 1; + uint32_t method2_call_thunk_size = 2; + mock_->next_write_alignment_ = method2_alignment_size; + mock_->next_write_call_thunk_ = method2_call_thunk_size; + uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset); + ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_); + ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size, + method2_offset_adjusted); + vos.WriteFully("2", 1); // Mark method2. + + EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize()); + EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize()); + + uint32_t adjustment2 = 0xd000; + patcher_.StartOatFile(adjustment2); + + uint32_t method3_offset = 0xf00; + uint32_t method3_alignment_size = 2; + uint32_t method3_misc_thunk_size = 1; + mock_->next_write_alignment_ = method3_alignment_size; + mock_->next_write_misc_thunk_ = method3_misc_thunk_size; + uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset); + ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_); + ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size, + method3_offset_adjusted); + vos.WriteFully("3", 1); // Mark method3. + + EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize()); + EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize()); + + uint8_t expected_output[] = { + '1', + 0, 'c', 'c', '2', + 0, 0, 'm', '3', + }; + ASSERT_EQ(arraysize(expected_output), output.size()); + for (size_t i = 0; i != arraysize(expected_output); ++i) { + ASSERT_EQ(expected_output[i], output[i]) << i; + } +} + +TEST_F(MultiOatRelativePatcherTest, Patch) { + std::vector<uint8_t> code(16); + + uint32_t adjustment1 = 0x1000; + patcher_.StartOatFile(adjustment1); + + uint32_t method1_literal_offset = 4u; + uint32_t method1_patch_offset = 0x1234u; + uint32_t method1_target_offset = 0x8888u; + patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset); + DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_); + DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_); + DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_); + + uint32_t method2_literal_offset = 12u; + uint32_t method2_patch_offset = 0x7654u; + uint32_t method2_target_offset = 0xccccu; + LinkerPatch method2_patch = + LinkerPatch::DexCacheArrayPatch(method2_literal_offset, nullptr, 0u, 1234u); + patcher_.PatchDexCacheReference( + &code, method2_patch, method2_patch_offset, method2_target_offset); + DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_); + DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_); + DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_); + + uint32_t adjustment2 = 0xd000; + patcher_.StartOatFile(adjustment2); + + uint32_t method3_literal_offset = 8u; + uint32_t method3_patch_offset = 0x108u; + uint32_t method3_target_offset = 0x200u; + patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset); + DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_); + DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_); + DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_); +} + +} // namespace linker +} // namespace art diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc index 82702dcf25..6727c17583 100644 --- a/compiler/linker/relative_patcher.cc +++ b/compiler/linker/relative_patcher.cc @@ -34,7 +34,8 @@ namespace art { namespace linker { std::unique_ptr<RelativePatcher> RelativePatcher::Create( - InstructionSet instruction_set, const InstructionSetFeatures* features, + InstructionSet instruction_set, + const InstructionSetFeatures* features, RelativePatcherTargetProvider* provider) { class RelativePatcherNone FINAL : public RelativePatcher { public: diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h index 8a9f3f8364..ba374512a1 100644 --- a/compiler/linker/relative_patcher.h +++ b/compiler/linker/relative_patcher.h @@ -83,23 +83,31 @@ class RelativePatcher { } // Reserve space for thunks if needed before a method, return adjusted offset. - virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method, + virtual uint32_t ReserveSpace(uint32_t offset, + const CompiledMethod* compiled_method, MethodReference method_ref) = 0; // Reserve space for thunks if needed after the last method, return adjusted offset. + // The caller may use this method to preemptively force thunk space reservation and + // then resume reservation for more methods. This is useful when there is a gap in + // the .text segment, for example when going to the next oat file for multi-image. virtual uint32_t ReserveSpaceEnd(uint32_t offset) = 0; - // Write relative call thunks if needed, return adjusted offset. + // Write relative call thunks if needed, return adjusted offset. Returns 0 on write failure. virtual uint32_t WriteThunks(OutputStream* out, uint32_t offset) = 0; // Patch method code. The input displacement is relative to the patched location, // the patcher may need to adjust it if the correct base is different. - virtual void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) = 0; + virtual void PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) = 0; // Patch a reference to a dex cache location. - virtual void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) = 0; + virtual void PatchDexCacheReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) = 0; protected: RelativePatcher() diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index bf8e786f64..704135a7b5 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -44,10 +44,22 @@ class RelativePatcherTest : public testing::Test { : compiler_options_(), verification_results_(&compiler_options_), inliner_map_(), - driver_(&compiler_options_, &verification_results_, &inliner_map_, - Compiler::kQuick, instruction_set, nullptr, - false, nullptr, nullptr, nullptr, 1u, - false, false, nullptr, -1, nullptr, nullptr), + driver_(&compiler_options_, + &verification_results_, + &inliner_map_, + Compiler::kQuick, + instruction_set, + /* instruction_set_features*/ nullptr, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 1u, + /* dump_stats */ false, + /* dump_passes */ false, + /* timer */ nullptr, + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr), error_msg_(), instruction_set_(instruction_set), features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), @@ -138,8 +150,10 @@ class RelativePatcherTest : public testing::Test { offset + patch.LiteralOffset(), target_offset); } else if (patch.Type() == kLinkerPatchDexCacheArray) { uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset(); - patcher_->PatchDexCacheReference(&patched_code_, patch, - offset + patch.LiteralOffset(), target_offset); + patcher_->PatchDexCacheReference(&patched_code_, + patch, + offset + patch.LiteralOffset(), + target_offset); } else { LOG(FATAL) << "Bad patch type."; } diff --git a/compiler/linker/x86/relative_patcher_x86.h b/compiler/linker/x86/relative_patcher_x86.h index 0c881f00ba..ddc244c269 100644 --- a/compiler/linker/x86/relative_patcher_x86.h +++ b/compiler/linker/x86/relative_patcher_x86.h @@ -26,8 +26,10 @@ class X86RelativePatcher FINAL : public X86BaseRelativePatcher { public: X86RelativePatcher() { } - void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; }; } // namespace linker diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/compiler/linker/x86/relative_patcher_x86_base.cc index bc285a7849..bf3a648218 100644 --- a/compiler/linker/x86/relative_patcher_x86_base.cc +++ b/compiler/linker/x86/relative_patcher_x86_base.cc @@ -34,8 +34,10 @@ uint32_t X86BaseRelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED, return offset; // No thunks added; no limit on relative call distance. } -void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) { DCHECK_LE(literal_offset + 4u, code->size()); // Unsigned arithmetic with its well-defined overflow behavior is just fine here. uint32_t displacement = target_offset - patch_offset; diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/compiler/linker/x86/relative_patcher_x86_base.h index 9200709398..ca83a72f48 100644 --- a/compiler/linker/x86/relative_patcher_x86_base.h +++ b/compiler/linker/x86/relative_patcher_x86_base.h @@ -29,8 +29,10 @@ class X86BaseRelativePatcher : public RelativePatcher { MethodReference method_ref) OVERRIDE; uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE; uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE; - void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector<uint8_t>* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; protected: X86BaseRelativePatcher() { } diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc index 598f3ac4a8..e571f50d2f 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64.cc +++ b/compiler/linker/x86_64/relative_patcher_x86_64.cc @@ -23,7 +23,8 @@ namespace linker { void X86_64RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) { + uint32_t patch_offset, + uint32_t target_offset) { DCHECK_LE(patch.LiteralOffset() + 4u, code->size()); // Unsigned arithmetic with its well-defined overflow behavior is just fine here. uint32_t displacement = target_offset - patch_offset; diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/compiler/linker/x86_64/relative_patcher_x86_64.h index af687b4a2f..feecb3a2ad 100644 --- a/compiler/linker/x86_64/relative_patcher_x86_64.h +++ b/compiler/linker/x86_64/relative_patcher_x86_64.h @@ -26,8 +26,10 @@ class X86_64RelativePatcher FINAL : public X86BaseRelativePatcher { public: X86_64RelativePatcher() { } - void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector<uint8_t>* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; }; } // namespace linker diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 894d29ee99..14fd1054c3 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -31,6 +31,7 @@ #include "elf_writer.h" #include "elf_writer_quick.h" #include "entrypoints/quick/quick_entrypoints.h" +#include "linker/multi_oat_relative_patcher.h" #include "linker/vector_output_stream.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -111,17 +112,16 @@ class OatTest : public CommonCompilerTest { compiler_kind, insn_set, insn_features_.get(), - false, - nullptr, - nullptr, - nullptr, - 2, - true, - true, + /* boot_image */ false, + /* image_classes */ nullptr, + /* compiled_classes */ nullptr, + /* compiled_methods */ nullptr, + /* thread_count */ 2, + /* dump_stats */ true, + /* dump_passes */ true, timer_.get(), - -1, - nullptr, - nullptr)); + /* swap_fd */ -1, + /* profile_compilation_info */ nullptr)); } bool WriteElf(File* file, @@ -200,7 +200,13 @@ class OatTest : public CommonCompilerTest { ScopedObjectAccess soa(Thread::Current()); class_linker->RegisterDexFile(*dex_file, runtime->GetLinearAlloc()); } - oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files); + linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(), + instruction_set_features_.get()); + oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files, &patcher); + size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset(); + size_t text_size = oat_writer.GetSize() - rodata_size; + elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize()); + if (!oat_writer.WriteRodata(rodata)) { return false; } @@ -216,7 +222,6 @@ class OatTest : public CommonCompilerTest { return false; } - elf_writer->SetBssSize(oat_writer.GetBssSize()); elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations()); @@ -415,7 +420,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/oat_writer.cc b/compiler/oat_writer.cc index 47dcfd56f8..c60b02a227 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -38,8 +38,8 @@ #include "gc/space/space.h" #include "handle_scope-inl.h" #include "image_writer.h" +#include "linker/multi_oat_relative_patcher.h" #include "linker/output_stream.h" -#include "linker/relative_patcher.h" #include "mirror/array.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" @@ -292,7 +292,8 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings) size_oat_class_status_(0), size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), - method_offset_map_() { + relative_patcher_(nullptr), + absolute_patch_locations_() { } bool OatWriter::AddDexFileSource(const char* filename, @@ -438,21 +439,21 @@ bool OatWriter::WriteAndOpenDexFiles( void OatWriter::PrepareLayout(const CompilerDriver* compiler, ImageWriter* image_writer, - const std::vector<const DexFile*>& dex_files) { + const std::vector<const DexFile*>& dex_files, + linker::MultiOatRelativePatcher* relative_patcher) { CHECK(write_state_ == WriteState::kPrepareLayout); - dex_files_ = &dex_files; - compiler_driver_ = compiler; image_writer_ = image_writer; + dex_files_ = &dex_files; + relative_patcher_ = relative_patcher; + SetMultiOatRelativePatcherAdjustment(); + if (compiling_boot_image_) { CHECK(image_writer_ != nullptr); } InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); CHECK_EQ(instruction_set, oat_header_->GetInstructionSet()); - const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures(); - relative_patcher_ = linker::RelativePatcher::Create(instruction_set, features, - &method_offset_map_); uint32_t offset = size_; { @@ -727,13 +728,11 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { // Deduplicate code arrays if we are not producing debuggable code. bool deduped = false; MethodReference method_ref(dex_file_, it.GetMemberIndex()); - auto method_lb = writer_->method_offset_map_.map.lower_bound(method_ref); if (debuggable_) { - if (method_lb != writer_->method_offset_map_.map.end() && - !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) { + quick_code_offset = writer_->relative_patcher_->GetOffset(method_ref); + if (quick_code_offset != 0u) { // Duplicate methods, we want the same code for both of them so that the oat writer puts // the same code in both ArtMethods so that we do not get different oat code at runtime. - quick_code_offset = method_lb->second; deduped = true; } else { quick_code_offset = NewQuickCodeOffset(compiled_method, it, thumb_offset); @@ -750,14 +749,14 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } if (code_size != 0) { - if (method_lb != writer_->method_offset_map_.map.end() && - !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) { + if (writer_->relative_patcher_->GetOffset(method_ref) != 0u) { // TODO: Should this be a hard failure? LOG(WARNING) << "Multiple definitions of " << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file) - << " offsets " << method_lb->second << " " << quick_code_offset; + << " offsets " << writer_->relative_patcher_->GetOffset(method_ref) + << " " << quick_code_offset; } else { - writer_->method_offset_map_.map.PutBefore(method_lb, method_ref, quick_code_offset); + writer_->relative_patcher_->SetOffset(method_ref, quick_code_offset); } } @@ -1106,27 +1105,29 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { patched_code_.assign(quick_code.begin(), quick_code.end()); quick_code = ArrayRef<const uint8_t>(patched_code_); for (const LinkerPatch& patch : compiled_method->GetPatches()) { + uint32_t literal_offset = patch.LiteralOffset(); if (patch.Type() == kLinkerPatchCallRelative) { // NOTE: Relative calls across oat files are not supported. uint32_t target_offset = GetTargetOffset(patch); - uint32_t literal_offset = patch.LiteralOffset(); - writer_->relative_patcher_->PatchCall(&patched_code_, literal_offset, - offset_ + literal_offset, target_offset); + writer_->relative_patcher_->PatchCall(&patched_code_, + literal_offset, + offset_ + literal_offset, + target_offset); } else if (patch.Type() == kLinkerPatchDexCacheArray) { uint32_t target_offset = GetDexCacheOffset(patch); - uint32_t literal_offset = patch.LiteralOffset(); - writer_->relative_patcher_->PatchDexCacheReference(&patched_code_, patch, + writer_->relative_patcher_->PatchDexCacheReference(&patched_code_, + patch, offset_ + literal_offset, target_offset); } else if (patch.Type() == kLinkerPatchCall) { uint32_t target_offset = GetTargetOffset(patch); - PatchCodeAddress(&patched_code_, patch.LiteralOffset(), target_offset); + PatchCodeAddress(&patched_code_, literal_offset, target_offset); } else if (patch.Type() == kLinkerPatchMethod) { ArtMethod* method = GetTargetMethod(patch); - PatchMethodAddress(&patched_code_, patch.LiteralOffset(), method); + PatchMethodAddress(&patched_code_, literal_offset, method); } else if (patch.Type() == kLinkerPatchType) { mirror::Class* type = GetTargetType(patch); - PatchObjectAddress(&patched_code_, patch.LiteralOffset(), type); + PatchObjectAddress(&patched_code_, literal_offset, type); } } } @@ -1172,16 +1173,16 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } uint32_t GetTargetOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) { - auto target_it = writer_->method_offset_map_.map.find(patch.TargetMethod()); - uint32_t target_offset = - (target_it != writer_->method_offset_map_.map.end()) ? target_it->second : 0u; - // If there's no compiled code, point to the correct trampoline. + uint32_t target_offset = writer_->relative_patcher_->GetOffset(patch.TargetMethod()); + // If there's no new compiled code, either we're compiling an app and the target method + // is in the boot image, or we need to point to the correct trampoline. if (UNLIKELY(target_offset == 0)) { ArtMethod* target = GetTargetMethod(patch); DCHECK(target != nullptr); size_t size = GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet()); const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(size); if (oat_code_offset != 0) { + DCHECK(!writer_->HasBootImage()); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset)); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset)); DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset)); @@ -1206,11 +1207,10 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) { if (writer_->HasBootImage()) { - auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>( - patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset()); - const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_); - const uint8_t* oat_data = - writer_->image_writer_->GetOatFileBegin(oat_filename) + file_offset_; + uintptr_t element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<uintptr_t>( + patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset()); + size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_); + uintptr_t oat_data = writer_->image_writer_->GetOatDataBegin(oat_index); return element - oat_data; } else { size_t start = writer_->dex_cache_arrays_offsets_.Get(patch.TargetDexCacheDexFile()); @@ -1270,9 +1270,13 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { SHARED_REQUIRES(Locks::mutator_lock_) { uint32_t address = target_offset; if (writer_->HasBootImage()) { - const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_); - address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin(oat_filename) + - writer_->oat_data_offset_ + target_offset); + size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_); + // TODO: Clean up offset types. + // The target_offset must be treated as signed for cross-oat patching. + const void* target = reinterpret_cast<const void*>( + writer_->image_writer_->GetOatDataBegin(oat_index) + + static_cast<int32_t>(target_offset)); + address = PointerToLowMemUInt32(target); } DCHECK_LE(offset + 4, code->size()); uint8_t* data = &(*code)[offset]; @@ -1540,6 +1544,8 @@ bool OatWriter::WriteRodata(OutputStream* out) { bool OatWriter::WriteCode(OutputStream* out) { CHECK(write_state_ == WriteState::kWriteText); + SetMultiOatRelativePatcherAdjustment(); + const size_t file_offset = oat_data_offset_; size_t relative_offset = oat_header_->GetExecutableOffset(); DCHECK_OFFSET(); @@ -1781,7 +1787,7 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream* out, return relative_offset; } -bool OatWriter::GetOatDataOffset(OutputStream* out) { +bool OatWriter::RecordOatDataOffset(OutputStream* out) { // Get the elf file offset of the oat file. const off_t raw_file_offset = out->Seek(0, kSeekCurrent); if (raw_file_offset == static_cast<off_t>(-1)) { @@ -1833,7 +1839,7 @@ bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) { TimingLogger::ScopedTiming split("WriteDexFiles", timings_); // Get the elf file offset of the oat file. - if (!GetOatDataOffset(rodata)) { + if (!RecordOatDataOffset(rodata)) { return false; } @@ -2261,12 +2267,15 @@ bool OatWriter::WriteData(OutputStream* out, const void* data, size_t size) { return out->WriteFully(data, size); } -std::pair<bool, uint32_t> OatWriter::MethodOffsetMap::FindMethodOffset(MethodReference ref) { - auto it = map.find(ref); - if (it == map.end()) { - return std::pair<bool, uint32_t>(false, 0u); - } else { - return std::pair<bool, uint32_t>(true, it->second); +void OatWriter::SetMultiOatRelativePatcherAdjustment() { + DCHECK(dex_files_ != nullptr); + DCHECK(relative_patcher_ != nullptr); + DCHECK_NE(oat_data_offset_, 0u); + if (image_writer_ != nullptr && !dex_files_->empty()) { + // The oat data begin may not be initialized yet but the oat file offset is ready. + size_t oat_index = image_writer_->GetOatIndexForDexFile(dex_files_->front()); + size_t elf_file_offset = image_writer_->GetOatFileOffset(oat_index); + relative_patcher_->StartOatFile(elf_file_offset + oat_data_offset_); } } diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 5a55fc6c95..74aab4efd0 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -47,6 +47,10 @@ namespace debug { struct MethodDebugInfo; } // namespace debug +namespace linker { +class MultiOatRelativePatcher; +} // namespace linker + // OatHeader variable length with count of D OatDexFiles // // OatDexFile[0] one variable sized OatDexFile with offsets to Dex and OatClasses @@ -153,7 +157,8 @@ class OatWriter { // Prepare layout of remaining data. void PrepareLayout(const CompilerDriver* compiler, ImageWriter* image_writer, - const std::vector<const DexFile*>& dex_files); + const std::vector<const DexFile*>& dex_files, + linker::MultiOatRelativePatcher* relative_patcher); // Write the rest of .rodata section (ClassOffsets[], OatClass[], maps). bool WriteRodata(OutputStream* out); // Write the code to the .text section. @@ -187,6 +192,10 @@ class OatWriter { return bss_size_; } + size_t GetOatDataOffset() const { + return oat_data_offset_; + } + ArrayRef<const uintptr_t> GetAbsolutePatchLocations() const { return ArrayRef<const uintptr_t>(absolute_patch_locations_); } @@ -249,7 +258,7 @@ class OatWriter { size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset); - bool GetOatDataOffset(OutputStream* out); + bool RecordOatDataOffset(OutputStream* out); bool ReadDexFileHeader(File* file, OatDexFile* oat_dex_file); bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); bool WriteDexFiles(OutputStream* rodata, File* file); @@ -268,6 +277,7 @@ class OatWriter { const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); bool WriteData(OutputStream* out, const void* data, size_t size); + void SetMultiOatRelativePatcherAdjustment(); enum class WriteState { kAddingDexFileSources, @@ -358,20 +368,12 @@ class OatWriter { uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; - std::unique_ptr<linker::RelativePatcher> relative_patcher_; + // The helper for processing relative patches is external so that we can patch across oat files. + linker::MultiOatRelativePatcher* relative_patcher_; // The locations of absolute patches relative to the start of the executable section. dchecked_vector<uintptr_t> absolute_patch_locations_; - // Map method reference to assigned offset. - // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. - class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider { - public: - std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE; - SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map; - }; - MethodOffsetMap method_offset_map_; - DISALLOW_COPY_AND_ASSIGN(OatWriter); }; 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/builder.cc b/compiler/optimizing/builder.cc index 05e1356ed8..35ec7d41ff 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -368,7 +368,6 @@ GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item if (native_debuggable) { const uint32_t num_instructions = code_item.insns_size_in_code_units_; native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false); - native_debug_info_locations->ClearAllBits(); FindNativeDebugInfoLocations(code_item, native_debug_info_locations); } @@ -443,23 +442,15 @@ void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_i } }; dex_file_->DecodeDebugPositionInfo(&code_item, Callback::Position, locations); - // Add native debug info at the start of every basic block. - for (uint32_t pc = 0; pc < code_item.insns_size_in_code_units_; pc++) { - if (FindBlockStartingAt(pc) != nullptr) { - locations->SetBit(pc); - } - } // Instruction-specific tweaks. const Instruction* const begin = Instruction::At(code_item.insns_); const Instruction* const end = begin->RelativeAt(code_item.insns_size_in_code_units_); for (const Instruction* inst = begin; inst < end; inst = inst->Next()) { switch (inst->Opcode()) { - case Instruction::MOVE_EXCEPTION: - case Instruction::MOVE_RESULT: - case Instruction::MOVE_RESULT_WIDE: - case Instruction::MOVE_RESULT_OBJECT: { - // The compiler checks that there are no instructions before those. - // So generate HNativeDebugInfo after them instead. + case Instruction::MOVE_EXCEPTION: { + // Stop in native debugger after the exception has been moved. + // The compiler also expects the move at the start of basic block so + // we do not want to interfere by inserting native-debug-info before it. locations->ClearBit(inst->GetDexPc(code_item.insns_)); const Instruction* next = inst->Next(); if (next < end) { diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index c2c8ccfc56..967d156cf6 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -195,6 +195,8 @@ void CodeGenerator::GenerateSlowPaths() { if (disasm_info_ != nullptr) { code_start = GetAssembler()->CodeSize(); } + // Record the dex pc at start of slow path (required for java line number mapping). + MaybeRecordNativeDebugInfo(nullptr /* instruction */, slow_path->GetDexPc()); slow_path->EmitNativeCode(this); if (disasm_info_ != nullptr) { disasm_info_->AddSlowPathInterval(slow_path, code_start, GetAssembler()->CodeSize()); @@ -226,6 +228,10 @@ void CodeGenerator::Compile(CodeAllocator* allocator) { // errors where we reference that label. if (block->IsSingleJump()) continue; Bind(block); + // This ensures that we have correct native line mapping for all native instructions. + // It is necessary to make stepping over a statement work. Otherwise, any initial + // instructions (e.g. moves) would be assumed to be the start of next statement. + MaybeRecordNativeDebugInfo(nullptr /* instruction */, block->GetDexPc()); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); DisassemblyScope disassembly_scope(current, *this); @@ -733,7 +739,8 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t native_pc = GetAssembler()->CodeSize(); if (instruction == nullptr) { - // For stack overflow checks. + // For stack overflow checks and native-debug-info entries without dex register + // mapping (i.e. start of basic block or start of slow path). stack_map_stream_.BeginStackMapEntry(outer_dex_pc, native_pc, 0, 0, 0, 0); stack_map_stream_.EndStackMapEntry(); return; @@ -808,6 +815,16 @@ bool CodeGenerator::HasStackMapAtCurrentPc() { return count > 0 && stack_map_stream_.GetStackMap(count - 1).native_pc_offset == pc; } +void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc) { + if (GetCompilerOptions().GetNativeDebuggable() && dex_pc != kNoDexPc) { + if (HasStackMapAtCurrentPc()) { + // Ensure that we do not collide with the stack map of the previous instruction. + GenerateNop(); + } + RecordPcInfo(instruction, dex_pc); + } +} + void CodeGenerator::RecordCatchBlockInfo() { ArenaAllocator* arena = graph_->GetArena(); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 49c193e7bf..9297fc956f 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -69,7 +69,7 @@ class CodeAllocator { class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> { public: - SlowPathCode() { + explicit SlowPathCode(HInstruction* instruction) : instruction_(instruction) { for (size_t i = 0; i < kMaximumNumberOfExpectedRegisters; ++i) { saved_core_stack_offsets_[i] = kRegisterNotSaved; saved_fpu_stack_offsets_[i] = kRegisterNotSaved; @@ -106,9 +106,15 @@ class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> { Label* GetEntryLabel() { return &entry_label_; } Label* GetExitLabel() { return &exit_label_; } + uint32_t GetDexPc() const { + return instruction_ != nullptr ? instruction_->GetDexPc() : kNoDexPc; + } + protected: static constexpr size_t kMaximumNumberOfExpectedRegisters = 32; static constexpr uint32_t kRegisterNotSaved = -1; + // The instruction where this slow path is happening. + HInstruction* instruction_; uint32_t saved_core_stack_offsets_[kMaximumNumberOfExpectedRegisters]; uint32_t saved_fpu_stack_offsets_[kMaximumNumberOfExpectedRegisters]; @@ -267,6 +273,8 @@ class CodeGenerator { void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr); // Check whether we have already recorded mapping at this PC. bool HasStackMapAtCurrentPc(); + // Record extra stack maps if we support native debugging. + void MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc); bool CanMoveNullCheckToUser(HNullCheck* null_check); void MaybeRecordImplicitNullCheck(HInstruction* instruction); @@ -440,6 +448,8 @@ class CodeGenerator { // Copy the result of a call into the given target. virtual void MoveFromReturnRegister(Location trg, Primitive::Type type) = 0; + virtual void GenerateNop() = 0; + protected: // Method patch info used for recording locations of required linker patches and // target methods. The target method can be used for various purposes, whether for diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 87f52c6f21..cdbb9c31aa 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -64,7 +64,7 @@ static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7; class NullCheckSlowPathARM : public SlowPathCode { public: - explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {} + explicit NullCheckSlowPathARM(HNullCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); @@ -83,13 +83,12 @@ class NullCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; } private: - HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM); }; class DivZeroCheckSlowPathARM : public SlowPathCode { public: - explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); @@ -108,14 +107,13 @@ class DivZeroCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM); }; class SuspendCheckSlowPathARM : public SlowPathCode { public: SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); @@ -144,7 +142,6 @@ class SuspendCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -157,7 +154,7 @@ class SuspendCheckSlowPathARM : public SlowPathCode { class BoundsCheckSlowPathARM : public SlowPathCode { public: explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); @@ -188,8 +185,6 @@ class BoundsCheckSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM); }; @@ -199,7 +194,7 @@ class LoadClassSlowPathARM : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -253,7 +248,7 @@ class LoadClassSlowPathARM : public SlowPathCode { class LoadStringSlowPathARM : public SlowPathCode { public: - explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -264,7 +259,8 @@ class LoadStringSlowPathARM : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index); arm_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); @@ -277,15 +273,13 @@ class LoadStringSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM); }; class TypeCheckSlowPathARM : public SlowPathCode { public: TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCode(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -340,7 +334,6 @@ class TypeCheckSlowPathARM : public SlowPathCode { bool IsFatal() const OVERRIDE { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM); @@ -349,7 +342,7 @@ class TypeCheckSlowPathARM : public SlowPathCode { class DeoptimizationSlowPathARM : public SlowPathCode { public: explicit DeoptimizationSlowPathARM(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); @@ -365,13 +358,12 @@ class DeoptimizationSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM); }; class ArraySetSlowPathARM : public SlowPathCode { public: - explicit ArraySetSlowPathARM(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathARM(HInstruction* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -410,8 +402,6 @@ class ArraySetSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM); }; @@ -419,7 +409,7 @@ class ArraySetSlowPathARM : public SlowPathCode { class ReadBarrierMarkSlowPathARM : public SlowPathCode { public: ReadBarrierMarkSlowPathARM(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCode(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -458,7 +448,6 @@ class ReadBarrierMarkSlowPathARM : public SlowPathCode { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -474,7 +463,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCode(instruction), out_(out), ref_(ref), obj_(obj), @@ -629,7 +618,6 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -646,7 +634,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { class ReadBarrierForRootSlowPathARM : public SlowPathCode { public: ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCode(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -679,7 +667,6 @@ class ReadBarrierForRootSlowPathARM : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; @@ -1557,11 +1544,11 @@ void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorARM::GenerateNop() { + __ nop(); } void LocationsBuilderARM::HandleCondition(HCondition* cond) { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index cfd7a3bc14..2e4dc1e014 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -510,6 +510,8 @@ class CodeGeneratorARM : public CodeGenerator { // artReadBarrierForRootSlow. void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root); + void GenerateNop(); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 435ae5e954..814f8b4d51 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -219,7 +219,7 @@ void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSum class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -246,14 +246,12 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM64"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64); }; class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); @@ -272,7 +270,6 @@ class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM64"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64); }; @@ -282,7 +279,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeARM64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -337,7 +334,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit LoadStringSlowPathARM64(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathARM64(HLoadString* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -348,7 +345,8 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ Mov(calling_convention.GetRegisterAt(0).W(), string_index); arm64_codegen->InvokeRuntime( QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>(); @@ -362,14 +360,12 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64); }; class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {} + explicit NullCheckSlowPathARM64(HNullCheck* instr) : SlowPathCodeARM64(instr) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); @@ -388,15 +384,13 @@ class NullCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM64"; } private: - HNullCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64); }; class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { public: SuspendCheckSlowPathARM64(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCodeARM64(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); @@ -425,7 +419,6 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM64"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -438,7 +431,7 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 { class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { public: TypeCheckSlowPathARM64(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCodeARM64(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -487,7 +480,6 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { bool IsFatal() const { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64); @@ -496,7 +488,7 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 { public: explicit DeoptimizationSlowPathARM64(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); @@ -512,13 +504,12 @@ class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM64"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64); }; class ArraySetSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit ArraySetSlowPathARM64(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathARM64(HInstruction* instruction) : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -557,8 +548,6 @@ class ArraySetSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM64"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM64); }; @@ -588,7 +577,7 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { public: ReadBarrierMarkSlowPathARM64(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCodeARM64(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -627,7 +616,6 @@ class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -643,7 +631,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCodeARM64(instruction), out_(out), ref_(ref), obj_(obj), @@ -804,7 +792,6 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -821,7 +808,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 { public: ReadBarrierForRootSlowPathARM64(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCodeARM64(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -865,7 +852,6 @@ class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM64"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; @@ -3057,11 +3043,11 @@ void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ Nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorARM64::GenerateNop() { + __ Nop(); } void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 360488eb4a..3527261835 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -66,7 +66,8 @@ Location ARM64ReturnLocation(Primitive::Type return_type); class SlowPathCodeARM64 : public SlowPathCode { public: - SlowPathCodeARM64() : entry_label_(), exit_label_() {} + explicit SlowPathCodeARM64(HInstruction* instruction) + : SlowPathCode(instruction), entry_label_(), exit_label_() {} vixl::Label* GetEntryLabel() { return &entry_label_; } vixl::Label* GetExitLabel() { return &exit_label_; } @@ -532,6 +533,8 @@ class CodeGeneratorARM64 : public CodeGenerator { // artReadBarrierForRootSlow. void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root); + void GenerateNop(); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index c500ea4408..8d3d94b79d 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -39,9 +39,6 @@ namespace mips { static constexpr int kCurrentMethodStackOffset = 0; static constexpr Register kMethodRegisterArgument = A0; -// We need extra temporary/scratch registers (in addition to AT) in some cases. -static constexpr FRegister FTMP = F8; - Location MipsReturnLocation(Primitive::Type return_type) { switch (return_type) { case Primitive::kPrimBoolean: @@ -149,7 +146,7 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type type) class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -181,14 +178,12 @@ class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS); }; class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen); @@ -210,7 +205,6 @@ class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS); }; @@ -220,7 +214,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeMIPS(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -279,7 +273,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit LoadStringSlowPathMIPS(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathMIPS(HLoadString* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -290,7 +284,8 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); mips_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -309,14 +304,12 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS); }; class NullCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit NullCheckSlowPathMIPS(HNullCheck* instr) : instruction_(instr) {} + explicit NullCheckSlowPathMIPS(HNullCheck* instr) : SlowPathCodeMIPS(instr) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen); @@ -338,15 +331,13 @@ class NullCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS"; } private: - HNullCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS); }; class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS { public: SuspendCheckSlowPathMIPS(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCodeMIPS(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen); @@ -374,7 +365,6 @@ class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -386,7 +376,7 @@ class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS { class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit TypeCheckSlowPathMIPS(HInstruction* instruction) : instruction_(instruction) {} + explicit TypeCheckSlowPathMIPS(HInstruction* instruction) : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -437,15 +427,13 @@ class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS); }; class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS { public: explicit DeoptimizationSlowPathMIPS(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen); @@ -462,7 +450,6 @@ class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS); }; @@ -3407,11 +3394,11 @@ void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ Nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorMIPS::GenerateNop() { + __ Nop(); } void LocationsBuilderMIPS::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index dd0641c7ca..605c794421 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -152,7 +152,8 @@ class ParallelMoveResolverMIPS : public ParallelMoveResolverWithSwap { class SlowPathCodeMIPS : public SlowPathCode { public: - SlowPathCodeMIPS() : entry_label_(), exit_label_() {} + explicit SlowPathCodeMIPS(HInstruction* instruction) + : SlowPathCode(instruction), entry_label_(), exit_label_() {} MipsLabel* GetEntryLabel() { return &entry_label_; } MipsLabel* GetExitLabel() { return &exit_label_; } @@ -360,6 +361,8 @@ class CodeGeneratorMIPS : public CodeGenerator { UNIMPLEMENTED(FATAL) << "Not implemented on MIPS"; } + void GenerateNop(); + private: // Labels for each block that will be compiled. MipsLabel* block_labels_; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index e3a44f1c96..c2b84b4335 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -37,9 +37,6 @@ namespace mips64 { static constexpr int kCurrentMethodStackOffset = 0; static constexpr GpuRegister kMethodRegisterArgument = A0; -// We need extra temporary/scratch registers (in addition to AT) in some cases. -static constexpr FpuRegister FTMP = F8; - Location Mips64ReturnLocation(Primitive::Type return_type) { switch (return_type) { case Primitive::kPrimBoolean: @@ -110,7 +107,7 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type type) class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -141,14 +138,12 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS64"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS64); }; class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); @@ -169,7 +164,6 @@ class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS64"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS64); }; @@ -179,7 +173,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCodeMIPS64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -234,7 +228,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -245,7 +239,8 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ LoadConst32(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex()); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ LoadConst32(calling_convention.GetRegisterAt(0), string_index); mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -263,14 +258,12 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64); }; class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : instruction_(instr) {} + explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : SlowPathCodeMIPS64(instr) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); @@ -291,15 +284,13 @@ class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS64"; } private: - HNullCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS64); }; class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: SuspendCheckSlowPathMIPS64(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCodeMIPS64(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); @@ -326,7 +317,6 @@ class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS64"; } private: - HSuspendCheck* const instruction_; // If not null, the block to branch to after the suspend check. HBasicBlock* const successor_; @@ -338,7 +328,7 @@ class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit TypeCheckSlowPathMIPS64(HInstruction* instruction) : instruction_(instruction) {} + explicit TypeCheckSlowPathMIPS64(HInstruction* instruction) : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -384,15 +374,13 @@ class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS64"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS64); }; class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: explicit DeoptimizationSlowPathMIPS64(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen); @@ -408,7 +396,6 @@ class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS64"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS64); }; @@ -2732,11 +2719,11 @@ void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ Nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorMIPS64::GenerateNop() { + __ Nop(); } void LocationsBuilderMIPS64::HandleFieldGet(HInstruction* instruction, diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index eb7315aa7a..ba9eaff46f 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -152,7 +152,8 @@ class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap { class SlowPathCodeMIPS64 : public SlowPathCode { public: - SlowPathCodeMIPS64() : entry_label_(), exit_label_() {} + explicit SlowPathCodeMIPS64(HInstruction* instruction) + : SlowPathCode(instruction), entry_label_(), exit_label_() {} Mips64Label* GetEntryLabel() { return &entry_label_; } Mips64Label* GetExitLabel() { return &exit_label_; } @@ -352,6 +353,8 @@ class CodeGeneratorMIPS64 : public CodeGenerator { UNIMPLEMENTED(FATAL) << "Not implemented on MIPS64"; } + void GenerateNop(); + private: // Labels for each block that will be compiled. Mips64Label* block_labels_; // Indexed by block id. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index f032f51649..88e42f3faf 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -52,7 +52,7 @@ static constexpr int kFakeReturnRegister = Register(8); class NullCheckSlowPathX86 : public SlowPathCode { public: - explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {} + explicit NullCheckSlowPathX86(HNullCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); @@ -73,13 +73,12 @@ class NullCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86"; } private: - HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86); }; class DivZeroCheckSlowPathX86 : public SlowPathCode { public: - explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); @@ -100,13 +99,13 @@ class DivZeroCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86); }; class DivRemMinusOneSlowPathX86 : public SlowPathCode { public: - DivRemMinusOneSlowPathX86(Register reg, bool is_div) : reg_(reg), is_div_(is_div) {} + DivRemMinusOneSlowPathX86(HInstruction* instruction, Register reg, bool is_div) + : SlowPathCode(instruction), reg_(reg), is_div_(is_div) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); @@ -128,7 +127,7 @@ class DivRemMinusOneSlowPathX86 : public SlowPathCode { class BoundsCheckSlowPathX86 : public SlowPathCode { public: - explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction) : instruction_(instruction) {} + explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -160,15 +159,13 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86); }; class SuspendCheckSlowPathX86 : public SlowPathCode { public: SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); @@ -199,7 +196,6 @@ class SuspendCheckSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86"; } private: - HSuspendCheck* const instruction_; HBasicBlock* const successor_; Label return_label_; @@ -208,7 +204,7 @@ class SuspendCheckSlowPathX86 : public SlowPathCode { class LoadStringSlowPathX86 : public SlowPathCode { public: - explicit LoadStringSlowPathX86(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathX86(HLoadString* instruction): SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -219,7 +215,8 @@ class LoadStringSlowPathX86 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction_->GetStringIndex())); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ movl(calling_convention.GetRegisterAt(0), Immediate(string_index)); x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -234,8 +231,6 @@ class LoadStringSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86); }; @@ -245,7 +240,7 @@ class LoadClassSlowPathX86 : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -299,7 +294,7 @@ class LoadClassSlowPathX86 : public SlowPathCode { class TypeCheckSlowPathX86 : public SlowPathCode { public: TypeCheckSlowPathX86(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCode(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -356,7 +351,6 @@ class TypeCheckSlowPathX86 : public SlowPathCode { bool IsFatal() const OVERRIDE { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86); @@ -365,7 +359,7 @@ class TypeCheckSlowPathX86 : public SlowPathCode { class DeoptimizationSlowPathX86 : public SlowPathCode { public: explicit DeoptimizationSlowPathX86(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); @@ -381,13 +375,12 @@ class DeoptimizationSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86); }; class ArraySetSlowPathX86 : public SlowPathCode { public: - explicit ArraySetSlowPathX86(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathX86(HInstruction* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -426,8 +419,6 @@ class ArraySetSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86); }; @@ -435,7 +426,7 @@ class ArraySetSlowPathX86 : public SlowPathCode { class ReadBarrierMarkSlowPathX86 : public SlowPathCode { public: ReadBarrierMarkSlowPathX86(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCode(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -474,7 +465,6 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -490,7 +480,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCode(instruction), out_(out), ref_(ref), obj_(obj), @@ -645,7 +635,6 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -662,7 +651,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { class ReadBarrierForRootSlowPathX86 : public SlowPathCode { public: ReadBarrierForRootSlowPathX86(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCode(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -695,7 +684,6 @@ class ReadBarrierForRootSlowPathX86 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathX86"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; @@ -1649,11 +1637,11 @@ void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorX86::GenerateNop() { + __ nop(); } void LocationsBuilderX86::VisitLocal(HLocal* local) { @@ -3453,9 +3441,8 @@ void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instr GenerateDivRemWithAnyConstant(instruction); } } else { - SlowPathCode* slow_path = - new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86(out.AsRegister<Register>(), - is_div); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86( + instruction, out.AsRegister<Register>(), is_div); codegen_->AddSlowPath(slow_path); Register second_reg = second.AsRegister<Register>(); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 63e9b2fc9c..0795f3b530 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -540,6 +540,7 @@ class CodeGeneratorX86 : public CodeGenerator { } } + void GenerateNop(); private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index a53a6be3de..bb24c6f59c 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -56,7 +56,7 @@ static constexpr int kC2ConditionMask = 0x400; class NullCheckSlowPathX86_64 : public SlowPathCode { public: - explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {} + explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); @@ -77,13 +77,12 @@ class NullCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86_64"; } private: - HNullCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64); }; class DivZeroCheckSlowPathX86_64 : public SlowPathCode { public: - explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : instruction_(instruction) {} + explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); @@ -104,14 +103,13 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86_64"; } private: - HDivZeroCheck* const instruction_; DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64); }; class DivRemMinusOneSlowPathX86_64 : public SlowPathCode { public: - DivRemMinusOneSlowPathX86_64(Register reg, Primitive::Type type, bool is_div) - : cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {} + DivRemMinusOneSlowPathX86_64(HInstruction* at, Register reg, Primitive::Type type, bool is_div) + : SlowPathCode(at), cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { __ Bind(GetEntryLabel()); @@ -145,7 +143,7 @@ class DivRemMinusOneSlowPathX86_64 : public SlowPathCode { class SuspendCheckSlowPathX86_64 : public SlowPathCode { public: SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor) - : instruction_(instruction), successor_(successor) {} + : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); @@ -176,7 +174,6 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86_64"; } private: - HSuspendCheck* const instruction_; HBasicBlock* const successor_; Label return_label_; @@ -186,7 +183,7 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { class BoundsCheckSlowPathX86_64 : public SlowPathCode { public: explicit BoundsCheckSlowPathX86_64(HBoundsCheck* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -218,8 +215,6 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86_64"; } private: - HBoundsCheck* const instruction_; - DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64); }; @@ -229,7 +224,7 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { HInstruction* at, uint32_t dex_pc, bool do_clinit) - : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { + : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -286,7 +281,7 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { class LoadStringSlowPathX86_64 : public SlowPathCode { public: - explicit LoadStringSlowPathX86_64(HLoadString* instruction) : instruction_(instruction) {} + explicit LoadStringSlowPathX86_64(HLoadString* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -297,8 +292,8 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; - __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), - Immediate(instruction_->GetStringIndex())); + const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex(); + __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(string_index)); x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), @@ -312,15 +307,13 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86_64"; } private: - HLoadString* const instruction_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86_64); }; class TypeCheckSlowPathX86_64 : public SlowPathCode { public: TypeCheckSlowPathX86_64(HInstruction* instruction, bool is_fatal) - : instruction_(instruction), is_fatal_(is_fatal) {} + : SlowPathCode(instruction), is_fatal_(is_fatal) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -379,7 +372,6 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { bool IsFatal() const OVERRIDE { return is_fatal_; } private: - HInstruction* const instruction_; const bool is_fatal_; DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86_64); @@ -388,7 +380,7 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { class DeoptimizationSlowPathX86_64 : public SlowPathCode { public: explicit DeoptimizationSlowPathX86_64(HDeoptimize* instruction) - : instruction_(instruction) {} + : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); @@ -404,13 +396,12 @@ class DeoptimizationSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86_64"; } private: - HDeoptimize* const instruction_; DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86_64); }; class ArraySetSlowPathX86_64 : public SlowPathCode { public: - explicit ArraySetSlowPathX86_64(HInstruction* instruction) : instruction_(instruction) {} + explicit ArraySetSlowPathX86_64(HInstruction* instruction) : SlowPathCode(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); @@ -449,8 +440,6 @@ class ArraySetSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86_64"; } private: - HInstruction* const instruction_; - DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86_64); }; @@ -458,7 +447,7 @@ class ArraySetSlowPathX86_64 : public SlowPathCode { class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode { public: ReadBarrierMarkSlowPathX86_64(HInstruction* instruction, Location out, Location obj) - : instruction_(instruction), out_(out), obj_(obj) { + : SlowPathCode(instruction), out_(out), obj_(obj) { DCHECK(kEmitCompilerReadBarrier); } @@ -497,7 +486,6 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode { } private: - HInstruction* const instruction_; const Location out_; const Location obj_; @@ -513,7 +501,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { Location obj, uint32_t offset, Location index) - : instruction_(instruction), + : SlowPathCode(instruction), out_(out), ref_(ref), obj_(obj), @@ -667,7 +655,6 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { UNREACHABLE(); } - HInstruction* const instruction_; const Location out_; const Location ref_; const Location obj_; @@ -684,7 +671,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { class ReadBarrierForRootSlowPathX86_64 : public SlowPathCode { public: ReadBarrierForRootSlowPathX86_64(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) { + : SlowPathCode(instruction), out_(out), root_(root) { DCHECK(kEmitCompilerReadBarrier); } @@ -716,7 +703,6 @@ class ReadBarrierForRootSlowPathX86_64 : public SlowPathCode { const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathX86_64"; } private: - HInstruction* const instruction_; const Location out_; const Location root_; @@ -1632,11 +1618,11 @@ void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) { } void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - if (codegen_->HasStackMapAtCurrentPc()) { - // Ensure that we do not collide with the stack map of the previous instruction. - __ nop(); - } - codegen_->RecordPcInfo(info, info->GetDexPc()); + codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +} + +void CodeGeneratorX86_64::GenerateNop() { + __ nop(); } void LocationsBuilderX86_64::VisitLocal(HLocal* local) { @@ -3546,7 +3532,7 @@ void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* in } else { SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86_64( - out.AsRegister(), type, is_div); + instruction, out.AsRegister(), type, is_div); codegen_->AddSlowPath(slow_path); CpuRegister second_reg = second.AsRegister<CpuRegister>(); @@ -6511,8 +6497,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/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 97f6f84236..b3d27e194a 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -513,6 +513,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { } } + void GenerateNop(); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h index e0bc6f73dc..e10b1d6b2e 100644 --- a/compiler/optimizing/constant_folding.h +++ b/compiler/optimizing/constant_folding.h @@ -39,7 +39,7 @@ namespace art { */ 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/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 37f2d79536..a1e1cde9df 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -379,7 +379,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(Inducti Primitive::Type type) { // Transfer over a shift left: treat shift by restricted constant as equivalent multiplication. int64_t value = -1; - if (a != nullptr && IsIntAndGet(b, &value)) { + if (a != nullptr && IsExact(b, &value)) { // Obtain the constant needed for the multiplication. This yields an existing instruction // if the constants is already there. Otherwise, this has a side effect on the HIR. // The restriction on the shift factor avoids generating a negative constant @@ -546,9 +546,10 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, // Analyze condition with induction at left-hand-side (e.g. i < U). InductionInfo* lower_expr = a->op_b; InductionInfo* upper_expr = b; - InductionInfo* stride = a->op_a; + InductionInfo* stride_expr = a->op_a; + // Constant stride? int64_t stride_value = 0; - if (!IsIntAndGet(stride, &stride_value)) { + if (!IsExact(stride_expr, &stride_value)) { return; } // Rewrite condition i != U into i < U or i > U if end condition is reached exactly. @@ -561,7 +562,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, // stride < 0, either i > U or i >= U if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) || (stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) { - VisitTripCount(loop, lower_expr, upper_expr, stride, stride_value, type, cmp); + VisitTripCount(loop, lower_expr, upper_expr, stride_expr, stride_value, type, cmp); } } } @@ -569,7 +570,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, InductionInfo* lower_expr, InductionInfo* upper_expr, - InductionInfo* stride, + InductionInfo* stride_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { @@ -612,9 +613,10 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, trip_count = CreateInvariantOp(kAdd, trip_count, CreateConstant(1, type)); } // Compensate for stride. - trip_count = CreateInvariantOp(kAdd, trip_count, stride); + trip_count = CreateInvariantOp(kAdd, trip_count, stride_expr); } - trip_count = CreateInvariantOp(kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride); + trip_count = CreateInvariantOp( + kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride_expr); // Assign the trip-count expression to the loop control. Clients that use the information // should be aware that the expression is only valid under the conditions listed above. InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests @@ -644,14 +646,25 @@ bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr, IfCondition cmp) { int64_t lower_value; int64_t upper_value; - if (IsIntAndGet(lower_expr, &lower_value) && IsIntAndGet(upper_expr, &upper_value)) { - switch (cmp) { - case kCondLT: return lower_value < upper_value; - case kCondLE: return lower_value <= upper_value; - case kCondGT: return lower_value > upper_value; - case kCondGE: return lower_value >= upper_value; - default: LOG(FATAL) << "CONDITION UNREACHABLE"; - } + switch (cmp) { + case kCondLT: + return IsAtMost(lower_expr, &lower_value) + && IsAtLeast(upper_expr, &upper_value) + && lower_value < upper_value; + case kCondLE: + return IsAtMost(lower_expr, &lower_value) + && IsAtLeast(upper_expr, &upper_value) + && lower_value <= upper_value; + case kCondGT: + return IsAtLeast(lower_expr, &lower_value) + && IsAtMost(upper_expr, &upper_value) + && lower_value > upper_value; + case kCondGE: + return IsAtLeast(lower_expr, &lower_value) + && IsAtMost(upper_expr, &upper_value) + && lower_value >= upper_value; + default: + LOG(FATAL) << "CONDITION UNREACHABLE"; } return false; // not certain, may be untaken } @@ -660,25 +673,23 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { - const int64_t min = type == Primitive::kPrimInt - ? std::numeric_limits<int32_t>::min() - : std::numeric_limits<int64_t>::min(); - const int64_t max = type == Primitive::kPrimInt - ? std::numeric_limits<int32_t>::max() - : std::numeric_limits<int64_t>::max(); + const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::min() + : std::numeric_limits<int64_t>::min(); + const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::max() + : std::numeric_limits<int64_t>::max(); // Some rules under which it is certain at compile-time that the loop is finite. int64_t value; switch (cmp) { case kCondLT: return stride_value == 1 || - (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value + 1)); + (IsAtMost(upper_expr, &value) && value <= (max - stride_value + 1)); case kCondLE: - return (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value)); + return (IsAtMost(upper_expr, &value) && value <= (max - stride_value)); case kCondGT: return stride_value == -1 || - (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value - 1)); + (IsAtLeast(upper_expr, &value) && value >= (min - stride_value - 1)); case kCondGE: - return (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value)); + return (IsAtLeast(upper_expr, &value) && value >= (min - stride_value)); default: LOG(FATAL) << "CONDITION UNREACHABLE"; } @@ -733,7 +744,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv // More exhaustive simplifications are done by later phases once induction nodes are // translated back into HIR code (e.g. by loop optimizations or BCE). int64_t value = -1; - if (IsIntAndGet(a, &value)) { + if (IsExact(a, &value)) { if (value == 0) { // Simplify 0 + b = b, 0 * b = 0. if (op == kAdd) { @@ -750,7 +761,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv } } } - if (IsIntAndGet(b, &value)) { + if (IsExact(b, &value)) { if (value == 0) { // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0. if (op == kAdd || op == kSub) { @@ -784,29 +795,16 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr); } -bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) { - if (info != nullptr && info->induction_class == kInvariant) { - // A direct constant fetch. - if (info->operation == kFetch) { - DCHECK(info->fetch); - if (info->fetch->IsIntConstant()) { - *value = info->fetch->AsIntConstant()->GetValue(); - return true; - } else if (info->fetch->IsLongConstant()) { - *value = info->fetch->AsLongConstant()->GetValue(); - return true; - } - } - // Use range analysis to resolve compound values. - InductionVarRange range(this); - int32_t min_val = 0; - int32_t max_val = 0; - if (range.IsConstantRange(info, &min_val, &max_val) && min_val == max_val) { - *value = min_val; - return true; - } - } - return false; +bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kExact, value); +} + +bool HInductionVarAnalysis::IsAtMost(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtMost, value); +} + +bool HInductionVarAnalysis::IsAtLeast(InductionInfo* info, int64_t* value) { + return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value); } bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1, diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 84d5d82568..94d2646aec 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -189,7 +189,9 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b); // Constants. - bool IsIntAndGet(InductionInfo* info, int64_t* value); + bool IsExact(InductionInfo* info, /*out*/ int64_t* value); + bool IsAtMost(InductionInfo* info, /*out*/ int64_t* value); + bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value); // Helpers. static bool InductionEqual(InductionInfo* info1, InductionInfo* info2); diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index 9566c29adf..b162696a42 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -45,17 +45,14 @@ static bool IsSafeDiv(int32_t c1, int32_t c2) { return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2)); } -/** Returns true for 32/64-bit integral constant. */ -static bool IsIntAndGet(HInstruction* instruction, int32_t* value) { +/** Returns true for 32/64-bit constant instruction. */ +static bool IsIntAndGet(HInstruction* instruction, int64_t* value) { if (instruction->IsIntConstant()) { *value = instruction->AsIntConstant()->GetValue(); return true; } else if (instruction->IsLongConstant()) { - const int64_t c = instruction->AsLongConstant()->GetValue(); - if (CanLongValueFitIntoInt(c)) { - *value = static_cast<int32_t>(c); - return true; - } + *value = instruction->AsLongConstant()->GetValue(); + return true; } return false; } @@ -65,8 +62,9 @@ static bool IsIntAndGet(HInstruction* instruction, int32_t* value) { * because length >= 0 is true. This makes it more likely the bound is useful to clients. */ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { - int32_t value; - if (v.a_constant > 1 && + int64_t value; + if (v.is_known && + v.a_constant > 1 && v.instruction->IsDiv() && v.instruction->InputAt(0)->IsArrayLength() && IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) { @@ -75,6 +73,16 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { return v; } +/** Helper method to test for a constant value. */ +static bool IsConstantValue(InductionVarRange::Value v) { + return v.is_known && v.a_constant == 0; +} + +/** Helper method to test for same constant value. */ +static bool IsSameConstantValue(InductionVarRange::Value v1, InductionVarRange::Value v2) { + return IsConstantValue(v1) && IsConstantValue(v2) && v1.b_constant == v2.b_constant; +} + /** Helper method to insert an instruction. */ static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) { DCHECK(block != nullptr); @@ -99,29 +107,45 @@ bool InductionVarRange::GetInductionRange(HInstruction* context, /*out*/Value* max_val, /*out*/bool* needs_finite_test) { HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - HBasicBlock* header = loop->GetHeader(); - bool in_body = context->GetBlock() != header; - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, instruction); - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); - // Find range. - *min_val = GetVal(info, trip, in_body, /* is_min */ true); - *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false)); - *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); - return true; + if (loop == nullptr) { + return false; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction); + if (info == nullptr) { + return false; // no induction information } - return false; // Nothing known + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = context->GetBlock() != header; + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + // Find range. + *min_val = GetVal(info, trip, in_body, /* is_min */ true); + *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false)); + *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); + return true; } -bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const { - Value v1 = RefineOuter(*min_val, /* is_min */ true); - Value v2 = RefineOuter(*max_val, /* is_min */ false); - if (v1.instruction != min_val->instruction || v2.instruction != max_val->instruction) { - *min_val = v1; - *max_val = v2; +bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val, + /*in-out*/ Value* max_val) const { + Value v1_min = RefineOuter(*min_val, /* is_min */ true); + Value v2_max = RefineOuter(*max_val, /* is_min */ false); + // The refined range is safe if both sides refine the same instruction. Otherwise, since two + // different ranges are combined, the new refined range is safe to pass back to the client if + // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur. + if (min_val->instruction != max_val->instruction) { + Value v1_max = RefineOuter(*min_val, /* is_min */ false); + Value v2_min = RefineOuter(*max_val, /* is_min */ true); + if (!IsConstantValue(v1_max) || + !IsConstantValue(v2_min) || + v1_max.b_constant > v2_min.b_constant) { + return false; + } + } + // Did something change? + if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) { + *min_val = v1_min; + *max_val = v2_max; return true; } return false; @@ -164,6 +188,38 @@ void InductionVarRange::GenerateTakenTest(HInstruction* context, // Private class methods. // +bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info, + ConstantRequest request, + /*out*/ int64_t *value) const { + if (info != nullptr) { + // A direct 32-bit or 64-bit constant fetch. This immediately satisfies + // any of the three requests (kExact, kAtMost, and KAtLeast). + if (info->induction_class == HInductionVarAnalysis::kInvariant && + info->operation == HInductionVarAnalysis::kFetch) { + if (IsIntAndGet(info->fetch, value)) { + return true; + } + } + // Try range analysis while traversing outward on loops. + bool in_body = true; // no known trip count + Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true); + Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false); + do { + // Make sure *both* extremes are known to avoid arithmetic wrap-around anomalies. + if (IsConstantValue(v_min) && IsConstantValue(v_max) && v_min.b_constant <= v_max.b_constant) { + if ((request == kExact && v_min.b_constant == v_max.b_constant) || request == kAtMost) { + *value = v_max.b_constant; + return true; + } else if (request == kAtLeast) { + *value = v_min.b_constant; + return true; + } + } + } while (RefineOuter(&v_min, &v_max)); + } + return false; +} + bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const { if (info != nullptr) { if (info->induction_class == HInductionVarAnalysis::kLinear) { @@ -206,12 +262,10 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind if (trip != nullptr) { HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a; if (trip_expr->operation == HInductionVarAnalysis::kSub) { - int32_t min_value = 0; - int32_t stride_value = 0; - if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value)) { if (!is_min && stride_value == 1) { - // Test original trip's negative operand (trip_expr->op_b) against - // the offset of the linear induction. + // Test original trip's negative operand (trip_expr->op_b) against offset of induction. if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) { // Analyze cancelled trip with just the positive operand (trip_expr->op_a). HInductionVarAnalysis::InductionInfo cancelled_trip( @@ -219,8 +273,7 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind return GetVal(&cancelled_trip, trip, in_body, is_min); } } else if (is_min && stride_value == -1) { - // Test original trip's positive operand (trip_expr->op_a) against - // the offset of the linear induction. + // Test original trip's positive operand (trip_expr->op_a) against offset of induction. if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) { // Analyze cancelled trip with just the negative operand (trip_expr->op_b). HInductionVarAnalysis::InductionInfo neg( @@ -248,14 +301,16 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, bool is_min) const { // Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes // more likely range analysis will compare the same instructions as terminal nodes. - int32_t value; - if (IsIntAndGet(instruction, &value)) { - return Value(value); + int64_t value; + if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) { + return Value(static_cast<int32_t>(value)); } else if (instruction->IsAdd()) { - if (IsIntAndGet(instruction->InputAt(0), &value)) { - return AddValue(Value(value), GetFetch(instruction->InputAt(1), trip, in_body, is_min)); - } else if (IsIntAndGet(instruction->InputAt(1), &value)) { - return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), Value(value)); + if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) { + return AddValue(Value(static_cast<int32_t>(value)), + GetFetch(instruction->InputAt(1), trip, in_body, is_min)); + } else if (IsIntAndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) { + return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), + Value(static_cast<int32_t>(value))); } } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) { return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min); @@ -331,29 +386,30 @@ InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::Induct Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false); Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true); Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false); - // Try to refine certain failure. - if (v1_min.a_constant && v1_max.a_constant) { - v1_min = RefineOuter(v1_min, /* is_min */ true); - v1_max = RefineOuter(v1_max, /* is_min */ false); - } - // Positive or negative range? - if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) { - // Positive range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? MulValue(v1_min, v2_min) - : MulValue(v1_max, v2_max); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? MulValue(v1_max, v2_min) - : MulValue(v1_min, v2_max); + // Try to refine first operand. + if (!IsConstantValue(v1_min) && !IsConstantValue(v1_max)) { + RefineOuter(&v1_min, &v1_max); + } + // Constant times range. + if (IsSameConstantValue(v1_min, v1_max)) { + return MulRangeAndConstant(v2_min, v2_max, v1_min, is_min); + } else if (IsSameConstantValue(v2_min, v2_max)) { + return MulRangeAndConstant(v1_min, v1_max, v2_min, is_min); + } + // Positive range vs. positive or negative range. + if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max); } - } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) { - // Negative range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? MulValue(v1_min, v2_max) - : MulValue(v1_max, v2_min); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? MulValue(v1_max, v2_max) - : MulValue(v1_min, v2_min); + } + // Negative range vs. positive or negative range. + if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min); } } return Value(); @@ -368,43 +424,41 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false); Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true); Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false); - // Positive or negative range? - if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) { - // Positive range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? DivValue(v1_min, v2_max) - : DivValue(v1_max, v2_min); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? DivValue(v1_max, v2_max) - : DivValue(v1_min, v2_min); + // Range divided by constant. + if (IsSameConstantValue(v2_min, v2_max)) { + return DivRangeAndConstant(v1_min, v1_max, v2_min, is_min); + } + // Positive range vs. positive or negative range. + if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min); } - } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) { - // Negative range vs. positive or negative range. - if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) { - return is_min ? DivValue(v1_min, v2_min) - : DivValue(v1_max, v2_max); - } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) { - return is_min ? DivValue(v1_max, v2_min) - : DivValue(v1_min, v2_max); + } + // Negative range vs. positive or negative range. + if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) { + if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) { + return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max); + } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) { + return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max); } } return Value(); } -bool InductionVarRange::IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t *min_value, - int32_t *max_value) const { - bool in_body = true; // no known trip count - Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true); - Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false); - do { - if (v_min.is_known && v_min.a_constant == 0 && v_max.is_known && v_max.a_constant == 0) { - *min_value = v_min.b_constant; - *max_value = v_max.b_constant; - return true; - } - } while (RefineOuter(&v_min, &v_max)); - return false; +InductionVarRange::Value InductionVarRange::MulRangeAndConstant(Value v_min, + Value v_max, + Value c, + bool is_min) const { + return is_min == (c.b_constant >= 0) ? MulValue(v_min, c) : MulValue(v_max, c); +} + +InductionVarRange::Value InductionVarRange::DivRangeAndConstant(Value v_min, + Value v_max, + Value c, + bool is_min) const { + return is_min == (c.b_constant >= 0) ? DivValue(v_min, c) : DivValue(v_max, c); } InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const { @@ -471,22 +525,25 @@ InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is } InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const { - if (v.instruction != nullptr) { - HLoopInformation* loop = - v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - bool in_body = true; // use is always in body of outer loop - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, v.instruction); - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction()); - // Try to refine "a x instruction + b" with outer loop range information on instruction. - return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), - Value(v.b_constant)); - } + if (v.instruction == nullptr) { + return v; // nothing to refine } - return v; + HLoopInformation* loop = + v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop + if (loop == nullptr) { + return v; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, v.instruction); + if (info == nullptr) { + return v; // no induction information + } + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = true; // inner always in more outer + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + // Try to refine "a x instruction + b" with outer loop range information on instruction. + return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), Value(v.b_constant)); } bool InductionVarRange::GenerateCode(HInstruction* context, @@ -499,44 +556,45 @@ bool InductionVarRange::GenerateCode(HInstruction* context, /*out*/bool* needs_finite_test, /*out*/bool* needs_taken_test) const { HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop - if (loop != nullptr) { - // Set up loop information. - HBasicBlock* header = loop->GetHeader(); - bool in_body = context->GetBlock() != header; - HInductionVarAnalysis::InductionInfo* info = - induction_analysis_->LookupInfo(loop, instruction); - if (info == nullptr) { - return false; // nothing to analyze - } - HInductionVarAnalysis::InductionInfo* trip = - induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); - // Determine what tests are needed. A finite test is needed if the evaluation code uses the - // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot" - // the computed range). A taken test is needed for any unknown trip-count, even if evaluation - // code does not use the trip-count explicitly (since there could be an implicit relation - // between e.g. an invariant subscript and a not-taken condition). - *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); - *needs_taken_test = IsBodyTripCount(trip); - // Code generation for taken test: generate the code when requested or otherwise analyze - // if code generation is feasible when taken test is needed. - if (taken_test != nullptr) { - return GenerateCode( - trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false); - } else if (*needs_taken_test) { - if (!GenerateCode( - trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) { - return false; - } + if (loop == nullptr) { + return false; // no loop + } + HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction); + if (info == nullptr) { + return false; // no induction information + } + // Set up loop information. + HBasicBlock* header = loop->GetHeader(); + bool in_body = context->GetBlock() != header; + HInductionVarAnalysis::InductionInfo* trip = + induction_analysis_->LookupInfo(loop, header->GetLastInstruction()); + if (trip == nullptr) { + return false; // codegen relies on trip count + } + // Determine what tests are needed. A finite test is needed if the evaluation code uses the + // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot" + // the computed range). A taken test is needed for any unknown trip-count, even if evaluation + // code does not use the trip-count explicitly (since there could be an implicit relation + // between e.g. an invariant subscript and a not-taken condition). + *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip); + *needs_taken_test = IsBodyTripCount(trip); + // Code generation for taken test: generate the code when requested or otherwise analyze + // if code generation is feasible when taken test is needed. + if (taken_test != nullptr) { + return GenerateCode(trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false); + } else if (*needs_taken_test) { + if (!GenerateCode( + trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) { + return false; } - // Code generation for lower and upper. - return - // Success on lower if invariant (not set), or code can be generated. - ((info->induction_class == HInductionVarAnalysis::kInvariant) || - GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) && - // And success on upper. - GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); } - return false; + // Code generation for lower and upper. + return + // Success on lower if invariant (not set), or code can be generated. + ((info->induction_class == HInductionVarAnalysis::kInvariant) || + GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) && + // And success on upper. + GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false); } bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, @@ -639,9 +697,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, case HInductionVarAnalysis::kLinear: { // Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only // to avoid arithmetic wrap-around situations that are hard to guard against. - int32_t min_value = 0; - int32_t stride_value = 0; - if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) { + int64_t stride_value = 0; + if (IsConstant(info->op_a, kExact, &stride_value)) { if (stride_value == 1 || stride_value == -1) { const bool is_min_a = stride_value == 1 ? is_min : !is_min; if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) && @@ -666,7 +723,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, // Wrap-around and periodic inductions are restricted to constants only, so that extreme // values are easy to test at runtime without complications of arithmetic wrap-around. Value extreme = GetVal(info, trip, in_body, is_min); - if (extreme.is_known && extreme.a_constant == 0) { + if (IsConstantValue(extreme)) { if (graph != nullptr) { *result = graph->GetIntConstant(extreme.b_constant); } diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h index 3cb7b4bfd5..0af41560ff 100644 --- a/compiler/optimizing/induction_var_range.h +++ b/compiler/optimizing/induction_var_range.h @@ -69,7 +69,8 @@ class InductionVarRange { /*out*/ bool* needs_finite_test); /** Refines the values with induction of next outer loop. Returns true on change. */ - bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const; + bool RefineOuter(/*in-out*/ Value* min_val, + /*in-out*/ Value* max_val) const; /** * Returns true if range analysis is able to generate code for the lower and upper @@ -116,6 +117,23 @@ class InductionVarRange { /*out*/ HInstruction** taken_test); private: + /* + * Enum used in IsConstant() request. + */ + enum ConstantRequest { + kExact, + kAtMost, + kAtLeast + }; + + /** + * Returns true if exact or upper/lower bound on the given induction + * information is known as a 64-bit constant, which is returned in value. + */ + bool IsConstant(HInductionVarAnalysis::InductionInfo* info, + ConstantRequest request, + /*out*/ int64_t *value) const; + bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const; bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const; bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const; @@ -143,9 +161,8 @@ class InductionVarRange { bool in_body, bool is_min) const; - bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t *min_value, - int32_t *max_value) const; + Value MulRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const; + Value DivRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const; Value AddValue(Value v1, Value v2) const; Value SubValue(Value v1, Value v2) const; diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index 55a654e301..c5c33bd9bc 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -215,10 +215,16 @@ class InductionVarRangeTest : public CommonCompilerTest { return range_.GetDiv(info1, info2, nullptr, /* in_body */ true, is_min); } - bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info, - int32_t* min_value, - int32_t* max_value) { - return range_.IsConstantRange(info, min_value, max_value); + bool IsExact(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kExact, value); + } + + bool IsAtMost(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kAtMost, value); + } + + bool IsAtLeast(HInductionVarAnalysis::InductionInfo* info, int64_t* value) { + return range_.IsConstant(info, InductionVarRange::kAtLeast, value); } Value AddValue(Value v1, Value v2) { return range_.AddValue(v1, v2); } @@ -249,6 +255,34 @@ class InductionVarRangeTest : public CommonCompilerTest { // Tests on private methods. // +TEST_F(InductionVarRangeTest, IsConstant) { + int64_t value; + // Constant. + EXPECT_TRUE(IsExact(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + EXPECT_TRUE(IsAtMost(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + EXPECT_TRUE(IsAtLeast(CreateConst(12345), &value)); + EXPECT_EQ(12345, value); + // Constant trivial range. + EXPECT_TRUE(IsExact(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + EXPECT_TRUE(IsAtMost(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + EXPECT_TRUE(IsAtLeast(CreateRange(111, 111), &value)); + EXPECT_EQ(111, value); + // Constant non-trivial range. + EXPECT_FALSE(IsExact(CreateRange(11, 22), &value)); + EXPECT_TRUE(IsAtMost(CreateRange(11, 22), &value)); + EXPECT_EQ(22, value); + EXPECT_TRUE(IsAtLeast(CreateRange(11, 22), &value)); + EXPECT_EQ(11, value); + // Symbolic. + EXPECT_FALSE(IsExact(CreateFetch(x_), &value)); + EXPECT_FALSE(IsAtMost(CreateFetch(x_), &value)); + EXPECT_FALSE(IsAtLeast(CreateFetch(x_), &value)); +} + TEST_F(InductionVarRangeTest, TripCountProperties) { EXPECT_FALSE(NeedsTripCount(nullptr)); EXPECT_FALSE(NeedsTripCount(CreateConst(1))); @@ -367,6 +401,10 @@ TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) { } TEST_F(InductionVarRangeTest, GetMulMin) { + ExpectEqual(Value(-14), GetMul(CreateConst(2), CreateRange(-7, 8), true)); + ExpectEqual(Value(-16), GetMul(CreateConst(-2), CreateRange(-7, 8), true)); + ExpectEqual(Value(-14), GetMul(CreateRange(-7, 8), CreateConst(2), true)); + ExpectEqual(Value(-16), GetMul(CreateRange(-7, 8), CreateConst(-2), true)); ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), true)); ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), true)); ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), true)); @@ -379,6 +417,10 @@ TEST_F(InductionVarRangeTest, GetMulMin) { } TEST_F(InductionVarRangeTest, GetMulMax) { + ExpectEqual(Value(16), GetMul(CreateConst(2), CreateRange(-7, 8), false)); + ExpectEqual(Value(14), GetMul(CreateConst(-2), CreateRange(-7, 8), false)); + ExpectEqual(Value(16), GetMul(CreateRange(-7, 8), CreateConst(2), false)); + ExpectEqual(Value(14), GetMul(CreateRange(-7, 8), CreateConst(-2), false)); ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), false)); ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), false)); ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), false)); @@ -391,6 +433,8 @@ TEST_F(InductionVarRangeTest, GetMulMax) { } TEST_F(InductionVarRangeTest, GetDivMin) { + ExpectEqual(Value(-5), GetDiv(CreateRange(-10, 20), CreateConst(2), true)); + ExpectEqual(Value(-10), GetDiv(CreateRange(-10, 20), CreateConst(-2), true)); ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), true)); ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), true)); ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), true)); @@ -403,6 +447,8 @@ TEST_F(InductionVarRangeTest, GetDivMin) { } TEST_F(InductionVarRangeTest, GetDivMax) { + ExpectEqual(Value(10), GetDiv(CreateRange(-10, 20), CreateConst(2), false)); + ExpectEqual(Value(5), GetDiv(CreateRange(-10, 20), CreateConst(-2), false)); ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), false)); ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), false)); ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), false)); @@ -414,18 +460,6 @@ TEST_F(InductionVarRangeTest, GetDivMax) { ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), false)); } -TEST_F(InductionVarRangeTest, IsConstantRange) { - int32_t min_value; - int32_t max_value; - ASSERT_TRUE(IsConstantRange(CreateConst(12345), &min_value, &max_value)); - EXPECT_EQ(12345, min_value); - EXPECT_EQ(12345, max_value); - ASSERT_TRUE(IsConstantRange(CreateRange(1, 2), &min_value, &max_value)); - EXPECT_EQ(1, min_value); - EXPECT_EQ(2, max_value); - EXPECT_FALSE(IsConstantRange(CreateFetch(x_), &min_value, &max_value)); -} - TEST_F(InductionVarRangeTest, AddValue) { ExpectEqual(Value(110), AddValue(Value(10), Value(100))); ExpectEqual(Value(-5), AddValue(Value(x_, 1, -4), Value(x_, -1, -1))); @@ -459,6 +493,24 @@ TEST_F(InductionVarRangeTest, MulValue) { ExpectEqual(Value(), MulValue(Value(90000), Value(-90000))); // unsafe } +TEST_F(InductionVarRangeTest, MulValueSpecial) { + const int32_t min_value = std::numeric_limits<int32_t>::min(); + const int32_t max_value = std::numeric_limits<int32_t>::max(); + + // Unsafe. + ExpectEqual(Value(), MulValue(Value(min_value), Value(min_value))); + ExpectEqual(Value(), MulValue(Value(min_value), Value(-1))); + ExpectEqual(Value(), MulValue(Value(min_value), Value(max_value))); + ExpectEqual(Value(), MulValue(Value(max_value), Value(max_value))); + + // Safe. + ExpectEqual(Value(min_value), MulValue(Value(min_value), Value(1))); + ExpectEqual(Value(max_value), MulValue(Value(max_value), Value(1))); + ExpectEqual(Value(-max_value), MulValue(Value(max_value), Value(-1))); + ExpectEqual(Value(-1), MulValue(Value(1), Value(-1))); + ExpectEqual(Value(1), MulValue(Value(-1), Value(-1))); +} + TEST_F(InductionVarRangeTest, DivValue) { ExpectEqual(Value(25), DivValue(Value(100), Value(4))); ExpectEqual(Value(), DivValue(Value(x_, 1, -4), Value(x_, 1, -1))); @@ -468,6 +520,23 @@ TEST_F(InductionVarRangeTest, DivValue) { ExpectEqual(Value(), DivValue(Value(1), Value(0))); // unsafe } +TEST_F(InductionVarRangeTest, DivValueSpecial) { + const int32_t min_value = std::numeric_limits<int32_t>::min(); + const int32_t max_value = std::numeric_limits<int32_t>::max(); + + // Unsafe. + ExpectEqual(Value(), DivValue(Value(min_value), Value(-1))); + + // Safe. + ExpectEqual(Value(1), DivValue(Value(min_value), Value(min_value))); + ExpectEqual(Value(1), DivValue(Value(max_value), Value(max_value))); + ExpectEqual(Value(min_value), DivValue(Value(min_value), Value(1))); + ExpectEqual(Value(max_value), DivValue(Value(max_value), Value(1))); + ExpectEqual(Value(-max_value), DivValue(Value(max_value), Value(-1))); + ExpectEqual(Value(-1), DivValue(Value(1), Value(-1))); + ExpectEqual(Value(1), DivValue(Value(-1), Value(-1))); +} + TEST_F(InductionVarRangeTest, MinValue) { ExpectEqual(Value(10), MinValue(Value(10), Value(100))); ExpectEqual(Value(x_, 1, -4), MinValue(Value(x_, 1, -4), Value(x_, 1, -1))); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index a5acab81ab..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) { @@ -303,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(), @@ -313,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, @@ -322,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" @@ -350,11 +360,39 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, } // 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, @@ -364,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); @@ -374,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, @@ -391,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) { @@ -557,8 +758,9 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, 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; @@ -781,16 +983,16 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, 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; @@ -883,7 +1085,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, 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, diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 9dd9bf5ad8..cdb2167082 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -101,12 +101,18 @@ 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 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 @@ -118,6 +124,57 @@ class HInliner : public HOptimization { 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/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 ea8669fa18..8cbdcbbcaf 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1825,6 +1825,90 @@ void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) { GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter); } +void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register out = locations->Out().AsRegister<Register>(); + Register in = locations->InAt(0).AsRegister<Register>(); + + __ rbit(out, in); +} + +void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>(); + Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); + Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>(); + Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>(); + + __ rbit(out_reg_lo, in_reg_hi); + __ rbit(out_reg_hi, in_reg_lo); +} + +void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register out = locations->Out().AsRegister<Register>(); + Register in = locations->InAt(0).AsRegister<Register>(); + + __ rev(out, in); +} + +void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>(); + Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); + Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>(); + Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>(); + + __ rev(out_reg_lo, in_reg_hi); + __ rev(out_reg_hi, in_reg_lo); +} + +void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register out = locations->Out().AsRegister<Register>(); + Register in = locations->InAt(0).AsRegister<Register>(); + + __ revsh(out, in); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -1834,12 +1918,7 @@ void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) } UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(IntegerReverse) -UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes) UNIMPLEMENTED_INTRINSIC(LongBitCount) -UNIMPLEMENTED_INTRINSIC(LongReverse) -UNIMPLEMENTED_INTRINSIC(LongReverseBytes) -UNIMPLEMENTED_INTRINSIC(ShortReverseBytes) UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 8741fd284f..b5f15fe22d 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -99,7 +99,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) { // restored! class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 { public: - explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPathARM64(HInvoke* invoke) + : SlowPathCodeARM64(invoke), invoke_(invoke) { } void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index c8629644b6..2f183c3a62 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -99,7 +99,7 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS* codegen) { // restored! class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : SlowPathCodeMIPS(invoke), invoke_(invoke) { } void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { CodeGeneratorMIPS* codegen = down_cast<CodeGeneratorMIPS*>(codegen_in); @@ -407,7 +407,7 @@ void IntrinsicCodeGeneratorMIPS::VisitIntegerReverseBytes(HInvoke* invoke) { Primitive::kPrimInt, IsR2OrNewer(), IsR6(), - false, + /* reverseBits */ false, GetAssembler()); } @@ -421,7 +421,7 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverseBytes(HInvoke* invoke) { Primitive::kPrimLong, IsR2OrNewer(), IsR6(), - false, + /* reverseBits */ false, GetAssembler()); } @@ -435,7 +435,7 @@ void IntrinsicCodeGeneratorMIPS::VisitShortReverseBytes(HInvoke* invoke) { Primitive::kPrimShort, IsR2OrNewer(), IsR6(), - false, + /* reverseBits */ false, GetAssembler()); } @@ -475,7 +475,7 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* in } void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeroes(invoke->GetLocations(), false, IsR6(), GetAssembler()); + GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler()); } // int java.lang.Long.numberOfLeadingZeros(long i) @@ -484,7 +484,7 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invok } void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeroes(invoke->GetLocations(), true, IsR6(), GetAssembler()); + GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler()); } static void GenNumberOfTrailingZeroes(LocationSummary* locations, @@ -497,7 +497,6 @@ static void GenNumberOfTrailingZeroes(LocationSummary* locations, Register in; if (is64bit) { - MipsLabel done; Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); in_lo = locations->InAt(0).AsRegisterPairLow<Register>(); @@ -588,7 +587,11 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* i } void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), false, IsR6(), IsR2OrNewer(), GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), + /* is64bit */ false, + IsR6(), + IsR2OrNewer(), + GetAssembler()); } // int java.lang.Long.numberOfTrailingZeros(long i) @@ -597,7 +600,11 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invo } void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), true, IsR6(), IsR2OrNewer(), GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), + /* is64bit */ true, + IsR6(), + IsR2OrNewer(), + GetAssembler()); } enum RotationDirection { @@ -806,7 +813,7 @@ void IntrinsicCodeGeneratorMIPS::VisitIntegerReverse(HInvoke* invoke) { Primitive::kPrimInt, IsR2OrNewer(), IsR6(), - true, + /* reverseBits */ true, GetAssembler()); } @@ -820,10 +827,561 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) { Primitive::kPrimLong, IsR2OrNewer(), IsR6(), - true, + /* reverseBits */ true, GetAssembler()); } +static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); +} + +static void MathAbsFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { + FRegister in = locations->InAt(0).AsFpuRegister<FRegister>(); + FRegister out = locations->Out().AsFpuRegister<FRegister>(); + + if (is64bit) { + __ AbsD(out, in); + } else { + __ AbsS(out, in); + } +} + +// double java.lang.Math.abs(double) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsDouble(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsDouble(HInvoke* invoke) { + MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); +} + +// float java.lang.Math.abs(float) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsFloat(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsFloat(HInvoke* invoke) { + MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); +} + +static void GenAbsInteger(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { + if (is64bit) { + Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); + Register out_lo = locations->Out().AsRegisterPairLow<Register>(); + Register out_hi = locations->Out().AsRegisterPairHigh<Register>(); + + // The comments in this section show the analogous operations which would + // be performed if we had 64-bit registers "in", and "out". + // __ Dsra32(AT, in, 31); + __ Sra(AT, in_hi, 31); + // __ Xor(out, in, AT); + __ Xor(TMP, in_lo, AT); + __ Xor(out_hi, in_hi, AT); + // __ Dsubu(out, out, AT); + __ Subu(out_lo, TMP, AT); + __ Sltu(TMP, out_lo, TMP); + __ Addu(out_hi, out_hi, TMP); + } else { + Register in = locations->InAt(0).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + __ Sra(AT, in, 31); + __ Xor(out, in, AT); + __ Subu(out, out, AT); + } +} + +// int java.lang.Math.abs(int) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsInt(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsInt(HInvoke* invoke) { + GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); +} + +// long java.lang.Math.abs(long) +void IntrinsicLocationsBuilderMIPS::VisitMathAbsLong(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathAbsLong(HInvoke* invoke) { + GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); +} + +static void GenMinMaxFP(LocationSummary* locations, + bool is_min, + Primitive::Type type, + bool is_R6, + MipsAssembler* assembler) { + FRegister out = locations->Out().AsFpuRegister<FRegister>(); + FRegister a = locations->InAt(0).AsFpuRegister<FRegister>(); + FRegister b = locations->InAt(1).AsFpuRegister<FRegister>(); + + if (is_R6) { + MipsLabel noNaNs; + MipsLabel done; + FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == Primitive::kPrimDouble) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinD(out, a, b); + } else { + __ MaxD(out, a, b); + } + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinS(out, a, b); + } else { + __ MaxS(out, a, b); + } + } + + __ Bind(&done); + } else { + MipsLabel ordered; + MipsLabel compare; + MipsLabel select; + MipsLabel done; + + if (type == Primitive::kPrimDouble) { + __ CunD(a, b); + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + __ CunS(a, b); + } + __ Bc1f(&ordered); + + // a or b (or both) is a NaN. Return one, which is a NaN. + if (type == Primitive::kPrimDouble) { + __ CeqD(b, b); + } else { + __ CeqS(b, b); + } + __ B(&select); + + __ Bind(&ordered); + + // Neither is a NaN. + // a == b? (-0.0 compares equal with +0.0) + // If equal, handle zeroes, else compare further. + if (type == Primitive::kPrimDouble) { + __ CeqD(a, b); + } else { + __ CeqS(a, b); + } + __ Bc1f(&compare); + + // a == b either bit for bit or one is -0.0 and the other is +0.0. + if (type == Primitive::kPrimDouble) { + __ MoveFromFpuHigh(TMP, a); + __ MoveFromFpuHigh(AT, b); + } else { + __ Mfc1(TMP, a); + __ Mfc1(AT, b); + } + + if (is_min) { + // -0.0 prevails over +0.0. + __ Or(TMP, TMP, AT); + } else { + // +0.0 prevails over -0.0. + __ And(TMP, TMP, AT); + } + + if (type == Primitive::kPrimDouble) { + __ Mfc1(AT, a); + __ Mtc1(AT, out); + __ MoveToFpuHigh(TMP, out); + } else { + __ Mtc1(TMP, out); + } + __ B(&done); + + __ Bind(&compare); + + if (type == Primitive::kPrimDouble) { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeD(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeD(b, a); // b <= a + } + } else { + if (is_min) { + // return (a <= b) ? a : b; + __ ColeS(a, b); + } else { + // return (a >= b) ? a : b; + __ ColeS(b, a); // b <= a + } + } + + __ Bind(&select); + + if (type == Primitive::kPrimDouble) { + __ MovtD(out, a); + __ MovfD(out, b); + } else { + __ MovtS(out, a); + __ MovfS(out, b); + } + + __ Bind(&done); + } +} + +static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap); +} + +// double java.lang.Math.min(double, double) +void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimDouble, + IsR6(), + GetAssembler()); +} + +// float java.lang.Math.min(float, float) +void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimFloat, + IsR6(), + GetAssembler()); +} + +// double java.lang.Math.max(double, double) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimDouble, + IsR6(), + GetAssembler()); +} + +// float java.lang.Math.max(float, float) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimFloat, + IsR6(), + GetAssembler()); +} + +static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +static void GenMinMax(LocationSummary* locations, + bool is_min, + Primitive::Type type, + bool is_R6, + MipsAssembler* assembler) { + if (is_R6) { + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions + // always change the target (output) register. If the condition is + // true the output register gets the contents of the "rs" register; + // otherwise, the output register is set to zero. One consequence + // of this is that to implement something like "rd = c==0 ? rs : rt" + // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions. + // After executing this pair of instructions one of the output + // registers from the pair will necessarily contain zero. Then the + // code ORs the output registers from the SELEQZ/SELNEZ instructions + // to get the final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (type == Primitive::kPrimLong) { + Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); + Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>(); + Register out_lo = locations->Out().AsRegisterPairLow<Register>(); + Register out_hi = locations->Out().AsRegisterPairHigh<Register>(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, b_hi, a_hi); + __ Bne(b_hi, a_hi, &compare_done); + + __ Sltu(TMP, b_lo, a_lo); + + __ Bind(&compare_done); + + if (is_min) { + __ Seleqz(AT, a_lo, TMP); + __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo + // because at this point we're + // done using a_lo/b_lo. + } else { + __ Selnez(AT, a_lo, TMP); + __ Seleqz(out_lo, b_lo, TMP); // ditto + } + __ Or(out_lo, out_lo, AT); + if (is_min) { + __ Seleqz(AT, a_hi, TMP); + __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } else { + __ Selnez(AT, a_hi, TMP); + __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi + } + __ Or(out_hi, out_hi, AT); + } + } else { + DCHECK_EQ(type, Primitive::kPrimInt); + Register a = locations->InAt(0).AsRegister<Register>(); + Register b = locations->InAt(1).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, b, a); + if (is_min) { + __ Seleqz(TMP, a, AT); + __ Selnez(AT, b, AT); + } else { + __ Selnez(TMP, a, AT); + __ Seleqz(AT, b, AT); + } + __ Or(out, TMP, AT); + } + } + } else { + if (type == Primitive::kPrimLong) { + Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); + Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>(); + Register out_lo = locations->Out().AsRegisterPairLow<Register>(); + Register out_hi = locations->Out().AsRegisterPairHigh<Register>(); + + MipsLabel compare_done; + + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } + } else { + __ Slt(TMP, a_hi, b_hi); + __ Bne(a_hi, b_hi, &compare_done); + + __ Sltu(TMP, a_lo, b_lo); + + __ Bind(&compare_done); + + if (is_min) { + if (out_lo != a_lo) { + __ Movn(out_hi, a_hi, TMP); + __ Movn(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movz(out_hi, b_hi, TMP); + __ Movz(out_lo, b_lo, TMP); + } + } else { + if (out_lo != a_lo) { + __ Movz(out_hi, a_hi, TMP); + __ Movz(out_lo, a_lo, TMP); + } + if (out_lo != b_lo) { + __ Movn(out_hi, b_hi, TMP); + __ Movn(out_lo, b_lo, TMP); + } + } + } + } else { + DCHECK_EQ(type, Primitive::kPrimInt); + Register a = locations->InAt(0).AsRegister<Register>(); + Register b = locations->InAt(1).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + if (a == b) { + if (out != a) { + __ Move(out, a); + } + } else { + __ Slt(AT, a, b); + if (is_min) { + if (out != a) { + __ Movn(out, a, AT); + } + if (out != b) { + __ Movz(out, b, AT); + } + } else { + if (out != a) { + __ Movz(out, a, AT); + } + if (out != b) { + __ Movn(out, b, AT); + } + } + } + } + } +} + +// int java.lang.Math.min(int, int) +void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimInt, + IsR6(), + GetAssembler()); +} + +// long java.lang.Math.min(long, long) +void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimLong, + IsR6(), + GetAssembler()); +} + +// int java.lang.Math.max(int, int) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimInt, + IsR6(), + GetAssembler()); +} + +// long java.lang.Math.max(long, long) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimLong, + IsR6(), + GetAssembler()); +} + +// double java.lang.Math.sqrt(double) +void IntrinsicLocationsBuilderMIPS::VisitMathSqrt(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitMathSqrt(HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + MipsAssembler* assembler = GetAssembler(); + FRegister in = locations->InAt(0).AsFpuRegister<FRegister>(); + FRegister out = locations->Out().AsFpuRegister<FRegister>(); + + __ SqrtD(out, in); +} + // byte libcore.io.Memory.peekByte(long address) void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekByte(HInvoke* invoke) { CreateIntToIntLocations(arena_, invoke); @@ -1151,19 +1709,6 @@ void IntrinsicCodeGeneratorMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) UNIMPLEMENTED_INTRINSIC(IntegerBitCount) UNIMPLEMENTED_INTRINSIC(LongBitCount) -UNIMPLEMENTED_INTRINSIC(MathAbsDouble) -UNIMPLEMENTED_INTRINSIC(MathAbsFloat) -UNIMPLEMENTED_INTRINSIC(MathAbsInt) -UNIMPLEMENTED_INTRINSIC(MathAbsLong) -UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) -UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) -UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble) -UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat) -UNIMPLEMENTED_INTRINSIC(MathMinIntInt) -UNIMPLEMENTED_INTRINSIC(MathMinLongLong) -UNIMPLEMENTED_INTRINSIC(MathMaxIntInt) -UNIMPLEMENTED_INTRINSIC(MathMaxLongLong) -UNIMPLEMENTED_INTRINSIC(MathSqrt) UNIMPLEMENTED_INTRINSIC(MathCeil) UNIMPLEMENTED_INTRINSIC(MathFloor) UNIMPLEMENTED_INTRINSIC(MathRint) diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index cf3a3657de..bd4f5329da 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -87,7 +87,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { // restored! class IntrinsicSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit IntrinsicSlowPathMIPS64(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPathMIPS64(HInvoke* invoke) + : SlowPathCodeMIPS64(invoke), invoke_(invoke) { } void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { CodeGeneratorMIPS64* codegen = down_cast<CodeGeneratorMIPS64*>(codegen_in); @@ -580,25 +581,71 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathAbsLong(HInvoke* invoke) { static void GenMinMaxFP(LocationSummary* locations, bool is_min, - bool is_double, + Primitive::Type type, Mips64Assembler* assembler) { - FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>(); - FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>(); + FpuRegister a = locations->InAt(0).AsFpuRegister<FpuRegister>(); + FpuRegister b = locations->InAt(1).AsFpuRegister<FpuRegister>(); FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); - if (is_double) { + Mips64Label noNaNs; + Mips64Label done; + FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP; + + // When Java computes min/max it prefers a NaN to a number; the + // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of + // the inputs is a NaN and the other is a valid number, the MIPS + // instruction will return the number; Java wants the NaN value + // returned. This is why there is extra logic preceding the use of + // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a + // NaN, return the NaN, otherwise return the min/max. + if (type == Primitive::kPrimDouble) { + __ CmpUnD(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqD(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelD(ftmp, a, b); + + if (ftmp != out) { + __ MovD(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + if (is_min) { - __ MinD(out, lhs, rhs); + __ MinD(out, a, b); } else { - __ MaxD(out, lhs, rhs); + __ MaxD(out, a, b); } } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + __ CmpUnS(FTMP, a, b); + __ Bc1eqz(FTMP, &noNaNs); + + // One of the inputs is a NaN + __ CmpEqS(ftmp, a, a); + // If a == a then b is the NaN, otherwise a is the NaN. + __ SelS(ftmp, a, b); + + if (ftmp != out) { + __ MovS(out, ftmp); + } + + __ Bc(&done); + + __ Bind(&noNaNs); + if (is_min) { - __ MinS(out, lhs, rhs); + __ MinS(out, a, b); } else { - __ MaxS(out, lhs, rhs); + __ MaxS(out, a, b); } } + + __ Bind(&done); } static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { @@ -616,7 +663,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) } void IntrinsicCodeGeneratorMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimDouble, GetAssembler()); } // float java.lang.Math.min(float, float) @@ -625,7 +672,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimFloat, GetAssembler()); } // double java.lang.Math.max(double, double) @@ -634,7 +681,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) } void IntrinsicCodeGeneratorMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimDouble, GetAssembler()); } // float java.lang.Math.max(float, float) @@ -643,7 +690,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetAssembler()); + GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimFloat, GetAssembler()); } static void GenMinMax(LocationSummary* locations, @@ -653,49 +700,55 @@ static void GenMinMax(LocationSummary* locations, GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); - // Some architectures, such as ARM and MIPS (prior to r6), have a - // conditional move instruction which only changes the target - // (output) register if the condition is true (MIPS prior to r6 had - // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always - // change the target (output) register. If the condition is true the - // output register gets the contents of the "rs" register; otherwise, - // the output register is set to zero. One consequence of this is - // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 - // needs to use a pair of SELEQZ/SELNEZ instructions. After - // executing this pair of instructions one of the output registers - // from the pair will necessarily contain zero. Then the code ORs the - // output registers from the SELEQZ/SELNEZ instructions to get the - // final result. - // - // The initial test to see if the output register is same as the - // first input register is needed to make sure that value in the - // first input register isn't clobbered before we've finished - // computing the output value. The logic in the corresponding else - // clause performs the same task but makes sure the second input - // register isn't clobbered in the event that it's the same register - // as the output register; the else clause also handles the case - // where the output register is distinct from both the first, and the - // second input registers. - if (out == lhs) { - __ Slt(AT, rhs, lhs); - if (is_min) { - __ Seleqz(out, lhs, AT); - __ Selnez(AT, rhs, AT); - } else { - __ Selnez(out, lhs, AT); - __ Seleqz(AT, rhs, AT); + if (lhs == rhs) { + if (out != lhs) { + __ Move(out, lhs); } } else { - __ Slt(AT, lhs, rhs); - if (is_min) { - __ Seleqz(out, rhs, AT); - __ Selnez(AT, lhs, AT); + // Some architectures, such as ARM and MIPS (prior to r6), have a + // conditional move instruction which only changes the target + // (output) register if the condition is true (MIPS prior to r6 had + // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always + // change the target (output) register. If the condition is true the + // output register gets the contents of the "rs" register; otherwise, + // the output register is set to zero. One consequence of this is + // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6 + // needs to use a pair of SELEQZ/SELNEZ instructions. After + // executing this pair of instructions one of the output registers + // from the pair will necessarily contain zero. Then the code ORs the + // output registers from the SELEQZ/SELNEZ instructions to get the + // final result. + // + // The initial test to see if the output register is same as the + // first input register is needed to make sure that value in the + // first input register isn't clobbered before we've finished + // computing the output value. The logic in the corresponding else + // clause performs the same task but makes sure the second input + // register isn't clobbered in the event that it's the same register + // as the output register; the else clause also handles the case + // where the output register is distinct from both the first, and the + // second input registers. + if (out == lhs) { + __ Slt(AT, rhs, lhs); + if (is_min) { + __ Seleqz(out, lhs, AT); + __ Selnez(AT, rhs, AT); + } else { + __ Selnez(out, lhs, AT); + __ Seleqz(AT, rhs, AT); + } } else { - __ Selnez(out, rhs, AT); - __ Seleqz(AT, lhs, AT); + __ Slt(AT, lhs, rhs); + if (is_min) { + __ Seleqz(out, rhs, AT); + __ Selnez(AT, lhs, AT); + } else { + __ Selnez(out, rhs, AT); + __ Seleqz(AT, lhs, AT); + } } + __ Or(out, out, AT); } - __ Or(out, out, AT); } static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h index e70afd29f0..c1f9ae6425 100644 --- a/compiler/optimizing/intrinsics_utils.h +++ b/compiler/optimizing/intrinsics_utils.h @@ -39,7 +39,7 @@ namespace art { template <typename TDexCallingConvention> class IntrinsicSlowPath : public SlowPathCode { public: - explicit IntrinsicSlowPath(HInvoke* invoke) : invoke_(invoke) { } + explicit IntrinsicSlowPath(HInvoke* invoke) : SlowPathCode(invoke), invoke_(invoke) { } Location MoveArguments(CodeGenerator* codegen) { TDexCallingConvention calling_convention_visitor; 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 b26ce0aa13..f9acb089ee 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1178,19 +1178,19 @@ HConstant* HUnaryOperation::TryStaticEvaluation() const { } HConstant* HBinaryOperation::TryStaticEvaluation() const { - if (GetLeft()->IsIntConstant()) { - if (GetRight()->IsIntConstant()) { - return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsIntConstant()); - } else if (GetRight()->IsLongConstant()) { - return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsLongConstant()); - } + if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) { + return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsIntConstant()); } else if (GetLeft()->IsLongConstant()) { if (GetRight()->IsIntConstant()) { + // The binop(long, int) case is only valid for shifts and rotations. + DCHECK(IsShl() || IsShr() || IsUShr() || IsRor()) << DebugName(); return Evaluate(GetLeft()->AsLongConstant(), GetRight()->AsIntConstant()); } else if (GetRight()->IsLongConstant()) { return Evaluate(GetLeft()->AsLongConstant(), GetRight()->AsLongConstant()); } } else if (GetLeft()->IsNullConstant() && GetRight()->IsNullConstant()) { + // The binop(null, null) case is only valid for equal and not-equal conditions. + DCHECK(IsEqual() || IsNotEqual()) << DebugName(); return Evaluate(GetLeft()->AsNullConstant(), GetRight()->AsNullConstant()); } else if (kEnableFloatingPointStaticEvaluation) { if (GetLeft()->IsFloatConstant() && GetRight()->IsFloatConstant()) { @@ -1420,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); @@ -1573,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_; @@ -1815,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()); @@ -1880,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. @@ -1898,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()); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 01ba704610..b355883a72 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. @@ -618,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); }; @@ -972,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, @@ -2063,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_; } @@ -2101,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); } @@ -2816,20 +2821,15 @@ class HBinaryOperation : public HExpression<2> { // 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; + LOG(FATAL) << DebugName() << " is not defined for the (null, null) case."; + UNREACHABLE(); } 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, - HLongConstant* y ATTRIBUTE_UNUSED) const { - VLOG(compiler) << DebugName() << " is not defined for the (int, long) case."; - return nullptr; - } virtual HConstant* Evaluate(HLongConstant* x ATTRIBUTE_UNUSED, HIntConstant* y ATTRIBUTE_UNUSED) const { - VLOG(compiler) << DebugName() << " is not defined for the (long, int) case."; - return nullptr; + LOG(FATAL) << DebugName() << " is not defined for the (long, int) case."; + UNREACHABLE(); } virtual HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const = 0; virtual HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const = 0; @@ -4300,8 +4300,6 @@ class HShl : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); } - // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this - // case is handled as `x << static_cast<int>(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4346,8 +4344,6 @@ class HShr : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); } - // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this - // case is handled as `x >> static_cast<int>(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4393,8 +4389,6 @@ class HUShr : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); } - // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this - // case is handled as `x >>> static_cast<int>(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4430,21 +4424,12 @@ class HAnd : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template <typename T, typename U> - auto Compute(T x, U y) const -> decltype(x & y) { return x & y; } + template <typename T> T Compute(T x, T y) const { return x & y; } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } - HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -4476,21 +4461,12 @@ class HOr : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template <typename T, typename U> - auto Compute(T x, U y) const -> decltype(x | y) { return x | y; } + template <typename T> T Compute(T x, T y) const { return x | y; } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } - HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -4522,21 +4498,12 @@ class HXor : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template <typename T, typename U> - auto Compute(T x, U y) const -> decltype(x ^ y) { return x ^ y; } + template <typename T> T Compute(T x, T y) const { return x ^ y; } HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); } - HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue()), GetDexPc()); - } HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue()), GetDexPc()); @@ -6370,6 +6337,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/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/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/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index f96376d9fe..a894565425 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -545,6 +545,9 @@ class ArmAssembler : public Assembler { virtual void movw(Register rd, uint16_t imm16, Condition cond = AL) = 0; virtual void movt(Register rd, uint16_t imm16, Condition cond = AL) = 0; virtual void rbit(Register rd, Register rm, Condition cond = AL) = 0; + virtual void rev(Register rd, Register rm, Condition cond = AL) = 0; + virtual void rev16(Register rd, Register rm, Condition cond = AL) = 0; + virtual void revsh(Register rd, Register rm, Condition cond = AL) = 0; // Multiply instructions. virtual void mul(Register rd, Register rn, Register rm, Condition cond = AL) = 0; diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index ebca25bbf9..0a227b21cd 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -750,6 +750,35 @@ void Arm32Assembler::movt(Register rd, uint16_t imm16, Condition cond) { } +void Arm32Assembler::EmitMiscellaneous(Condition cond, uint8_t op1, + uint8_t op2, uint32_t a_part, + uint32_t rest) { + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B26 | B25 | B23 | + (op1 << 20) | + (a_part << 16) | + (op2 << 5) | + B4 | + rest; + Emit(encoding); +} + + +void Arm32Assembler::EmitReverseBytes(Register rd, Register rm, Condition cond, + uint8_t op1, uint8_t op2) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(rm, kNoRegister); + CHECK_NE(cond, kNoCondition); + CHECK_NE(rd, PC); + CHECK_NE(rm, PC); + + int32_t encoding = (static_cast<int32_t>(rd) << kRdShift) | + (0b1111 << 8) | + static_cast<int32_t>(rm); + EmitMiscellaneous(cond, op1, op2, 0b1111, encoding); +} + + void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) { CHECK_NE(rd, kNoRegister); CHECK_NE(rm, kNoRegister); @@ -764,6 +793,21 @@ void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) { } +void Arm32Assembler::rev(Register rd, Register rm, Condition cond) { + EmitReverseBytes(rd, rm, cond, 0b011, 0b001); +} + + +void Arm32Assembler::rev16(Register rd, Register rm, Condition cond) { + EmitReverseBytes(rd, rm, cond, 0b011, 0b101); +} + + +void Arm32Assembler::revsh(Register rd, Register rm, Condition cond) { + EmitReverseBytes(rd, rm, cond, 0b111, 0b101); +} + + void Arm32Assembler::EmitMulOp(Condition cond, int32_t opcode, Register rd, Register rn, Register rm, Register rs) { diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index bf332feb62..e3e05caf92 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -91,6 +91,9 @@ class Arm32Assembler FINAL : public ArmAssembler { void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev16(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void revsh(Register rd, Register rm, Condition cond = AL) OVERRIDE; // Multiply instructions. void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE; @@ -388,6 +391,11 @@ class Arm32Assembler FINAL : public ArmAssembler { void EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond); + void EmitMiscellaneous(Condition cond, uint8_t op1, uint8_t op2, + uint32_t a_part, uint32_t rest); + void EmitReverseBytes(Register rd, Register rm, Condition cond, + uint8_t op1, uint8_t op2); + void EmitBranch(Condition cond, Label* label, bool link); static int32_t EncodeBranchOffset(int offset, int32_t inst); static int DecodeBranchOffset(int32_t inst); diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc index 43805966a9..e570e22fca 100644 --- a/compiler/utils/arm/assembler_arm32_test.cc +++ b/compiler/utils/arm/assembler_arm32_test.cc @@ -887,4 +887,16 @@ TEST_F(AssemblerArm32Test, rbit) { T3Helper(&arm::Arm32Assembler::rbit, true, "rbit{cond} {reg1}, {reg2}", "rbit"); } +TEST_F(AssemblerArm32Test, rev) { + T3Helper(&arm::Arm32Assembler::rev, true, "rev{cond} {reg1}, {reg2}", "rev"); +} + +TEST_F(AssemblerArm32Test, rev16) { + T3Helper(&arm::Arm32Assembler::rev16, true, "rev16{cond} {reg1}, {reg2}", "rev16"); +} + +TEST_F(AssemblerArm32Test, revsh) { + T3Helper(&arm::Arm32Assembler::revsh, true, "revsh{cond} {reg1}, {reg2}", "revsh"); +} + } // namespace art diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 52023a67ee..15298b390b 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -2569,20 +2569,36 @@ void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x } +void Thumb2Assembler::Emit32Miscellaneous(uint8_t op1, + uint8_t op2, + uint32_t rest_encoding) { + int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B23 | + op1 << 20 | + 0xf << 12 | + B7 | + op2 << 4 | + rest_encoding; + Emit32(encoding); +} + + +void Thumb2Assembler::Emit16Miscellaneous(uint32_t rest_encoding) { + int16_t encoding = B15 | B13 | B12 | + rest_encoding; + Emit16(encoding); +} + void Thumb2Assembler::clz(Register rd, Register rm, Condition cond) { CHECK_NE(rd, kNoRegister); CHECK_NE(rm, kNoRegister); CheckCondition(cond); CHECK_NE(rd, PC); CHECK_NE(rm, PC); - int32_t encoding = B31 | B30 | B29 | B28 | B27 | - B25 | B23 | B21 | B20 | + int32_t encoding = static_cast<uint32_t>(rm) << 16 | - 0xf << 12 | static_cast<uint32_t>(rd) << 8 | - B7 | static_cast<uint32_t>(rm); - Emit32(encoding); + Emit32Miscellaneous(0b11, 0b00, encoding); } @@ -2630,14 +2646,55 @@ void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) { CHECK_NE(rm, PC); CHECK_NE(rd, SP); CHECK_NE(rm, SP); - int32_t encoding = B31 | B30 | B29 | B28 | B27 | - B25 | B23 | B20 | + int32_t encoding = static_cast<uint32_t>(rm) << 16 | - 0xf << 12 | static_cast<uint32_t>(rd) << 8 | - B7 | B5 | static_cast<uint32_t>(rm); - Emit32(encoding); + + Emit32Miscellaneous(0b01, 0b10, encoding); +} + + +void Thumb2Assembler::EmitReverseBytes(Register rd, Register rm, + uint32_t op) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(rm, kNoRegister); + CHECK_NE(rd, PC); + CHECK_NE(rm, PC); + CHECK_NE(rd, SP); + CHECK_NE(rm, SP); + + if (!IsHighRegister(rd) && !IsHighRegister(rm) && !force_32bit_) { + uint16_t t1_op = B11 | B9 | (op << 6); + int16_t encoding = t1_op | + static_cast<uint16_t>(rm) << 3 | + static_cast<uint16_t>(rd); + Emit16Miscellaneous(encoding); + } else { + int32_t encoding = + static_cast<uint32_t>(rm) << 16 | + static_cast<uint32_t>(rd) << 8 | + static_cast<uint32_t>(rm); + Emit32Miscellaneous(0b01, op, encoding); + } +} + + +void Thumb2Assembler::rev(Register rd, Register rm, Condition cond) { + CheckCondition(cond); + EmitReverseBytes(rd, rm, 0b00); +} + + +void Thumb2Assembler::rev16(Register rd, Register rm, Condition cond) { + CheckCondition(cond); + EmitReverseBytes(rd, rm, 0b01); +} + + +void Thumb2Assembler::revsh(Register rd, Register rm, Condition cond) { + CheckCondition(cond); + EmitReverseBytes(rd, rm, 0b11); } diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index bf07b2dbf8..6b61acafac 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -117,6 +117,9 @@ class Thumb2Assembler FINAL : public ArmAssembler { void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE; void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void rev16(Register rd, Register rm, Condition cond = AL) OVERRIDE; + void revsh(Register rd, Register rm, Condition cond = AL) OVERRIDE; // Multiply instructions. void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE; @@ -644,6 +647,17 @@ class Thumb2Assembler FINAL : public ArmAssembler { Register rd, const ShifterOperand& so); + // Emit a single 32 bit miscellaneous instruction. + void Emit32Miscellaneous(uint8_t op1, + uint8_t op2, + uint32_t rest_encoding); + + // Emit reverse byte instructions: rev, rev16, revsh. + void EmitReverseBytes(Register rd, Register rm, uint32_t op); + + // Emit a single 16 bit miscellaneous instruction. + void Emit16Miscellaneous(uint32_t rest_encoding); + // Must the instruction be 32 bits or can it possibly be encoded // in 16 bits? bool Is32BitDataProcessing(Condition cond, diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc index 7b32b0fd26..650b08900b 100644 --- a/compiler/utils/arm/assembler_thumb2_test.cc +++ b/compiler/utils/arm/assembler_thumb2_test.cc @@ -1331,4 +1331,28 @@ TEST_F(AssemblerThumb2Test, rbit) { DriverStr(expected, "rbit"); } +TEST_F(AssemblerThumb2Test, rev) { + __ rev(arm::R1, arm::R0); + + const char* expected = "rev r1, r0\n"; + + DriverStr(expected, "rev"); +} + +TEST_F(AssemblerThumb2Test, rev16) { + __ rev16(arm::R1, arm::R0); + + const char* expected = "rev16 r1, r0\n"; + + DriverStr(expected, "rev16"); +} + +TEST_F(AssemblerThumb2Test, revsh) { + __ revsh(arm::R1, arm::R0); + + const char* expected = "revsh r1, r0\n"; + + DriverStr(expected, "revsh"); +} + } // namespace art diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 6fd65ee9a4..7c41813457 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -537,12 +537,20 @@ void MipsAssembler::Bgtz(Register rt, uint16_t imm16) { EmitI(0x7, rt, static_cast<Register>(0), imm16); } +void MipsAssembler::Bc1f(uint16_t imm16) { + Bc1f(0, imm16); +} + void MipsAssembler::Bc1f(int cc, uint16_t imm16) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16); } +void MipsAssembler::Bc1t(uint16_t imm16) { + Bc1t(0, imm16); +} + void MipsAssembler::Bc1t(int cc, uint16_t imm16) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; @@ -843,6 +851,22 @@ void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) { EmitFR(0x11, 0x11, ft, fs, fd, 0x3); } +void MipsAssembler::SqrtS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4); +} + +void MipsAssembler::SqrtD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4); +} + +void MipsAssembler::AbsS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5); +} + +void MipsAssembler::AbsD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5); +} + void MipsAssembler::MovS(FRegister fd, FRegister fs) { EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6); } @@ -859,84 +883,140 @@ void MipsAssembler::NegD(FRegister fd, FRegister fs) { EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7); } +void MipsAssembler::CunS(FRegister fs, FRegister ft) { + CunS(0, fs, ft); +} + void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31); } +void MipsAssembler::CeqS(FRegister fs, FRegister ft) { + CeqS(0, fs, ft); +} + void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32); } +void MipsAssembler::CueqS(FRegister fs, FRegister ft) { + CueqS(0, fs, ft); +} + void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33); } +void MipsAssembler::ColtS(FRegister fs, FRegister ft) { + ColtS(0, fs, ft); +} + void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34); } +void MipsAssembler::CultS(FRegister fs, FRegister ft) { + CultS(0, fs, ft); +} + void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35); } +void MipsAssembler::ColeS(FRegister fs, FRegister ft) { + ColeS(0, fs, ft); +} + void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36); } +void MipsAssembler::CuleS(FRegister fs, FRegister ft) { + CuleS(0, fs, ft); +} + void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37); } +void MipsAssembler::CunD(FRegister fs, FRegister ft) { + CunD(0, fs, ft); +} + void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31); } +void MipsAssembler::CeqD(FRegister fs, FRegister ft) { + CeqD(0, fs, ft); +} + void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32); } +void MipsAssembler::CueqD(FRegister fs, FRegister ft) { + CueqD(0, fs, ft); +} + void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33); } +void MipsAssembler::ColtD(FRegister fs, FRegister ft) { + ColtD(0, fs, ft); +} + void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34); } +void MipsAssembler::CultD(FRegister fs, FRegister ft) { + CultD(0, fs, ft); +} + void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35); } +void MipsAssembler::ColeD(FRegister fs, FRegister ft) { + ColeD(0, fs, ft); +} + void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36); } +void MipsAssembler::CuleD(FRegister fs, FRegister ft) { + CuleD(0, fs, ft); +} + void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; @@ -1055,6 +1135,70 @@ void MipsAssembler::Movt(Register rd, Register rs, int cc) { EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01); } +void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11); +} + +void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11); +} + +void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11); +} + +void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) { + CHECK(!IsR6()); + CHECK(IsUint<3>(cc)) << cc; + EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11); +} + +void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, ft, fs, fd, 0x10); +} + +void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, ft, fs, fd, 0x10); +} + +void MipsAssembler::ClassS(FRegister fd, FRegister fs) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b); +} + +void MipsAssembler::ClassD(FRegister fd, FRegister fs) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b); +} + +void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, ft, fs, fd, 0x1c); +} + +void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, ft, fs, fd, 0x1c); +} + +void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x10, ft, fs, fd, 0x1e); +} + +void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, ft, fs, fd, 0x1e); +} + void MipsAssembler::TruncLS(FRegister fd, FRegister fs) { EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09); } @@ -1095,6 +1239,14 @@ void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) { EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21); } +void MipsAssembler::FloorWS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf); +} + +void MipsAssembler::FloorWD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf); +} + void MipsAssembler::Mfc1(Register rt, FRegister fs) { EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0); } @@ -2062,11 +2214,19 @@ void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) { } } +void MipsAssembler::Bc1f(MipsLabel* label) { + Bc1f(0, label); +} + void MipsAssembler::Bc1f(int cc, MipsLabel* label) { CHECK(IsUint<3>(cc)) << cc; Bcond(label, kCondF, static_cast<Register>(cc), ZERO); } +void MipsAssembler::Bc1t(MipsLabel* label) { + Bc1t(0, label); +} + void MipsAssembler::Bc1t(int cc, MipsLabel* label) { CHECK(IsUint<3>(cc)) << cc; Bcond(label, kCondT, static_cast<Register>(cc), ZERO); diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 2262af49b3..a7179fd1dc 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -51,6 +51,20 @@ enum StoreOperandType { kStoreDoubleword }; +// Used to test the values returned by ClassS/ClassD. +enum FPClassMaskType { + kSignalingNaN = 0x001, + kQuietNaN = 0x002, + kNegativeInfinity = 0x004, + kNegativeNormal = 0x008, + kNegativeSubnormal = 0x010, + kNegativeZero = 0x020, + kPositiveInfinity = 0x040, + kPositiveNormal = 0x080, + kPositiveSubnormal = 0x100, + kPositiveZero = 0x200, +}; + class MipsLabel : public Label { public: MipsLabel() : prev_branch_id_plus_one_(0) {} @@ -191,7 +205,9 @@ class MipsAssembler FINAL : public Assembler { void Bgez(Register rt, uint16_t imm16); void Blez(Register rt, uint16_t imm16); void Bgtz(Register rt, uint16_t imm16); + void Bc1f(uint16_t imm16); // R2 void Bc1f(int cc, uint16_t imm16); // R2 + void Bc1t(uint16_t imm16); // R2 void Bc1t(int cc, uint16_t imm16); // R2 void J(uint32_t addr26); void Jal(uint32_t addr26); @@ -227,24 +243,42 @@ class MipsAssembler FINAL : public Assembler { void SubD(FRegister fd, FRegister fs, FRegister ft); void MulD(FRegister fd, FRegister fs, FRegister ft); void DivD(FRegister fd, FRegister fs, FRegister ft); + void SqrtS(FRegister fd, FRegister fs); + void SqrtD(FRegister fd, FRegister fs); + void AbsS(FRegister fd, FRegister fs); + void AbsD(FRegister fd, FRegister fs); void MovS(FRegister fd, FRegister fs); void MovD(FRegister fd, FRegister fs); void NegS(FRegister fd, FRegister fs); void NegD(FRegister fd, FRegister fs); + void CunS(FRegister fs, FRegister ft); // R2 void CunS(int cc, FRegister fs, FRegister ft); // R2 + void CeqS(FRegister fs, FRegister ft); // R2 void CeqS(int cc, FRegister fs, FRegister ft); // R2 + void CueqS(FRegister fs, FRegister ft); // R2 void CueqS(int cc, FRegister fs, FRegister ft); // R2 + void ColtS(FRegister fs, FRegister ft); // R2 void ColtS(int cc, FRegister fs, FRegister ft); // R2 + void CultS(FRegister fs, FRegister ft); // R2 void CultS(int cc, FRegister fs, FRegister ft); // R2 + void ColeS(FRegister fs, FRegister ft); // R2 void ColeS(int cc, FRegister fs, FRegister ft); // R2 + void CuleS(FRegister fs, FRegister ft); // R2 void CuleS(int cc, FRegister fs, FRegister ft); // R2 + void CunD(FRegister fs, FRegister ft); // R2 void CunD(int cc, FRegister fs, FRegister ft); // R2 + void CeqD(FRegister fs, FRegister ft); // R2 void CeqD(int cc, FRegister fs, FRegister ft); // R2 + void CueqD(FRegister fs, FRegister ft); // R2 void CueqD(int cc, FRegister fs, FRegister ft); // R2 + void ColtD(FRegister fs, FRegister ft); // R2 void ColtD(int cc, FRegister fs, FRegister ft); // R2 + void CultD(FRegister fs, FRegister ft); // R2 void CultD(int cc, FRegister fs, FRegister ft); // R2 + void ColeD(FRegister fs, FRegister ft); // R2 void ColeD(int cc, FRegister fs, FRegister ft); // R2 + void CuleD(FRegister fs, FRegister ft); // R2 void CuleD(int cc, FRegister fs, FRegister ft); // R2 void CmpUnS(FRegister fd, FRegister fs, FRegister ft); // R6 void CmpEqS(FRegister fd, FRegister fs, FRegister ft); // R6 @@ -266,8 +300,20 @@ class MipsAssembler FINAL : public Assembler { void CmpOrD(FRegister fd, FRegister fs, FRegister ft); // R6 void CmpUneD(FRegister fd, FRegister fs, FRegister ft); // R6 void CmpNeD(FRegister fd, FRegister fs, FRegister ft); // R6 - void Movf(Register rd, Register rs, int cc); // R2 - void Movt(Register rd, Register rs, int cc); // R2 + void Movf(Register rd, Register rs, int cc = 0); // R2 + void Movt(Register rd, Register rs, int cc = 0); // R2 + void MovfS(FRegister fd, FRegister fs, int cc = 0); // R2 + void MovfD(FRegister fd, FRegister fs, int cc = 0); // R2 + void MovtS(FRegister fd, FRegister fs, int cc = 0); // R2 + void MovtD(FRegister fd, FRegister fs, int cc = 0); // R2 + void SelS(FRegister fd, FRegister fs, FRegister ft); // R6 + void SelD(FRegister fd, FRegister fs, FRegister ft); // R6 + void ClassS(FRegister fd, FRegister fs); // R6 + void ClassD(FRegister fd, FRegister fs); // R6 + void MinS(FRegister fd, FRegister fs, FRegister ft); // R6 + void MinD(FRegister fd, FRegister fs, FRegister ft); // R6 + void MaxS(FRegister fd, FRegister fs, FRegister ft); // R6 + void MaxD(FRegister fd, FRegister fs, FRegister ft); // R6 void TruncLS(FRegister fd, FRegister fs); // R2+, FR=1 void TruncLD(FRegister fd, FRegister fs); // R2+, FR=1 @@ -279,6 +325,8 @@ class MipsAssembler FINAL : public Assembler { void Cvtds(FRegister fd, FRegister fs); void Cvtsl(FRegister fd, FRegister fs); // R2+, FR=1 void Cvtdl(FRegister fd, FRegister fs); // R2+, FR=1 + void FloorWS(FRegister fd, FRegister fs); + void FloorWD(FRegister fd, FRegister fs); void Mfc1(Register rt, FRegister fs); void Mtc1(Register rt, FRegister fs); @@ -322,7 +370,9 @@ class MipsAssembler FINAL : public Assembler { void Bge(Register rs, Register rt, MipsLabel* label); void Bltu(Register rs, Register rt, MipsLabel* label); void Bgeu(Register rs, Register rt, MipsLabel* label); + void Bc1f(MipsLabel* label); // R2 void Bc1f(int cc, MipsLabel* label); // R2 + void Bc1t(MipsLabel* label); // R2 void Bc1t(int cc, MipsLabel* label); // R2 void Bc1eqz(FRegister ft, MipsLabel* label); // R6 void Bc1nez(FRegister ft, MipsLabel* label); // R6 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..cac12d1920 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" @@ -65,13 +66,13 @@ #include "interpreter/unstarted_runtime.h" #include "jit/offline_profiling_info.h" #include "leb128.h" +#include "linker/multi_oat_relative_patcher.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" #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 +340,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 +505,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 +547,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 +806,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 +1118,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") { @@ -1408,7 +1366,7 @@ class Dex2Oat FINAL { if (opened_dex_files_map != nullptr) { opened_dex_files_maps_.push_back(std::move(opened_dex_files_map)); for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { - dex_file_oat_filename_map_.emplace(dex_file.get(), oat_filenames_[i]); + dex_file_oat_index_map_.emplace(dex_file.get(), i); opened_dex_files_.push_back(std::move(dex_file)); } } else { @@ -1557,13 +1515,12 @@ class Dex2Oat FINAL { IsBootImage(), image_classes_.release(), compiled_classes_.release(), - nullptr, + /* compiled_methods */ nullptr, thread_count_, dump_stats_, dump_passes_, compiler_phases_timings_.get(), swap_fd_, - &dex_file_oat_filename_map_, profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); driver_->CompileAll(class_loader_, dex_files_, timings_); @@ -1667,7 +1624,7 @@ class Dex2Oat FINAL { IsAppImage(), image_storage_mode_, oat_filenames_, - dex_file_oat_filename_map_)); + dex_file_oat_index_map_)); // We need to prepare method offsets in the image address space for direct method patching. TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_); @@ -1677,21 +1634,39 @@ class Dex2Oat FINAL { } } + linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get()); { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { - std::unique_ptr<File>& oat_file = oat_files_[i]; std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i]; std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i]; std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i]; - oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files); + oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files, &patcher); - // We need to mirror the layout of the ELF file in the compressed debug-info. - // Therefore we need to propagate the sizes of at least those sections. size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); size_t text_size = oat_writer->GetSize() - rodata_size; - elf_writer->PrepareDebugInfo(rodata_size, text_size, oat_writer->GetMethodDebugInfo()); + elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer->GetBssSize()); + + if (IsImage()) { + // Update oat layout. + DCHECK(image_writer_ != nullptr); + DCHECK_LT(i, oat_filenames_.size()); + image_writer_->UpdateOatFileLayout(i, + elf_writer->GetLoadedSize(), + oat_writer->GetOatDataOffset(), + oat_writer->GetSize()); + } + } + + for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { + std::unique_ptr<File>& oat_file = oat_files_[i]; + std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i]; + std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i]; + + // We need to mirror the layout of the ELF file in the compressed debug-info. + // Therefore PrepareDebugInfo() relies on the SetLoadedSectionSizes() call further above. + elf_writer->PrepareDebugInfo(oat_writer->GetMethodDebugInfo()); OutputStream*& rodata = rodata_[i]; DCHECK(rodata != nullptr); @@ -1717,7 +1692,13 @@ class Dex2Oat FINAL { return false; } - elf_writer->SetBssSize(oat_writer->GetBssSize()); + if (IsImage()) { + // Update oat header information. + DCHECK(image_writer_ != nullptr); + DCHECK_LT(i, oat_filenames_.size()); + image_writer_->UpdateOatFileHeader(i, oat_writer->GetOatHeader()); + } + elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations()); @@ -1731,19 +1712,10 @@ class Dex2Oat FINAL { if (oat_files_[i] != nullptr) { if (oat_files_[i]->Flush() != 0) { PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i]; - oat_files_[i]->Erase(); return false; } } - if (IsImage()) { - // Update oat estimates. - DCHECK(image_writer_ != nullptr); - DCHECK_LT(i, oat_filenames_.size()); - - image_writer_->UpdateOatFile(oat_file.get(), oat_filenames_[i]); - } - VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i]; oat_writer.reset(); @@ -1865,33 +1837,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: @@ -2226,22 +2209,21 @@ class Dex2Oat FINAL { } if (!image_writer_->Write(app_image_fd_, image_filenames_, - oat_fd_, - oat_filenames_, - oat_location_)) { + oat_filenames_)) { LOG(ERROR) << "Failure during image file creation"; return false; } // We need the OatDataBegin entries. - std::map<const char*, uintptr_t> oat_data_begins; - for (const char* oat_filename : oat_filenames_) { - oat_data_begins.emplace(oat_filename, image_writer_->GetOatDataBegin(oat_filename)); + dchecked_vector<uintptr_t> oat_data_begins; + for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { + oat_data_begins.push_back(image_writer_->GetOatDataBegin(i)); } // Destroy ImageWriter before doing FixupElf. image_writer_.reset(); - for (const char* oat_filename : oat_filenames_) { + for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) { + const char* oat_filename = oat_filenames_[i]; // Do not fix up the ELF file if we are --compile-pic or compiling the app image if (!compiler_options_->GetCompilePic() && IsBootImage()) { std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename)); @@ -2250,9 +2232,7 @@ class Dex2Oat FINAL { return false; } - uintptr_t oat_data_begin = oat_data_begins.find(oat_filename)->second; - - if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) { + if (!ElfWriter::Fixup(oat_file.get(), oat_data_begins[i])) { oat_file->Erase(); LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath(); return false; @@ -2460,15 +2440,13 @@ 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_; std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_; - std::unordered_map<const DexFile*, const char*> dex_file_oat_filename_map_; + std::unordered_map<const DexFile*, size_t> dex_file_oat_index_map_; // Backing storage. std::vector<std::string> char_backing_storage_; @@ -2584,16 +2562,20 @@ static int dex2oat(int argc, char** argv) { TimingLogger timings("compiler", false, false); - Dex2Oat dex2oat(&timings); + // Allocate `dex2oat` on the heap instead of on the stack, as Clang + // might produce a stack frame too large for this function or for + // functions inlining it (such as main), that would not fit the + // requirements of the `-Wframe-larger-than` option. + std::unique_ptr<Dex2Oat> dex2oat = MakeUnique<Dex2Oat>(&timings); // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. - dex2oat.ParseArgs(argc, argv); + dex2oat->ParseArgs(argc, 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.ShouldCompileBasedOnProfiles()) { + if (dex2oat->UseProfileGuidedCompilation()) { + if (dex2oat->LoadProfile()) { + if (!dex2oat->ShouldCompileBasedOnProfiles()) { LOG(INFO) << "Skipped compilation because of insignificant profile delta"; return EXIT_SUCCESS; } @@ -2604,7 +2586,7 @@ static int dex2oat(int argc, char** argv) { } // Check early that the result of compilation can be written - if (!dex2oat.OpenFile()) { + if (!dex2oat->OpenFile()) { return EXIT_FAILURE; } @@ -2614,25 +2596,25 @@ static int dex2oat(int argc, char** argv) { // 3) Compiling with --host // 4) Compiling on the host (not a target build) // Otherwise, print a stripped command line. - if (kIsDebugBuild || dex2oat.IsBootImage() || dex2oat.IsHost() || !kIsTargetBuild) { + if (kIsDebugBuild || dex2oat->IsBootImage() || dex2oat->IsHost() || !kIsTargetBuild) { LOG(INFO) << CommandLine(); } else { LOG(INFO) << StrippedCommandLine(); } - if (!dex2oat.Setup()) { - dex2oat.EraseOatFiles(); + if (!dex2oat->Setup()) { + dex2oat->EraseOatFiles(); return EXIT_FAILURE; } bool result; - if (dex2oat.IsImage()) { - result = CompileImage(dex2oat); + if (dex2oat->IsImage()) { + result = CompileImage(*dex2oat); } else { - result = CompileApp(dex2oat); + result = CompileApp(*dex2oat); } - dex2oat.Shutdown(); + dex2oat->Shutdown(); return result; } } // namespace art diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index af08fc4e93..e30b968a96 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -145,7 +145,9 @@ class OatSymbolizer FINAL { bss->WriteNoBitsSection(oat_file_->BssSize()); } - builder_->WriteDynamicSection(elf_file->GetPath()); + builder_->PrepareDynamicSection( + elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize()); + builder_->WriteDynamicSection(); Walk(&art::OatSymbolizer::RegisterForDedup); 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/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index 1096af0131..ae01bd5d18 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -100,6 +100,7 @@ enum FRegister { F29 = 29, F30 = 30, F31 = 31, + FTMP = F8, // scratch register kNumberOfFRegisters = 32, kNoFRegister = -1, }; diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index b027c955bf..81fae72b44 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -101,6 +101,7 @@ enum FpuRegister { F29 = 29, F30 = 30, F31 = 31, + FTMP = F8, // scratch register kNumberOfFpuRegisters = 32, kNoFpuRegister = -1, }; diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index d4b873e441..d5807e27b5 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -175,12 +175,16 @@ class StubTest : public CommonRuntimeTest { #elif defined(__aarch64__) __asm__ __volatile__( // Spill x0-x7 which we say we don't clobber. May contain args. - "sub sp, sp, #64\n\t" - ".cfi_adjust_cfa_offset 64\n\t" + "sub sp, sp, #80\n\t" + ".cfi_adjust_cfa_offset 80\n\t" "stp x0, x1, [sp]\n\t" "stp x2, x3, [sp, #16]\n\t" "stp x4, x5, [sp, #32]\n\t" "stp x6, x7, [sp, #48]\n\t" + // To be extra defensive, store x20. We do this because some of the stubs might make a + // transition into the runtime via the blr instruction below and *not* save x20. + "str x20, [sp, #64]\n\t" + // 8 byte buffer "sub sp, sp, #16\n\t" // Reserve stack space, 16B aligned ".cfi_adjust_cfa_offset 16\n\t" @@ -279,8 +283,9 @@ class StubTest : public CommonRuntimeTest { "ldp x2, x3, [sp, #16]\n\t" "ldp x4, x5, [sp, #32]\n\t" "ldp x6, x7, [sp, #48]\n\t" - "add sp, sp, #64\n\t" // Free stack space, now sp as on entry - ".cfi_adjust_cfa_offset -64\n\t" + "ldr x20, [sp, #64]\n\t" + "add sp, sp, #80\n\t" // Free stack space, now sp as on entry + ".cfi_adjust_cfa_offset -80\n\t" "str x9, %[fpr_result]\n\t" // Store the FPR comparison result "mov %[result], x8\n\t" // Store the call result @@ -298,13 +303,17 @@ class StubTest : public CommonRuntimeTest { // Use the result from r0 : [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self), [referrer] "r"(referrer), [hidden] "r"(hidden), [fpr_result] "m" (fpr_result) - : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20", + // Leave one register unclobbered, which is needed for compiling with + // -fstack-protector-strong. According to AAPCS64 registers x9-x15 are caller-saved, + // which means we should unclobber one of the callee-saved registers that are unused. + // Here we use x20. + : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x30", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31", - "memory"); // clobber. + "memory"); #elif defined(__mips__) && !defined(__LP64__) __asm__ __volatile__ ( // Spill a0-a3 and t0-t7 which we say we don't clobber. May contain args. 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/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/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.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..b443c69718 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) || (kRuntimeISA == kX86)) { + 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..b05360b6ae 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -163,13 +163,26 @@ unspecified registers or condition codes. #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* - * - * 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. + * Profile branch. rINST should contain the offset. %eax is scratch. */ -#define MTERP_SUSPEND 0 +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement + RESTORE_IBASE +#endif +.endm /* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must @@ -189,13 +202,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 +224,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 +792,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 +811,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 +830,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 +849,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 +872,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 +895,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 +924,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 +964,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 +990,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 +1015,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 +1040,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 +1058,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 @@ -1068,17 +1089,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * double to get a byte offset. */ /* goto +AA */ - movsbl rINSTbl, %eax # eax <- ssssssAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movsbl rINSTbl, rINST # rINST <- ssssssAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1092,17 +1108,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * double to get a byte offset. */ /* goto/16 +AAAA */ - movswl 2(rPC), %eax # eax <- ssssAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movswl 2(rPC), rINST # rINST <- ssssAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1121,17 +1132,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop * offset to byte offset. */ /* goto/32 +AAAAAAAA */ - movl 2(rPC), %eax # eax <- AAAAAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movl 2(rPC), rINST # rINST <- AAAAAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT /* ------------------------------ */ @@ -1154,17 +1160,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData call SYMBOL(MterpDoPackedSwitch) - addl %eax, %eax - leal (rPC, %eax), rPC + movl %eax, rINST + MTERP_PROFILE_BRANCH + addl rINST, rINST + leal (rPC, rINST), rPC FETCH_INST REFRESH_IBASE - jg 1f -#if MTERP_SUSPEND - # REFRESH_IBASE - we did it above. -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT /* ------------------------------ */ @@ -1188,17 +1190,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData call SYMBOL(MterpDoSparseSwitch) - addl %eax, %eax - leal (rPC, %eax), rPC + movl %eax, rINST + MTERP_PROFILE_BRANCH + addl rINST, rINST + leal (rPC, rINST), rPC FETCH_INST REFRESH_IBASE - jg 1f -#if MTERP_SUSPEND - # REFRESH_IBASE - we did it above. -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT @@ -1416,20 +1414,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jne 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1451,20 +1444,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST je 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1486,20 +1474,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jge 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1521,20 +1504,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jl 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1556,20 +1534,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jle 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1591,20 +1564,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop GET_VREG %eax, %ecx # eax <- vA sarl $4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $2, %eax # assume not taken + movl $2, rINST jg 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1622,20 +1590,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jne 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1653,20 +1616,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST je 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1684,20 +1642,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jge 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1715,20 +1668,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jl 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1746,20 +1694,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jle 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1777,20 +1720,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop */ /* if-cmp vAA, +BBBB */ cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $2, %eax # assume branch not taken + movl $2, rINST jg 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT @@ -1923,7 +1861,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 +2028,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 +2159,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 +2197,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 +2223,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 +2258,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 +2293,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 +2328,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 +2363,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 +2399,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 +2423,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 +2441,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 +2471,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 +2502,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 +2533,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 +2564,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 +2590,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 +2623,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 +2647,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 +2679,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 +2711,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 +2743,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 +2775,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 +2807,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 +2832,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 +2850,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 +2877,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 +2905,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 +2933,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 +2961,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 +2987,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 +3020,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 +3053,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 +3079,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 +3106,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 +3153,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 +3179,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 +3205,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 +3231,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 +3257,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 +3985,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 +4039,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 +4723,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 +5452,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 +5659,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 +5923,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 +5975,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 +6000,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 +6026,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 @@ -12810,7 +12748,6 @@ SYMBOL(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 @@ -12912,7 +12849,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 @@ -12941,6 +12878,21 @@ MterpCheckSuspendAndContinue: GOTO_NEXT /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpLogOSR) +#endif + movl $1, %eax + jmp MterpDone + +/* * Bail out to reference interpreter. */ MterpFallback: diff --git a/runtime/interpreter/mterp/x86/bincmp.S b/runtime/interpreter/mterp/x86/bincmp.S index 27cf6ea6d4..c72a5cf9d4 100644 --- a/runtime/interpreter/mterp/x86/bincmp.S +++ b/runtime/interpreter/mterp/x86/bincmp.S @@ -11,18 +11,13 @@ GET_VREG %eax, %ecx # eax <- vA sarl $$4, rINST # rINST <- B cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB) - movl $$2, %eax # assume not taken + movl $$2, rINST j${revcmp} 1f - movswl 2(rPC),%eax # Get signed branch offset + movswl 2(rPC), rINST # Get signed branch offset 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT 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..c67491e577 100644 --- a/runtime/interpreter/mterp/x86/footer.S +++ b/runtime/interpreter/mterp/x86/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 @@ -114,7 +113,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 @@ -143,6 +142,21 @@ MterpCheckSuspendAndContinue: GOTO_NEXT /* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpLogOSR) +#endif + movl $$1, %eax + jmp MterpDone + +/* * Bail out to reference interpreter. */ MterpFallback: diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 0977b901e2..6bddaf9344 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -156,13 +156,26 @@ unspecified registers or condition codes. #define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET) #define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET) +#define MTERP_PROFILE_BRANCHES 1 +#define MTERP_LOGGING 0 + /* - * - * 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. + * Profile branch. rINST should contain the offset. %eax is scratch. */ -#define MTERP_SUSPEND 0 +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rINST, OUT_ARG2(%esp) + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement + RESTORE_IBASE +#endif +.endm /* * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must @@ -182,13 +195,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 +217,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_goto.S b/runtime/interpreter/mterp/x86/op_goto.S index 411399d3ad..9a87361c8e 100644 --- a/runtime/interpreter/mterp/x86/op_goto.S +++ b/runtime/interpreter/mterp/x86/op_goto.S @@ -5,15 +5,10 @@ * double to get a byte offset. */ /* goto +AA */ - movsbl rINSTbl, %eax # eax <- ssssssAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movsbl rINSTbl, rINST # rINST <- ssssssAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_goto_16.S b/runtime/interpreter/mterp/x86/op_goto_16.S index 4f04f9e479..a25c31b2d0 100644 --- a/runtime/interpreter/mterp/x86/op_goto_16.S +++ b/runtime/interpreter/mterp/x86/op_goto_16.S @@ -5,15 +5,10 @@ * double to get a byte offset. */ /* goto/16 +AAAA */ - movswl 2(rPC), %eax # eax <- ssssAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movswl 2(rPC), rINST # rINST <- ssssAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_goto_32.S b/runtime/interpreter/mterp/x86/op_goto_32.S index 48f6e5afd7..159128be1c 100644 --- a/runtime/interpreter/mterp/x86/op_goto_32.S +++ b/runtime/interpreter/mterp/x86/op_goto_32.S @@ -10,15 +10,10 @@ * offset to byte offset. */ /* goto/32 +AAAAAAAA */ - movl 2(rPC), %eax # eax <- AAAAAAAA - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + movl 2(rPC), rINST # rINST <- AAAAAAAA + MTERP_PROFILE_BRANCH + addl rINST, rINST # rINST <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 1f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT 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_packed_switch.S b/runtime/interpreter/mterp/x86/op_packed_switch.S index 230b58e02b..e33cf75499 100644 --- a/runtime/interpreter/mterp/x86/op_packed_switch.S +++ b/runtime/interpreter/mterp/x86/op_packed_switch.S @@ -15,15 +15,11 @@ movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData call SYMBOL($func) - addl %eax, %eax - leal (rPC, %eax), rPC + movl %eax, rINST + MTERP_PROFILE_BRANCH + addl rINST, rINST + leal (rPC, rINST), rPC FETCH_INST REFRESH_IBASE - jg 1f -#if MTERP_SUSPEND - # REFRESH_IBASE - we did it above. -#else - jmp MterpCheckSuspendAndContinue -#endif -1: + jle MterpCheckSuspendAndContinue GOTO_NEXT 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/mterp/x86/zcmp.S b/runtime/interpreter/mterp/x86/zcmp.S index 5ce4f0f6a7..0f28d1acd8 100644 --- a/runtime/interpreter/mterp/x86/zcmp.S +++ b/runtime/interpreter/mterp/x86/zcmp.S @@ -7,18 +7,13 @@ */ /* if-cmp vAA, +BBBB */ cmpl $$0, VREG_ADDRESS(rINST) # compare (vA, 0) - movl $$2, %eax # assume branch not taken + movl $$2, rINST j${revcmp} 1f - movswl 2(rPC),%eax # fetch signed displacement + movswl 2(rPC), rINST # fetch signed displacement 1: - addl %eax, %eax # eax <- AA * 2 - leal (rPC, %eax), rPC + MTERP_PROFILE_BRANCH + addl rINST, rINST # eax <- AA * 2 + leal (rPC, rINST), rPC FETCH_INST - jg 2f # AA * 2 > 0 => no suspend check -#if MTERP_SUSPEND - REFRESH_IBASE -#else - jmp MterpCheckSuspendAndContinue -#endif -2: + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check GOTO_NEXT 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 c6ec5b083a..1a28733efd 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; } 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 74ff741d93..478b164597 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -123,9 +123,11 @@ JitCodeCache::JitCodeCache(MemMap* code_map, current_capacity_(initial_code_capacity + initial_data_capacity), code_end_(initial_code_capacity), data_end_(initial_data_capacity), - has_done_one_collection_(false), + has_done_full_collection_(false), last_update_time_ns_(0), garbage_collect_code_(garbage_collect_code), + used_memory_for_data_(0), + used_memory_for_code_(0), number_of_compilations_(0) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); @@ -232,25 +234,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) { @@ -281,7 +278,7 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { 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; @@ -307,19 +304,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); @@ -376,9 +372,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() { @@ -387,9 +381,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() { @@ -399,7 +391,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) { @@ -410,7 +402,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) { @@ -419,7 +411,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; @@ -534,8 +526,56 @@ bool JitCodeCache::IncreaseCodeCacheCapacity() { return true; } +void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) { + Barrier barrier(0); + size_t threads_running_checkpoint = 0; + MarkCodeClosure closure(this, &barrier); + threads_running_checkpoint = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); + // Now that we have run our checkpoint, move to a suspended state and wait + // for other threads to run the checkpoint. + ScopedThreadSuspension sts(self, kSuspended); + if (threads_running_checkpoint != 0) { + barrier.Increment(self, threads_running_checkpoint); + } +} + +void JitCodeCache::RemoveUnusedCode(Thread* self) { + // Clear the osr map, chances are most of the code in it is now dead. + { + MutexLock mu(self, lock_); + osr_code_map_.clear(); + } + + // Run a checkpoint on all threads to mark the JIT compiled code they are running. + MarkCompiledCodeOnThreadStacks(self); + + // Iterate over all compiled code and remove entries that are not marked and not + // the entrypoint of their corresponding ArtMethod. + { + MutexLock mu(self, lock_); + ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + const void* code_ptr = it->first; + ArtMethod* method = it->second; + uintptr_t allocation = FromCodeToAllocation(code_ptr); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if ((method->GetEntryPointFromQuickCompiledCode() != method_header->GetEntryPoint()) && + !GetLiveBitmap()->Test(allocation)) { + FreeCode(code_ptr, method); + it = method_code_map_.erase(it); + } else { + ++it; + } + } + } +} + void JitCodeCache::GarbageCollectCache(Thread* self) { - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (!garbage_collect_code_) { + MutexLock mu(self, lock_); + IncreaseCodeCacheCapacity(); + return; + } // Wait for an existing collection, or let everyone know we are starting one. { @@ -544,42 +584,75 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { if (WaitForPotentialCollectionToComplete(self)) { return; } else { + live_bitmap_.reset(CodeCacheBitmap::Create( + "code-cache-bitmap", + reinterpret_cast<uintptr_t>(code_map_->Begin()), + reinterpret_cast<uintptr_t>(code_map_->Begin() + current_capacity_ / 2))); collection_in_progress_ = true; } } - // Check if we just need to grow the capacity. If we don't, allocate the bitmap while - // we hold the lock. + // Check if we want to do a full collection. + bool do_full_collection = true; { MutexLock mu(self, lock_); - if (!garbage_collect_code_) { + if (current_capacity_ == max_capacity_) { + // Always do a full collection when the code cache is full. + do_full_collection = true; + } else if (current_capacity_ < kReservedCapacity) { + // Do a partial collection until we hit the reserved capacity limit. + do_full_collection = false; + } else if (has_done_full_collection_) { + // Do a partial collection if we have done a full collection in the last + // collection round. + do_full_collection = false; + } + } + + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "Do " + << (do_full_collection ? "full" : "partial") + << " code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); + } + + if (do_full_collection) { + DoFullCollection(self); + } else { + RemoveUnusedCode(self); + } + + { + MutexLock mu(self, lock_); + if (!do_full_collection) { + has_done_full_collection_ = false; IncreaseCodeCacheCapacity(); - NotifyCollectionDone(self); - return; - } else if (has_done_one_collection_ && IncreaseCodeCacheCapacity()) { - has_done_one_collection_ = false; - NotifyCollectionDone(self); - return; } else { - live_bitmap_.reset(CodeCacheBitmap::Create( - "code-cache-bitmap", - reinterpret_cast<uintptr_t>(code_map_->Begin()), - reinterpret_cast<uintptr_t>(code_map_->Begin() + current_capacity_ / 2))); + has_done_full_collection_ = true; } + live_bitmap_.reset(nullptr); + NotifyCollectionDone(self); } if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "Clearing code cache, code=" + LOG(INFO) << "After code cache collection, code=" << PrettySize(CodeCacheSize()) << ", data=" << PrettySize(DataCacheSize()); } - // Walk over all compiled methods and set the entry points of these - // methods to interpreter. +} + +void JitCodeCache::DoFullCollection(Thread* self) { + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); { MutexLock mu(self, lock_); + // Walk over all compiled methods and set the entry points of these + // methods to interpreter. for (auto& it : method_code_map_) { instrumentation->UpdateMethodsCode(it.second, GetQuickToInterpreterBridge()); } + + // Clear the profiling info of methods that are not being compiled. for (ProfilingInfo* info : profiling_infos_) { if (!info->IsMethodBeingCompiled()) { info->GetMethod()->SetProfilingInfo(nullptr); @@ -592,19 +665,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } // Run a checkpoint on all threads to mark the JIT compiled code they are running. - { - Barrier barrier(0); - size_t threads_running_checkpoint = 0; - MarkCodeClosure closure(this, &barrier); - threads_running_checkpoint = - Runtime::Current()->GetThreadList()->RunCheckpoint(&closure); - // Now that we have run our checkpoint, move to a suspended state and wait - // for other threads to run the checkpoint. - ScopedThreadSuspension sts(self, kSuspended); - if (threads_running_checkpoint != 0) { - barrier.Increment(self, threads_running_checkpoint); - } - } + MarkCompiledCodeOnThreadStacks(self); { MutexLock mu(self, lock_); @@ -628,27 +689,16 @@ 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; }); profiling_infos_.erase(profiling_kept_end, profiling_infos_.end()); - - live_bitmap_.reset(nullptr); - has_done_one_collection_ = true; - NotifyCollectionDone(self); - } - - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "After clearing code cache, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); } } @@ -718,7 +768,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; } @@ -809,5 +859,32 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, } } +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 71f5cda0c8..e5b8e6ca17 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. @@ -231,6 +234,18 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); + void DoFullCollection(Thread* self) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + + void RemoveUnusedCode(Thread* self) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + + void MarkCompiledCodeOnThreadStacks(Thread* self) + REQUIRES(!lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -266,8 +281,8 @@ class JitCodeCache { // The current footprint in bytes of the data portion of the code cache. size_t data_end_ GUARDED_BY(lock_); - // Whether a collection has already been done on the current capacity. - bool has_done_one_collection_ GUARDED_BY(lock_); + // Whether a full collection has already been done on the current capacity. + bool has_done_full_collection_ GUARDED_BY(lock_); // Last time the the code_cache was updated. // It is atomic to avoid locking when reading it. @@ -276,6 +291,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/mem_map.cc b/runtime/mem_map.cc index c908b3920a..11156c6229 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -230,17 +230,16 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte PLOG(WARNING) << StringPrintf("munmap(%p, %zd) failed", actual_ptr, byte_count); } - // We call this here so that we can try and generate a full error - // message with the overlapping mapping. There's no guarantee that - // that there will be an overlap though, since - // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is - // true, even if there is no overlap - // - There might have been an overlap at the point of mmap, but the - // overlapping region has since been unmapped. - std::string error_detail; - CheckNonOverlapping(expected, limit, &error_detail); - if (error_msg != nullptr) { + // We call this here so that we can try and generate a full error + // message with the overlapping mapping. There's no guarantee that + // that there will be an overlap though, since + // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is + // true, even if there is no overlap + // - There might have been an overlap at the point of mmap, but the + // overlapping region has since been unmapped. + std::string error_detail; + CheckNonOverlapping(expected, limit, &error_detail); std::ostringstream os; os << StringPrintf("Failed to mmap at expected address, mapped at " "0x%08" PRIxPTR " instead of 0x%08" PRIxPTR, @@ -338,11 +337,18 @@ MemMap* MemMap::MapAnonymous(const char* name, saved_errno = errno; if (actual == MAP_FAILED) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + if (error_msg != nullptr) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); - *error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. See process " - "maps in the log.", expected_ptr, page_aligned_byte_count, prot, - flags, fd.get(), strerror(saved_errno)); + *error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. " + "See process maps in the log.", + expected_ptr, + page_aligned_byte_count, + prot, + flags, + fd.get(), + strerror(saved_errno)); + } return nullptr; } std::ostringstream check_map_request_error_msg; diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc index 81c855e736..e703b78cfa 100644 --- a/runtime/mem_map_test.cc +++ b/runtime/mem_map_test.cc @@ -164,6 +164,19 @@ TEST_F(MemMapTest, MapAnonymousEmpty) { ASSERT_TRUE(error_msg.empty()); } +TEST_F(MemMapTest, MapAnonymousFailNullError) { + CommonInit(); + // Test that we don't crash with a null error_str when mapping at an invalid location. + std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousInvalid", + reinterpret_cast<uint8_t*>(kPageSize), + 0x20000, + PROT_READ | PROT_WRITE, + false, + false, + nullptr)); + ASSERT_EQ(nullptr, map.get()); +} + #ifdef __LP64__ TEST_F(MemMapTest, MapAnonymousEmpty32bit) { CommonInit(); 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 fe6a529078..6317f5e401 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -289,13 +289,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(); @@ -367,6 +372,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 +607,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); }; @@ -634,7 +641,7 @@ void QuickExceptionHandler::DeoptimizeSingleFrame() { DCHECK(deopt_method != nullptr); if (Runtime::Current()->UseJit()) { Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor( - deopt_method, handler_method_header_); + deopt_method, visitor.GetSingleFrameDeoptQuickMethodHeader()); } else { // Transfer the code to interpreter. Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( 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 cbb3e89444..8aac4ce9b4 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -564,6 +564,9 @@ class Runtime { const ArenaPool* GetArenaPool() const { return arena_pool_.get(); } + + void ReclaimArenaPoolMemory(); + LinearAlloc* GetLinearAlloc() { return linear_alloc_.get(); } diff --git a/runtime/safe_map.h b/runtime/safe_map.h index a8b48ee4dc..0e5b503e26 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -99,16 +99,16 @@ class SafeMap { } // Used to insert a new mapping at a known position for better performance. - iterator PutBefore(iterator pos, const K& k, const V& v) { + iterator PutBefore(const_iterator pos, const K& k, const V& v) { // Check that we're using the correct position and the key is not in the map. DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first)); - DCHECK(pos == map_.begin() || map_.key_comp()((--iterator(pos))->first, k)); + DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k)); return map_.emplace_hint(pos, k, v); } - iterator PutBefore(iterator pos, const K& k, V&& v) { + iterator PutBefore(const_iterator pos, const K& k, V&& v) { // Check that we're using the correct position and the key is not in the map. DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first)); - DCHECK(pos == map_.begin() || map_.key_comp()((--iterator(pos))->first, k)); + DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k)); return map_.emplace_hint(pos, k, std::move(v)); } 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 79e4da19c8..83ac0b870e 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -292,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); @@ -348,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/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/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index af25d9bc54..5b3fa14076 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,9 @@ public class Main { Assert.assertEquals(Math.round(-2.5f), -2); Assert.assertEquals(Math.round(-2.9f), -3); Assert.assertEquals(Math.round(-3.0f), -3); + // 0.4999999701976776123046875 + Assert.assertEquals(Math.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f); + 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); @@ -1056,6 +1060,8 @@ public class Main { Assert.assertEquals(StrictMath.round(-2.5f), -2); Assert.assertEquals(StrictMath.round(-2.9f), -3); Assert.assertEquals(StrictMath.round(-3.0f), -3); + // 0.4999999701976776123046875 + Assert.assertEquals(StrictMath.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f); Assert.assertEquals(StrictMath.round(Float.NaN), (int)+0.0f); Assert.assertEquals(StrictMath.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE); Assert.assertEquals(StrictMath.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE); 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/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/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index deff279f77..8633745a8a 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -471,7 +471,7 @@ public class Main { // /// CHECK-START: void Main.linearTriangularOnTwoArrayLengths(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 private static void linearTriangularOnTwoArrayLengths(int n) { int[] a = new int[n]; for (int i = 0; i < a.length; i++) { @@ -513,7 +513,7 @@ public class Main { // /// CHECK-START: void Main.linearTriangularOnParameter(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 private static void linearTriangularOnParameter(int n) { int[] a = new int[n]; for (int i = 0; i < n; i++) { @@ -528,22 +528,22 @@ public class Main { } } - /// CHECK-START: void Main.linearTriangularVariations(int) BCE (before) + /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (before) /// CHECK-DAG: BoundsCheck /// CHECK-DAG: BoundsCheck /// CHECK-DAG: BoundsCheck /// CHECK-DAG: BoundsCheck // - /// CHECK-START: void Main.linearTriangularVariations(int) BCE (after) + /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static void linearTriangularVariations(int n) { + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 + private static void linearTriangularVariationsInnerStrict(int n) { int[] a = new int[n]; for (int i = 0; i < n; i++) { for (int j = 0; j < i; j++) { a[j] += 1; } - for (int j = i - 1; j >= 0; j--) { + for (int j = i - 1; j > -1; j--) { a[j] += 1; } for (int j = i; j < n; j++) { @@ -556,6 +556,34 @@ public class Main { verifyTriangular(a); } + /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (after) + /// CHECK-NOT: BoundsCheck + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 + private static void linearTriangularVariationsInnerNonStrict(int n) { + int[] a = new int[n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j <= i - 1; j++) { + a[j] += 1; + } + for (int j = i - 1; j >= 0; j--) { + a[j] += 1; + } + for (int j = i; j <= n - 1; j++) { + a[j] += 1; + } + for (int j = n - 1; j >= i; j--) { + a[j] += 1; + } + } + verifyTriangular(a); + } + // Verifier for triangular loops. private static void verifyTriangular(int[] a, int[] b, int m, int n) { expectEquals(n, a.length); @@ -577,596 +605,6 @@ public class Main { } } - /// CHECK-START: void Main.bubble(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.bubble(int[]) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static void bubble(int[] a) { - for (int i = a.length; --i >= 0;) { - for (int j = 0; j < i; j++) { - if (a[j] > a[j+1]) { - int tmp = a[j]; - a[j] = a[j+1]; - a[j+1] = tmp; - } - } - } - } - - /// CHECK-START: int Main.periodicIdiom(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicIdiom(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicIdiom(int tc) { - int[] x = { 1, 3 }; - // Loop with periodic sequence (0, 1). - int k = 0; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k]; - k = 1 - k; - } - return result; - } - - /// CHECK-START: int Main.periodicSequence2(int) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicSequence2(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicSequence2(int tc) { - int[] x = { 1, 3 }; - // Loop with periodic sequence (0, 1). - int k = 0; - int l = 1; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k]; - int t = l; - l = k; - k = t; - } - return result; - } - - /// CHECK-START: int Main.periodicSequence4(int) BCE (before) - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.periodicSequence4(int) BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int periodicSequence4(int tc) { - int[] x = { 1, 3, 5, 7 }; - // Loop with periodic sequence (0, 1, 2, 3). - int k = 0; - int l = 1; - int m = 2; - int n = 3; - int result = 0; - for (int i = 0; i < tc; i++) { - result += x[k] + x[l] + x[m] + x[n]; // all used at once - int t = n; - n = k; - k = l; - l = m; - m = t; - } - return result; - } - - /// CHECK-START: int Main.justRightUp1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp1() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightUp2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp2() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp2() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) { - result += x[i - Integer.MAX_VALUE + 10]; - } - return result; - } - - /// CHECK-START: int Main.justRightUp3() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightUp3() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightUp3() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justOOBUp() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBUp() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBUp() BCE (after) - /// CHECK-NOT: Deoptimize - private static int justOOBUp() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - // Infinite loop! - for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown1() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown2() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown2() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) { - result += x[Integer.MAX_VALUE + i]; - } - return result; - } - - /// CHECK-START: int Main.justRightDown3() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justRightDown3() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int justRightDown3() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.justOOBDown() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBDown() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int Main.justOOBDown() BCE (after) - /// CHECK-NOT: Deoptimize - private static int justOOBDown() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int result = 0; - // Infinite loop! - for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: void Main.lowerOOB(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - private static void lowerOOB(int[] x) { - // OOB! - for (int i = -1; i < x.length; i++) { - sResult += x[i]; - } - } - - /// CHECK-START: void Main.upperOOB(int[]) BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.upperOOB(int[]) BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.upperOOB(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - private static void upperOOB(int[] x) { - // OOB! - for (int i = 0; i <= x.length; i++) { - sResult += x[i]; - } - } - - /// CHECK-START: void Main.doWhileUpOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileUpOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileUpOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void doWhileUpOOB() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int i = 0; - // OOB! - do { - sResult += x[i++]; - } while (i <= x.length); - } - - /// CHECK-START: void Main.doWhileDownOOB() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileDownOOB() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: void Main.doWhileDownOOB() BCE (after) - /// CHECK-NOT: Deoptimize - private static void doWhileDownOOB() { - int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - int i = x.length - 1; - // OOB! - do { - sResult += x[i--]; - } while (-1 <= i); - } - - /// CHECK-START: int[] Main.multiply1() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply1() BCE (after) - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: Deoptimize - private static int[] multiply1() { - int[] a = new int[10]; - try { - for (int i = 0; i <= 3; i++) { - for (int j = 0; j <= 3; j++) { - // Range [0,9]: safe. - a[i * j] += 1; - } - } - } catch (Exception e) { - a[0] += 1000; - } - return a; - } - - /// CHECK-START: int[] Main.multiply2() BCE (before) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply2() BCE (after) - /// CHECK-DAG: BoundsCheck - // - /// CHECK-START: int[] Main.multiply2() BCE (after) - /// CHECK-NOT: Deoptimize - static int[] multiply2() { - int[] a = new int[10]; - try { - for (int i = -3; i <= 3; i++) { - for (int j = -3; j <= 3; j++) { - // Range [-9,9]: unsafe. - a[i * j] += 1; - } - } - } catch (Exception e) { - a[0] += 1000; - } - return a; - } - - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int linearDynamicBCE1(int[] x, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - sResult += x[i]; - } - return result; - } - - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) { - int result = 0; - for (int i = lo; i < hi; i++) { - sResult += x[offset + i]; - } - return result; - } - - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int wrapAroundDynamicBCE(int[] x) { - int w = 9; - int result = 0; - for (int i = 0; i < 10; i++) { - result += x[w]; - w = i; - } - return result; - } - - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - private static int periodicDynamicBCE(int[] x) { - int k = 0; - int result = 0; - for (int i = 0; i < 10; i++) { - result += x[k]; - k = 1 - k; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { - // This loop could be infinite for hi = max int. Since i is also used - // as subscript, however, dynamic bce can proceed. - int result = 0; - for (int i = lo; i <= hi; i++) { - result += x[i]; - } - return result; - } - - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-NOT: Deoptimize - static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { - // As above, but now the index is not used as subscript, - // and dynamic bce is not applied. - int result = 0; - for (int k = 0, i = lo; i <= hi; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK-NOT: Deoptimize - static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) { - int result = 0; - // Mix of int and long induction. - int k = 0; - for (long i = lo; i < hi; i++) { - result += x[k++]; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - // Order matters: - /// CHECK: Deoptimize loop:<<Loop:B\d+>> - // CHECK-NOT: Goto loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> - /// CHECK: Goto loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - /// CHECK-DAG: Deoptimize loop:none - static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) { - // Deliberately test array length on a before the loop so that only bounds checks - // on constant subscripts remain, making them a viable candidate for hoisting. - if (a.length == 0) { - return -1; - } - // Loop that allows BCE on x[i]. - int result = 0; - for (int i = lo; i < hi; i++) { - result += x[i]; - if ((i % 10) != 0) { - // None of the subscripts inside a conditional are removed by dynamic bce, - // making them a candidate for deoptimization based on constant indices. - // Compiler should ensure the array loads are not subsequently hoisted - // "above" the deoptimization "barrier" on the bounds. - a[0][i] = 1; - a[1][i] = 2; - a[99][i] = 3; - } - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - // For brevity, just test occurrence of at least one of each in the loop: - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-NOT: ArrayGet loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-NOT: NullCheck loop:{{B\d+}} - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-DAG: Deoptimize loop:none - static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, - boolean[] r, - byte[] s, - char[] t, - short[] u, - int[] v, - long[] w, - float[] x, - double[] y, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - // All constant index array references can be hoisted out of the loop during BCE on q[i]. - result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] + - (int) w[0] + (int) x[0] + (int) y[0]; - } - return result; - } - - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - /// CHECK-DAG: ArrayGet loop:<<Loop>> - /// CHECK-DAG: NullCheck loop:<<Loop>> - /// CHECK-DAG: ArrayLength loop:<<Loop>> - /// CHECK-DAG: BoundsCheck loop:<<Loop>> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> - /// CHECK-DAG: Deoptimize loop:none - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-NOT: ArrayLength loop:{{B\d+}} - /// CHECK-NOT: BoundsCheck loop:{{B\d+}} - static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) { - int result = 0; - for (int i = lo; i < hi; i++) { - // Similar to above, but now implicit call to intValue() may prevent hoisting - // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i]. - result += q[i] + z[0]; - } - return result; - } - - // - // Verifier. - // - public static void main(String[] args) { int[] empty = { }; int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -1239,191 +677,8 @@ public class Main { linearTriangularOnTwoArrayLengths(10); linearTriangularOnOneArrayLength(10); linearTriangularOnParameter(10); - linearTriangularVariations(10); - - // Sorting. - int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 }; - bubble(sort); - for (int i = 0; i < 10; i++) { - expectEquals(sort[i], x[i]); - } - - // Periodic adds (1, 3), one at the time. - expectEquals(0, periodicIdiom(-1)); - for (int tc = 0; tc < 32; tc++) { - int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) - expected += 1; - expectEquals(expected, periodicIdiom(tc)); - } - - // Periodic adds (1, 3), one at the time. - expectEquals(0, periodicSequence2(-1)); - for (int tc = 0; tc < 32; tc++) { - int expected = (tc >> 1) << 2; - if ((tc & 1) != 0) - expected += 1; - expectEquals(expected, periodicSequence2(tc)); - } - - // Periodic adds (1, 3, 5, 7), all at once. - expectEquals(0, periodicSequence4(-1)); - for (int tc = 0; tc < 32; tc++) { - expectEquals(tc * 16, periodicSequence4(tc)); - } - - // Large bounds. - expectEquals(55, justRightUp1()); - expectEquals(55, justRightUp2()); - expectEquals(55, justRightUp3()); - expectEquals(55, justRightDown1()); - expectEquals(55, justRightDown2()); - expectEquals(55, justRightDown3()); - sResult = 0; - try { - justOOBUp(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - sResult = 0; - try { - justOOBDown(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - - // Lower bound goes OOB. - sResult = 0; - try { - lowerOOB(x); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - - // Upper bound goes OOB. - sResult = 0; - try { - upperOOB(x); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Do while up goes OOB. - sResult = 0; - try { - doWhileUpOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Do while down goes OOB. - sResult = 0; - try { - doWhileDownOOB(); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Multiplication. - { - int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 }; - int[] a1 = multiply1(); - for (int i = 0; i < 10; i++) { - expectEquals(a1[i], e1[i]); - } - int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 }; - int[] a2 = multiply2(); - for (int i = 0; i < 10; i++) { - expectEquals(a2[i], e2[i]); - } - } - - // Dynamic BCE. - sResult = 0; - try { - linearDynamicBCE1(x, -1, x.length); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - sResult = 0; - linearDynamicBCE1(x, 0, x.length); - expectEquals(55, sResult); - sResult = 0; - try { - linearDynamicBCE1(x, 0, x.length + 1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1055, sResult); - - // Dynamic BCE with offset. - sResult = 0; - try { - linearDynamicBCE2(x, 0, x.length, -1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1000, sResult); - sResult = 0; - linearDynamicBCE2(x, 0, x.length, 0); - expectEquals(55, sResult); - sResult = 0; - try { - linearDynamicBCE2(x, 0, x.length, 1); - } catch (ArrayIndexOutOfBoundsException e) { - sResult += 1000; - } - expectEquals(1054, sResult); - - // Dynamic BCE candidates. - expectEquals(55, wrapAroundDynamicBCE(x)); - expectEquals(15, periodicDynamicBCE(x)); - expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9)); - expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9)); - expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10)); - - // Dynamic BCE combined with constant indices. - int[][] a; - a = new int[0][0]; - expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10)); - a = new int[100][10]; - expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); - for (int i = 0; i < 10; i++) { - expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]); - expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]); - expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]); - } - a = new int[2][10]; - sResult = 0; - try { - expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); - } catch (ArrayIndexOutOfBoundsException e) { - sResult = 1; - } - expectEquals(1, sResult); - expectEquals(a[0][1], 1); - expectEquals(a[1][1], 2); - - // Dynamic BCE combined with constant indices of all types. - boolean[] x1 = { true }; - byte[] x2 = { 2 }; - char[] x3 = { 3 }; - short[] x4 = { 4 }; - int[] x5 = { 5 }; - long[] x6 = { 6 }; - float[] x7 = { 7 }; - double[] x8 = { 8 }; - expectEquals(415, - dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10)); - Integer[] x9 = { 9 }; - expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10)); + linearTriangularVariationsInnerStrict(10); + linearTriangularVariationsInnerNonStrict(10); } private static void expectEquals(int expected, int result) { diff --git a/test/530-checker-loops2/expected.txt b/test/530-checker-loops2/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/530-checker-loops2/expected.txt diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt new file mode 100644 index 0000000000..f5d334d011 --- /dev/null +++ b/test/530-checker-loops2/info.txt @@ -0,0 +1 @@ +Test on loop optimizations. diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java new file mode 100644 index 0000000000..64be1a2be4 --- /dev/null +++ b/test/530-checker-loops2/src/Main.java @@ -0,0 +1,999 @@ +/* + * 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. + */ + +// +// Test on loop optimizations. +// +public class Main { + + static int sResult; + + // + // Various sequence variables used in bound checks. + // + + /// CHECK-START: void Main.bubble(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.bubble(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + // TODO: also CHECK-NOT: Deoptimize, see b/27151190 + private static void bubble(int[] a) { + for (int i = a.length; --i >= 0;) { + for (int j = 0; j < i; j++) { + if (a[j] > a[j+1]) { + int tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + } + } + } + } + + /// CHECK-START: int Main.periodicIdiom(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicIdiom(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicIdiom(int tc) { + int[] x = { 1, 3 }; + // Loop with periodic sequence (0, 1). + int k = 0; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k]; + k = 1 - k; + } + return result; + } + + /// CHECK-START: int Main.periodicSequence2(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicSequence2(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicSequence2(int tc) { + int[] x = { 1, 3 }; + // Loop with periodic sequence (0, 1). + int k = 0; + int l = 1; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k]; + int t = l; + l = k; + k = t; + } + return result; + } + + /// CHECK-START: int Main.periodicSequence4(int) BCE (before) + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.periodicSequence4(int) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int periodicSequence4(int tc) { + int[] x = { 1, 3, 5, 7 }; + // Loop with periodic sequence (0, 1, 2, 3). + int k = 0; + int l = 1; + int m = 2; + int n = 3; + int result = 0; + for (int i = 0; i < tc; i++) { + result += x[k] + x[l] + x[m] + x[n]; // all used at once + int t = n; + n = k; + k = l; + l = m; + m = t; + } + return result; + } + + /// CHECK-START: int Main.justRightUp1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp2() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) { + result += x[i - Integer.MAX_VALUE + 10]; + } + return result; + } + + /// CHECK-START: int Main.justRightUp3() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightUp3() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightUp3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBUp() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-NOT: Deoptimize + private static int justOOBUp() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown1() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown2() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown2() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) { + result += x[Integer.MAX_VALUE + i]; + } + return result; + } + + /// CHECK-START: int Main.justRightDown3() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justRightDown3() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int justRightDown3() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.justOOBDown() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-NOT: Deoptimize + private static int justOOBDown() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int result = 0; + // Infinite loop! + for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: void Main.lowerOOB(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + private static void lowerOOB(int[] x) { + // OOB! + for (int i = -1; i < x.length; i++) { + sResult += x[i]; + } + } + + /// CHECK-START: void Main.upperOOB(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.upperOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.upperOOB(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + private static void upperOOB(int[] x) { + // OOB! + for (int i = 0; i <= x.length; i++) { + sResult += x[i]; + } + } + + /// CHECK-START: void Main.doWhileUpOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileUpOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileUpOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void doWhileUpOOB() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int i = 0; + // OOB! + do { + sResult += x[i++]; + } while (i <= x.length); + } + + /// CHECK-START: void Main.doWhileDownOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileDownOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileDownOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void doWhileDownOOB() { + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + int i = x.length - 1; + // OOB! + do { + sResult += x[i--]; + } while (-1 <= i); + } + + /// CHECK-START: void Main.hiddenOOB1(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB1(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB1(int lo) { + int[] a = { 1 } ; + for (int i = lo; i <= 10; i++) { + // Dangerous loop where careless static range analysis would yield strict upper bound + // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound + // becomes really positive due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 4; j < i - 5; j++) { + sResult += a[j - 4]; + } + } + } + + /// CHECK-START: void Main.hiddenOOB2(int) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenOOB2(int) BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenOOB2(int hi) { + int[] a = { 1 } ; + for (int i = 0; i < hi; i++) { + // Dangerous loop where careless static range analysis would yield strict lower bound + // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound + // becomes really negative due to arithmetic wrap-around, causing OOB. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = 6; j > i + 5; j--) { + sResult += a[j - 6]; + } + } + } + + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after) + /// CHECK-NOT: Deoptimize + private static void hiddenInfiniteOOB() { + int[] a = { 11 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop where careless static range analysis would yield a safe upper bound + // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647; + // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB. + for (int j = -3; j <= 2147483646 * i - 3; j++) { + sResult += a[j + 3]; + } + } + } + + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-DAG: Deoptimize + // + /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after) + /// CHECK-NOT: BoundsCheck + private static void hiddenFiniteOOB() { + int[] a = { 111 } ; + for (int i = -1; i <= 0; i++) { + // Dangerous loop similar as above where the loop is now finite, but the + // loop still goes out of bounds for i = -1 due to the large upper bound. + // Dynamic BCE is feasible though, since it checks the range. + for (int j = -4; j < 2147483646 * i - 3; j++) { + sResult += a[j + 4]; + } + } + } + + /// CHECK-START: int[] Main.add() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.add() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int[] add() { + int[] a = new int[10]; + for (int i = 0; i <= 3; i++) { + for (int j = 0; j <= 6; j++) { + a[i + j] += 1; + } + } + return a; + } + + /// CHECK-START: int[] Main.multiply1() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply1() BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int[] multiply1() { + int[] a = new int[10]; + try { + for (int i = 0; i <= 3; i++) { + for (int j = 0; j <= 3; j++) { + // Range [0,9]: safe. + a[i * j] += 1; + } + } + } catch (Exception e) { + a[0] += 1000; + } + return a; + } + + /// CHECK-START: int[] Main.multiply2() BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply2() BCE (after) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply2() BCE (after) + /// CHECK-NOT: Deoptimize + static int[] multiply2() { + int[] a = new int[10]; + try { + for (int i = -3; i <= 3; i++) { + for (int j = -3; j <= 3; j++) { + // Range [-9,9]: unsafe. + a[i * j] += 1; + } + } + } catch (Exception e) { + a[0] += 1000; + } + return a; + } + + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int linearDynamicBCE1(int[] x, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + sResult += x[i]; + } + return result; + } + + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) { + int result = 0; + for (int i = lo; i < hi; i++) { + sResult += x[offset + i]; + } + return result; + } + + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int wrapAroundDynamicBCE(int[] x) { + int w = 9; + int result = 0; + for (int i = 0; i < 10; i++) { + result += x[w]; + w = i; + } + return result; + } + + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + private static int periodicDynamicBCE(int[] x) { + int k = 0; + int result = 0; + for (int i = 0; i < 10; i++) { + result += x[k]; + k = 1 - k; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:{{B\d+}} + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { + // This loop could be infinite for hi = max int. Since i is also used + // as subscript, however, dynamic bce can proceed. + int result = 0; + for (int i = lo; i <= hi; i++) { + result += x[i]; + } + return result; + } + + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-NOT: Deoptimize + static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { + // As above, but now the index is not used as subscript, + // and dynamic bce is not applied. + int result = 0; + for (int k = 0, i = lo; i <= hi; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-NOT: Deoptimize + static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) { + int result = 0; + // Mix of int and long induction. + int k = 0; + for (long i = lo; i < hi; i++) { + result += x[k++]; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>> + /// CHECK-DAG: ArrayGet loop:<<InnerLoop>> + /// CHECK-DAG: If loop:<<InnerLoop>> + /// CHECK-DAG: If loop:<<OuterLoop:B\d+>> + /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<InnerLoop:B\d+>> + /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>> + /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + // + // No additional top tests were introduced. + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: If + /// CHECK-DAG: If + /// CHECK-NOT: If + static int dynamicBCEConstantRange(int[] x) { + int result = 0; + for (int i = 2; i <= 6; i++) { + // Range analysis sees that innermost loop is finite and always taken. + for (int j = i - 2; j <= i + 2; j++) { + result += x[j]; + } + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + // Order matters: + /// CHECK: Deoptimize loop:<<Loop:B\d+>> + // CHECK-NOT: Goto loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK: Goto loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) { + // Deliberately test array length on a before the loop so that only bounds checks + // on constant subscripts remain, making them a viable candidate for hoisting. + if (a.length == 0) { + return -1; + } + // Loop that allows BCE on x[i]. + int result = 0; + for (int i = lo; i < hi; i++) { + result += x[i]; + if ((i % 10) != 0) { + // None of the subscripts inside a conditional are removed by dynamic bce, + // making them a candidate for deoptimization based on constant indices. + // Compiler should ensure the array loads are not subsequently hoisted + // "above" the deoptimization "barrier" on the bounds. + a[0][i] = 1; + a[1][i] = 2; + a[99][i] = 3; + } + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + // For brevity, just test occurrence of at least one of each in the loop: + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-NOT: ArrayGet loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, + boolean[] r, + byte[] s, + char[] t, + short[] u, + int[] v, + long[] w, + float[] x, + double[] y, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + // All constant index array references can be hoisted out of the loop during BCE on q[i]. + result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] + + (int) w[0] + (int) x[0] + (int) y[0]; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + // Similar to above, but now implicit call to intValue() may prevent hoisting + // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i]. + result += q[i] + z[0]; + } + return result; + } + + // + // Verifier. + // + + public static void main(String[] args) { + // Set to run expensive tests for correctness too. + boolean HEAVY = false; + + int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + // Sorting. + int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 }; + bubble(sort); + for (int i = 0; i < 10; i++) { + expectEquals(sort[i], x[i]); + } + + // Periodic adds (1, 3), one at the time. + expectEquals(0, periodicIdiom(-1)); + for (int tc = 0; tc < 32; tc++) { + int expected = (tc >> 1) << 2; + if ((tc & 1) != 0) + expected += 1; + expectEquals(expected, periodicIdiom(tc)); + } + + // Periodic adds (1, 3), one at the time. + expectEquals(0, periodicSequence2(-1)); + for (int tc = 0; tc < 32; tc++) { + int expected = (tc >> 1) << 2; + if ((tc & 1) != 0) + expected += 1; + expectEquals(expected, periodicSequence2(tc)); + } + + // Periodic adds (1, 3, 5, 7), all at once. + expectEquals(0, periodicSequence4(-1)); + for (int tc = 0; tc < 32; tc++) { + expectEquals(tc * 16, periodicSequence4(tc)); + } + + // Large bounds. + expectEquals(55, justRightUp1()); + expectEquals(55, justRightUp2()); + expectEquals(55, justRightUp3()); + expectEquals(55, justRightDown1()); + expectEquals(55, justRightDown2()); + expectEquals(55, justRightDown3()); + sResult = 0; + try { + justOOBUp(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + sResult = 0; + try { + justOOBDown(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + + // Lower bound goes OOB. + sResult = 0; + try { + lowerOOB(x); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + + // Upper bound goes OOB. + sResult = 0; + try { + upperOOB(x); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Do while up goes OOB. + sResult = 0; + try { + doWhileUpOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Do while down goes OOB. + sResult = 0; + try { + doWhileDownOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Hidden OOB. + sResult = 0; + try { + hiddenOOB1(10); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + sResult = 0; + try { + hiddenOOB1(-2147483648); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1001, sResult); + sResult = 0; + try { + hiddenOOB2(1); // no OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1, sResult); + if (HEAVY) { + sResult = 0; + try { + hiddenOOB2(2147483647); // OOB + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1002, sResult); + } + sResult = 0; + try { + hiddenInfiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1011, sResult); + sResult = 0; + try { + hiddenFiniteOOB(); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1111, sResult); + + // Addition. + { + int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 }; + int[] a1 = add(); + for (int i = 0; i < 10; i++) { + expectEquals(a1[i], e1[i]); + } + } + + // Multiplication. + { + int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 }; + int[] a1 = multiply1(); + for (int i = 0; i < 10; i++) { + expectEquals(a1[i], e1[i]); + } + int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 }; + int[] a2 = multiply2(); + for (int i = 0; i < 10; i++) { + expectEquals(a2[i], e2[i]); + } + } + + // Dynamic BCE. + sResult = 0; + try { + linearDynamicBCE1(x, -1, x.length); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + sResult = 0; + linearDynamicBCE1(x, 0, x.length); + expectEquals(55, sResult); + sResult = 0; + try { + linearDynamicBCE1(x, 0, x.length + 1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1055, sResult); + + // Dynamic BCE with offset. + sResult = 0; + try { + linearDynamicBCE2(x, 0, x.length, -1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1000, sResult); + sResult = 0; + linearDynamicBCE2(x, 0, x.length, 0); + expectEquals(55, sResult); + sResult = 0; + try { + linearDynamicBCE2(x, 0, x.length, 1); + } catch (ArrayIndexOutOfBoundsException e) { + sResult += 1000; + } + expectEquals(1054, sResult); + + // Dynamic BCE candidates. + expectEquals(55, wrapAroundDynamicBCE(x)); + expectEquals(15, periodicDynamicBCE(x)); + expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9)); + expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9)); + expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10)); + expectEquals(125, dynamicBCEConstantRange(x)); + + // Dynamic BCE combined with constant indices. + int[][] a; + a = new int[0][0]; + expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10)); + a = new int[100][10]; + expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); + for (int i = 0; i < 10; i++) { + expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]); + expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]); + expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]); + } + a = new int[2][10]; + sResult = 0; + try { + expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10)); + } catch (ArrayIndexOutOfBoundsException e) { + sResult = 1; + } + expectEquals(1, sResult); + expectEquals(a[0][1], 1); + expectEquals(a[1][1], 2); + + // Dynamic BCE combined with constant indices of all types. + boolean[] x1 = { true }; + byte[] x2 = { 2 }; + char[] x3 = { 3 }; + short[] x4 = { 4 }; + int[] x5 = { 5 }; + long[] x6 = { 6 }; + float[] x7 = { 7 }; + double[] x8 = { 8 }; + expectEquals(415, + dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10)); + Integer[] x9 = { 9 }; + expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10)); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java index 2d0688d57e..87a89bd9dc 100644 --- a/test/550-checker-multiply-accumulate/src/Main.java +++ b/test/550-checker-multiply-accumulate/src/Main.java @@ -1,18 +1,18 @@ /* -* 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. -*/ + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ public class Main { diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index 8d73d69db9..9c86154bd4 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -1,18 +1,18 @@ /* -* 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. -*/ + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ public class Main { diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index 41af97b3b7..2d70e111aa 100644 --- a/test/565-checker-doublenegbitwise/src/Main.java +++ b/test/565-checker-doublenegbitwise/src/Main.java @@ -1,18 +1,18 @@ /* -* 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. -*/ + * 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 { 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/tools/libcore_failures.txt b/tools/libcore_failures.txt index 44206df089..46100ae15c 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -208,13 +208,6 @@ "org.apache.harmony.tests.java.util.TimerTaskTest#test_scheduledExecutionTime"] }, { - description: "'cat -' does not work anymore", - result: EXEC_FAILED, - bug: 26395656, - modes: [device], - names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getOutputStream"] -}, -{ description: "Missing resource in classpath", result: EXEC_FAILED, modes: [device], @@ -272,5 +265,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/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 diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh new file mode 100755 index 0000000000..8dc4e27885 --- /dev/null +++ b/tools/symbolize-buildbot-crashes.sh @@ -0,0 +1,23 @@ +#!/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. + +# We push art and its dependencies to '/data/local/tmp', but the 'stack' +# script expect things to be in '/'. So we just remove the +# '/data/local/tmp' prefix. +adb logcat -d | sed 's,/data/local/tmp,,g' | development/scripts/stack + +# Always return 0 to avoid having the buildbot complain about wrong stacks. +exit 0 |