diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 5e762a3bda2353ec7537a20ccd89d47ec4c3ee4d..2294ddbd55a4497aed0e59b3c67b86e4f0314908 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -334,7 +334,6 @@ art_non_debug_cflags := \ art_debug_cflags := \ $(ART_DEBUG_OPT_FLAG) \ -DDYNAMIC_ANNOTATIONS_ENABLED=1 \ - -DVIXL_DEBUG \ -UNDEBUG art_host_non_debug_cflags := $(art_non_debug_cflags) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 22c0e8d7d012c36a413e4d420b3f7cf1fd2b3794..33242f1c5de0cf6f840cf22e6acac285e29d7245 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -258,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 \ @@ -516,7 +517,8 @@ $$(gtest_rule): $$(gtest_exe) $$(gtest_deps) valgrind-$$(gtest_rule): $$(gtest_exe) $$(gtest_deps) $(ART_VALGRIND_DEPENDENCIES) $(hide) $$(call ART_TEST_SKIP,$$@) && \ VALGRIND_LIB=$(HOST_OUT)/lib64/valgrind \ - $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 $$< && \ + $(HOST_OUT_EXECUTABLES)/valgrind --leak-check=full --error-exitcode=1 \ + --suppressions=art/test/valgrind-suppressions.txt $$< && \ $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) ART_TEST_HOST_VALGRIND_GTEST$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += valgrind-$$(gtest_rule) @@ -572,14 +574,11 @@ define define-art-gtest ifeq ($$(art_target_or_host),target) $$(eval $$(call set-target-local-clang-vars)) $$(eval $$(call set-target-local-cflags-vars,debug)) - LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixld + LOCAL_SHARED_LIBRARIES += libdl libicuuc libicui18n libnativehelper libz libcutils libvixl LOCAL_MODULE_PATH_32 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_32) 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 := @@ -613,7 +612,7 @@ test-art-target-gtest-$$(art_gtest_name): $$(ART_TEST_TARGET_GTEST_$$(art_gtest_ LOCAL_CLANG := $$(ART_HOST_CLANG) LOCAL_CFLAGS += $$(ART_HOST_CFLAGS) $$(ART_HOST_DEBUG_CFLAGS) LOCAL_ASFLAGS += $$(ART_HOST_ASFLAGS) - LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixld + LOCAL_SHARED_LIBRARIES += libicuuc-host libicui18n-host libnativehelper libziparchive-host libz-host libvixl LOCAL_LDLIBS := $(ART_HOST_LDLIBS) -lpthread -ldl LOCAL_IS_HOST_MODULE := true LOCAL_MULTILIB := both diff --git a/compiler/Android.mk b/compiler/Android.mk index 3f61e8eb1bbb3086cced373d3943bc5b39f8f3a3..11ee6dd3a1b887c948ff9f322a78abd02200ec09 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 \ @@ -141,7 +142,9 @@ LIBART_COMPILER_SRC_FILES_arm64 := \ jni/quick/arm64/calling_convention_arm64.cc \ linker/arm64/relative_patcher_arm64.cc \ optimizing/code_generator_arm64.cc \ + optimizing/instruction_simplifier_arm.cc \ optimizing/instruction_simplifier_arm64.cc \ + optimizing/instruction_simplifier_shared.cc \ optimizing/intrinsics_arm64.cc \ utils/arm64/assembler_arm64.cc \ utils/arm64/managed_register_arm64.cc \ @@ -327,9 +330,9 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT # Vixl assembly support for ARM64 targets. ifeq ($$(art_ndebug_or_debug),debug) ifeq ($$(art_static_or_shared), static) - LOCAL_WHOLESTATIC_LIBRARIES += libvixld + LOCAL_WHOLESTATIC_LIBRARIES += libvixl else - LOCAL_SHARED_LIBRARIES += libvixld + LOCAL_SHARED_LIBRARIES += libvixl endif else ifeq ($$(art_static_or_shared), static) diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index e4bfac9ee7e1a270cb9c6ea85a00e21a6100dd59..6075cd6fbe2c6731cbc739098fa7d617b143c350 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -187,23 +187,24 @@ void CommonCompilerTest::SetUp() { } } -void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) { +void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, + InstructionSet isa, + size_t number_of_threads) { compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(), kind, isa, instruction_set_features_.get(), - true, + /* boot_image */ true, GetImageClasses(), GetCompiledClasses(), GetCompiledMethods(), - 2, - true, - true, + number_of_threads, + /* 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/common_compiler_test.h b/compiler/common_compiler_test.h index 7e0fbabff8c64140a24307e08b7c3bf4822f6582..7c2c844e6f802ee97adf116f9f3fb7e0c7022f14 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -93,7 +93,7 @@ class CommonCompilerTest : public CommonRuntimeTest { const char* method_name, const char* signature) SHARED_REQUIRES(Locks::mutator_lock_); - void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa); + void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads = 2U); void ReserveImageSpace(); @@ -122,6 +122,13 @@ class CommonCompilerTest : public CommonRuntimeTest { return; \ } +// TODO: When read barrier works with all tests, get rid of this. +#define TEST_DISABLED_FOR_READ_BARRIER() \ + if (kUseReadBarrier) { \ + printf("WARNING: TEST DISABLED FOR READ BARRIER\n"); \ + return; \ + } + // TODO: When read barrier works with all compilers in use, get rid of this. #define TEST_DISABLED_FOR_READ_BARRIER_WITH_QUICK() \ if (kUseReadBarrier && GetCompilerKind() == Compiler::kQuick) { \ diff --git a/compiler/debug/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h index 58502a3f9c62fdafd94f2bf870be1cdd6aa7153d..b4a4d63f01362b33b3ee76bda5aca398991c65e0 100644 --- a/compiler/debug/dwarf/debug_line_opcode_writer.h +++ b/compiler/debug/dwarf/debug_line_opcode_writer.h @@ -36,7 +36,7 @@ class DebugLineOpCodeWriter FINAL : private Writer { public: static constexpr int kOpcodeBase = 13; - static constexpr bool kDefaultIsStmt = true; + static constexpr bool kDefaultIsStmt = false; static constexpr int kLineBase = -5; static constexpr int kLineRange = 14; @@ -81,8 +81,11 @@ class DebugLineOpCodeWriter FINAL : private Writer { this->PushUleb128(column); } - void NegateStmt() { - this->PushUint8(DW_LNS_negate_stmt); + void SetIsStmt(bool is_stmt) { + if (is_stmt_ != is_stmt) { + this->PushUint8(DW_LNS_negate_stmt); + is_stmt_ = is_stmt; + } } void SetBasicBlock() { @@ -112,6 +115,7 @@ class DebugLineOpCodeWriter FINAL : private Writer { current_address_ = 0; current_file_ = 1; current_line_ = 1; + is_stmt_ = kDefaultIsStmt; } // Uncoditionally set address using the long encoding. @@ -227,7 +231,8 @@ class DebugLineOpCodeWriter FINAL : private Writer { code_factor_bits_(codeFactorBits), current_address_(0), current_file_(1), - current_line_(1) { + current_line_(1), + is_stmt_(kDefaultIsStmt) { } private: @@ -244,6 +249,7 @@ class DebugLineOpCodeWriter FINAL : private Writer { uint64_t current_address_; int current_file_; int current_line_; + bool is_stmt_; std::vector patch_locations_; DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter); diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc index e455d0d617045471ab826a503a60bf3c74961b3e..2ba3af5e10df0a63804eec92012e56f29341070c 100644 --- a/compiler/debug/dwarf/dwarf_test.cc +++ b/compiler/debug/dwarf/dwarf_test.cc @@ -217,7 +217,9 @@ TEST_F(DwarfTest, DebugLine) { DW_CHECK_NEXT("Advance Line by 2 to 3"); opcodes.SetColumn(4); DW_CHECK_NEXT("Set column to 4"); - opcodes.NegateStmt(); + opcodes.SetIsStmt(true); + DW_CHECK_NEXT("Set is_stmt to 1"); + opcodes.SetIsStmt(false); DW_CHECK_NEXT("Set is_stmt to 0"); opcodes.SetBasicBlock(); DW_CHECK_NEXT("Set basic block"); diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h index 41bfe79c2196d3b385d71469a9cc725dbc341bf3..e2f0a65ab70a46d7547319fb2e12235552e6cd50 100644 --- a/compiler/debug/dwarf/dwarf_test.h +++ b/compiler/debug/dwarf/dwarf_test.h @@ -62,7 +62,7 @@ class DwarfTest : public CommonRuntimeTest { InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86; ScratchFile file; FileOutputStream output_stream(file.GetFile()); - ElfBuilder builder(isa, &output_stream); + ElfBuilder builder(isa, nullptr, &output_stream); builder.Start(); if (!debug_info_data_.empty()) { builder.WriteSection(".debug_info", &debug_info_data_); diff --git a/compiler/debug/elf_compilation_unit.h b/compiler/debug/elf_compilation_unit.h index f725f45e1559d9d50bc630cef684dae3f822a9c3..b1d89ebeb28e2724c7116b55213624375f5c0981 100644 --- a/compiler/debug/elf_compilation_unit.h +++ b/compiler/debug/elf_compilation_unit.h @@ -27,8 +27,9 @@ namespace debug { struct ElfCompilationUnit { std::vector methods; size_t debug_line_offset = 0; - uintptr_t low_pc = std::numeric_limits::max(); - uintptr_t high_pc = 0; + bool is_code_address_text_relative; // Is the address offset from start of .text section? + uint64_t code_address = std::numeric_limits::max(); + uint64_t code_end = 0; }; } // namespace debug diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index f6d9b169c4936458be423677584c53aa2f210529..f9d33c1c30c7c26fa82eae1fc1cf87b9539584a1 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -175,18 +175,6 @@ void WriteCFISection(ElfBuilder* builder, CHECK(format == dwarf::DW_DEBUG_FRAME_FORMAT || format == dwarf::DW_EH_FRAME_FORMAT); typedef typename ElfTypes::Addr Elf_Addr; - if (method_infos.empty()) { - return; - } - - std::vector binary_search_table; - std::vector patch_locations; - if (format == dwarf::DW_EH_FRAME_FORMAT) { - binary_search_table.reserve(2 * method_infos.size()); - } else { - patch_locations.reserve(method_infos.size()); - } - // The methods can be written in any order. // Let's therefore sort them in the lexicographical order of the opcodes. // This has no effect on its own. However, if the final .debug_frame section is @@ -194,17 +182,30 @@ void WriteCFISection(ElfBuilder* builder, std::vector sorted_method_infos; sorted_method_infos.reserve(method_infos.size()); for (size_t i = 0; i < method_infos.size(); i++) { - sorted_method_infos.push_back(&method_infos[i]); + if (!method_infos[i].cfi.empty() && !method_infos[i].deduped) { + sorted_method_infos.push_back(&method_infos[i]); + } } - std::sort( + if (sorted_method_infos.empty()) { + return; + } + std::stable_sort( sorted_method_infos.begin(), sorted_method_infos.end(), [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { - ArrayRef l = lhs->compiled_method->GetCFIInfo(); - ArrayRef r = rhs->compiled_method->GetCFIInfo(); + ArrayRef l = lhs->cfi; + ArrayRef r = rhs->cfi; return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); }); + std::vector binary_search_table; + std::vector patch_locations; + if (format == dwarf::DW_EH_FRAME_FORMAT) { + binary_search_table.reserve(2 * sorted_method_infos.size()); + } else { + patch_locations.reserve(sorted_method_infos.size()); + } + // Write .eh_frame/.debug_frame section. auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT ? builder->GetDebugFrame() @@ -212,9 +213,6 @@ void WriteCFISection(ElfBuilder* builder, { cfi_section->Start(); const bool is64bit = Is64BitInstructionSet(builder->GetIsa()); - const Elf_Addr text_address = builder->GetText()->Exists() - ? builder->GetText()->GetAddress() - : 0; const Elf_Addr cfi_address = cfi_section->GetAddress(); const Elf_Addr cie_address = cfi_address; Elf_Addr buffer_address = cfi_address; @@ -224,25 +222,21 @@ void WriteCFISection(ElfBuilder* builder, buffer_address += buffer.size(); buffer.clear(); for (const MethodDebugInfo* mi : sorted_method_infos) { - if (!mi->deduped) { // Only one FDE per unique address. - ArrayRef opcodes = mi->compiled_method->GetCFIInfo(); - if (!opcodes.empty()) { - const Elf_Addr code_address = text_address + mi->low_pc; - if (format == dwarf::DW_EH_FRAME_FORMAT) { - binary_search_table.push_back( - dchecked_integral_cast(code_address)); - binary_search_table.push_back( - dchecked_integral_cast(buffer_address)); - } - WriteFDE(is64bit, cfi_address, cie_address, - code_address, mi->high_pc - mi->low_pc, - opcodes, format, buffer_address, &buffer, - &patch_locations); - cfi_section->WriteFully(buffer.data(), buffer.size()); - buffer_address += buffer.size(); - buffer.clear(); - } + DCHECK(!mi->deduped); + DCHECK(!mi->cfi.empty()); + const Elf_Addr code_address = mi->code_address + + (mi->is_code_address_text_relative ? builder->GetText()->GetAddress() : 0); + if (format == dwarf::DW_EH_FRAME_FORMAT) { + binary_search_table.push_back(dchecked_integral_cast(code_address)); + binary_search_table.push_back(dchecked_integral_cast(buffer_address)); } + WriteFDE(is64bit, cfi_address, cie_address, + code_address, mi->code_size, + mi->cfi, format, buffer_address, &buffer, + &patch_locations); + cfi_section->WriteFully(buffer.data(), buffer.size()); + buffer_address += buffer.size(); + buffer.clear(); } cfi_section->End(); } diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index eed032f88d0aa73717db67b07cfc8ab66fc38463..a6e6f8b5da87ea3335554b840afffbd59abd4206 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -46,6 +46,7 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector GetParamNames(const MethodDebugInfo* mi) { std::vector names; if (mi->code_item != nullptr) { + DCHECK(mi->dex_file != nullptr); const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item); if (stream != nullptr) { DecodeUnsignedLeb128(&stream); // line. @@ -117,22 +118,23 @@ class ElfCompilationUnitWriter { void Write(const ElfCompilationUnit& compilation_unit) { CHECK(!compilation_unit.methods.empty()); - const Elf_Addr text_address = owner_->builder_->GetText()->Exists() + const Elf_Addr base_address = compilation_unit.is_code_address_text_relative ? owner_->builder_->GetText()->GetAddress() : 0; - const uintptr_t cu_size = compilation_unit.high_pc - compilation_unit.low_pc; + const uint64_t cu_size = compilation_unit.code_end - compilation_unit.code_address; using namespace dwarf; // NOLINT. For easy access to DWARF constants. info_.StartTag(DW_TAG_compile_unit); info_.WriteString(DW_AT_producer, "Android dex2oat"); info_.WriteData1(DW_AT_language, DW_LANG_Java); info_.WriteString(DW_AT_comp_dir, "$JAVA_SRC_ROOT"); - info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc); + info_.WriteAddr(DW_AT_low_pc, base_address + compilation_unit.code_address); info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast(cu_size)); info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset); const char* last_dex_class_desc = nullptr; for (auto mi : compilation_unit.methods) { + DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; const DexFile::CodeItem* dex_code = mi->code_item; const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index); @@ -165,14 +167,27 @@ class ElfCompilationUnitWriter { int start_depth = info_.Depth(); info_.StartTag(DW_TAG_subprogram); WriteName(dex->GetMethodName(dex_method)); - info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc); - info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast(mi->high_pc-mi->low_pc)); + info_.WriteAddr(DW_AT_low_pc, base_address + mi->code_address); + info_.WriteUdata(DW_AT_high_pc, mi->code_size); std::vector expr_buffer; Expression expr(&expr_buffer); expr.WriteOpCallFrameCfa(); info_.WriteExprLoc(DW_AT_frame_base, expr); WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto)); + // Decode dex register locations for all stack maps. + // It might be expensive, so do it just once and reuse the result. + std::vector dex_reg_maps; + if (mi->code_info != nullptr) { + const CodeInfo code_info(mi->code_info); + StackMapEncoding encoding = code_info.ExtractEncoding(); + for (size_t s = 0; s < code_info.GetNumberOfStackMaps(); ++s) { + const StackMap& stack_map = code_info.GetStackMapAt(s, encoding); + dex_reg_maps.push_back(code_info.GetDexRegisterMapOf( + stack_map, encoding, dex_code->registers_size_)); + } + } + // Write parameters. DecodeDebugLocalInfo returns them as well, but it does not // guarantee order or uniqueness so it is safer to iterate over them manually. // DecodeDebugLocalInfo might not also be available if there is no debug info. @@ -187,7 +202,7 @@ class ElfCompilationUnitWriter { // Write the stack location of the parameter. const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; const bool is64bitValue = false; - WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc); + WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } arg_reg++; info_.EndTag(); @@ -206,7 +221,7 @@ class ElfCompilationUnitWriter { if (dex_code != nullptr) { // Write the stack location of the parameter. const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg; - WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc); + WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address); } arg_reg += is64bitValue ? 2 : 1; info_.EndTag(); @@ -229,8 +244,13 @@ class ElfCompilationUnitWriter { WriteName(var.name_); WriteLazyType(var.descriptor_); bool is64bitValue = var.descriptor_[0] == 'D' || var.descriptor_[0] == 'J'; - WriteRegLocation(mi, var.reg_, is64bitValue, compilation_unit.low_pc, - var.start_address_, var.end_address_); + WriteRegLocation(mi, + dex_reg_maps, + var.reg_, + is64bitValue, + compilation_unit.code_address, + var.start_address_, + var.end_address_); info_.EndTag(); } } @@ -424,15 +444,17 @@ class ElfCompilationUnitWriter { // The dex register might be valid only at some points and it might // move between machine registers and stack. void WriteRegLocation(const MethodDebugInfo* method_info, + const std::vector& dex_register_maps, uint16_t vreg, bool is64bitValue, - uint32_t compilation_unit_low_pc, + uint64_t compilation_unit_code_address, uint32_t dex_pc_low = 0, uint32_t dex_pc_high = 0xFFFFFFFF) { WriteDebugLocEntry(method_info, + dex_register_maps, vreg, is64bitValue, - compilation_unit_low_pc, + compilation_unit_code_address, dex_pc_low, dex_pc_high, owner_->builder_->GetIsa(), diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index d3859ca7525755d95cb10925e92055832672e493..66e135f395563487f3c6c431c4080c33cba87258 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_ #define ART_COMPILER_DEBUG_ELF_DEBUG_LINE_WRITER_H_ +#include #include #include "compiled_method.h" @@ -53,7 +54,7 @@ class ElfDebugLineWriter { // Returns the number of bytes written. size_t WriteCompilationUnit(ElfCompilationUnit& compilation_unit) { const bool is64bit = Is64BitInstructionSet(builder_->GetIsa()); - const Elf_Addr text_address = builder_->GetText()->Exists() + const Elf_Addr base_address = compilation_unit.is_code_address_text_relative ? builder_->GetText()->GetAddress() : 0; @@ -81,48 +82,80 @@ class ElfDebugLineWriter { case kX86_64: break; } + std::unordered_set seen_addresses(compilation_unit.methods.size()); dwarf::DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_); for (const MethodDebugInfo* mi : compilation_unit.methods) { // Ignore function if we have already generated line table for the same address. // It would confuse the debugger and the DWARF specification forbids it. - if (mi->deduped) { + // We allow the line table for method to be replicated in different compilation unit. + // This ensures that each compilation unit contains line table for all its methods. + if (!seen_addresses.insert(mi->code_address).second) { continue; } uint32_t prologue_end = std::numeric_limits::max(); - ArrayRef pc2dex_map; - std::vector pc2dex_map_from_stack_maps; - if (mi->IsFromOptimizingCompiler()) { + std::vector pc2dex_map; + if (mi->code_info != nullptr) { // Use stack maps to create mapping table from pc to dex. - const CodeInfo code_info(mi->compiled_method->GetVmapTable().data()); + const CodeInfo code_info(mi->code_info); const StackMapEncoding encoding = code_info.ExtractEncoding(); + pc2dex_map.reserve(code_info.GetNumberOfStackMaps()); for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { StackMap stack_map = code_info.GetStackMapAt(s, encoding); DCHECK(stack_map.IsValid()); const uint32_t pc = stack_map.GetNativePcOffset(encoding); const int32_t dex = stack_map.GetDexPc(encoding); - pc2dex_map_from_stack_maps.push_back({pc, dex}); + pc2dex_map.push_back({pc, dex}); if (stack_map.HasDexRegisterMap(encoding)) { // Guess that the first map with local variables is the end of prologue. prologue_end = std::min(prologue_end, pc); } } - std::sort(pc2dex_map_from_stack_maps.begin(), - pc2dex_map_from_stack_maps.end()); - pc2dex_map = ArrayRef(pc2dex_map_from_stack_maps); - } else { - // Use the mapping table provided by the quick compiler. - pc2dex_map = mi->compiled_method->GetSrcMappingTable(); - prologue_end = 0; + std::sort(pc2dex_map.begin(), pc2dex_map.end()); } if (pc2dex_map.empty()) { continue; } - Elf_Addr method_address = text_address + mi->low_pc; + // Compensate for compiler's off-by-one-instruction error. + // + // The compiler generates stackmap with PC *after* the branch instruction + // (because this is the PC which is easier to obtain when unwinding). + // + // However, the debugger is more clever and it will ask us for line-number + // mapping at the location of the branch instruction (since the following + // instruction could belong to other line, this is the correct thing to do). + // + // So we really want to just decrement the PC by one instruction so that the + // branch instruction is covered as well. However, we do not know the size + // of the previous instruction, and we can not subtract just a fixed amount + // (the debugger would trust us that the PC is valid; it might try to set + // breakpoint there at some point, and setting breakpoint in mid-instruction + // would make the process crash in spectacular way). + // + // Therefore, we say that the PC which the compiler gave us for the stackmap + // is the end of its associated address range, and we use the PC from the + // previous stack map as the start of the range. This ensures that the PC is + // valid and that the branch instruction is covered. + // + // This ensures we have correct line number mapping at call sites (which is + // important for backtraces), but there is nothing we can do for non-call + // sites (so stepping through optimized code in debugger is not possible). + // + // We do not adjust the stackmaps if the code was compiled as debuggable. + // In that case, the stackmaps should accurately cover all instructions. + if (!mi->is_native_debuggable) { + for (size_t i = pc2dex_map.size() - 1; i > 0; --i) { + pc2dex_map[i].from_ = pc2dex_map[i - 1].from_; + } + pc2dex_map[0].from_ = 0; + } + + Elf_Addr method_address = base_address + mi->code_address; PositionInfos dex2line_map; + DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) { continue; @@ -184,6 +217,10 @@ class ElfDebugLineWriter { // Generate mapping opcodes from PC to Java lines. if (file_index != 0) { + // If the method was not compiled as native-debuggable, we still generate all available + // lines, but we try to prevent the debugger from stepping and setting breakpoints since + // the information is too inaccurate for that (breakpoints would be set after the calls). + const bool default_is_stmt = mi->is_native_debuggable; bool first = true; for (SrcMapElem pc2dex : pc2dex_map) { uint32_t pc = pc2dex.from_; @@ -205,13 +242,14 @@ class ElfDebugLineWriter { // Assume that any preceding code is prologue. int first_line = dex2line_map.front().line_; // Prologue is not a sensible place for a breakpoint. - opcodes.NegateStmt(); + opcodes.SetIsStmt(false); opcodes.AddRow(method_address, first_line); - opcodes.NegateStmt(); opcodes.SetPrologueEnd(); } + opcodes.SetIsStmt(default_is_stmt); opcodes.AddRow(method_address + pc, line); } else if (line != opcodes.CurrentLine()) { + opcodes.SetIsStmt(default_is_stmt); opcodes.AddRow(method_address + pc, line); } } @@ -221,7 +259,7 @@ class ElfDebugLineWriter { opcodes.AddRow(method_address, 0); } - opcodes.AdvancePC(text_address + mi->high_pc); + opcodes.AdvancePC(method_address + mi->code_size); opcodes.EndSequence(); } std::vector buffer; diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index 8fd20aa428c662c9d9d24a8f4b9146a8b5eb0b31..2d4fff4d14e4ec29204e2cac9ad5c29e36d3bda5 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -74,8 +74,8 @@ static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) { } struct VariableLocation { - uint32_t low_pc; - uint32_t high_pc; + uint32_t low_pc; // Relative to compilation unit. + uint32_t high_pc; // Relative to compilation unit. DexRegisterLocation reg_lo; // May be None if the location is unknown. DexRegisterLocation reg_hi; // Most significant bits of 64-bit value. }; @@ -85,33 +85,41 @@ struct VariableLocation { // The result will cover all ranges where the variable is in scope. // PCs corresponding to stackmap with dex register map are accurate, // all other PCs are best-effort only. -std::vector GetVariableLocations(const MethodDebugInfo* method_info, - uint16_t vreg, - bool is64bitValue, - uint32_t dex_pc_low, - uint32_t dex_pc_high) { +std::vector GetVariableLocations( + const MethodDebugInfo* method_info, + const std::vector& dex_register_maps, + uint16_t vreg, + bool is64bitValue, + uint64_t compilation_unit_code_address, + uint32_t dex_pc_low, + uint32_t dex_pc_high) { std::vector variable_locations; // Get stack maps sorted by pc (they might not be sorted internally). - const CodeInfo code_info(method_info->compiled_method->GetVmapTable().data()); + const CodeInfo code_info(method_info->code_info); const StackMapEncoding encoding = code_info.ExtractEncoding(); - std::map stack_maps; + std::map stack_maps; // low_pc -> stack_map_index. for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { StackMap stack_map = code_info.GetStackMapAt(s, encoding); DCHECK(stack_map.IsValid()); - const uint32_t low_pc = method_info->low_pc + stack_map.GetNativePcOffset(encoding); - DCHECK_LE(low_pc, method_info->high_pc); - stack_maps.emplace(low_pc, stack_map); + const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding); + DCHECK_LE(pc_offset, method_info->code_size); + DCHECK_LE(compilation_unit_code_address, method_info->code_address); + const uint32_t low_pc = dchecked_integral_cast( + method_info->code_address + pc_offset - compilation_unit_code_address); + stack_maps.emplace(low_pc, s); } // Create entries for the requested register based on stack map data. for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) { - const StackMap& stack_map = it->second; const uint32_t low_pc = it->first; + const uint32_t stack_map_index = it->second; + const StackMap& stack_map = code_info.GetStackMapAt(stack_map_index, encoding); auto next_it = it; next_it++; - const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first - : method_info->high_pc; + const uint32_t high_pc = next_it != stack_maps.end() + ? next_it->first + : method_info->code_address + method_info->code_size - compilation_unit_code_address; DCHECK_LE(low_pc, high_pc); if (low_pc == high_pc) { continue; // Ignore if the address range is empty. @@ -126,9 +134,9 @@ std::vector GetVariableLocations(const MethodDebugInfo* method // Find the location of the dex register. DexRegisterLocation reg_lo = DexRegisterLocation::None(); DexRegisterLocation reg_hi = DexRegisterLocation::None(); - if (stack_map.HasDexRegisterMap(encoding)) { - DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( - stack_map, encoding, method_info->code_item->registers_size_); + DCHECK_LT(stack_map_index, dex_register_maps.size()); + DexRegisterMap dex_register_map = dex_register_maps[stack_map_index]; + if (dex_register_map.IsValid()) { reg_lo = dex_register_map.GetDexRegisterLocation( vreg, method_info->code_item->registers_size_, code_info, encoding); if (is64bitValue) { @@ -159,9 +167,10 @@ std::vector GetVariableLocations(const MethodDebugInfo* method // The dex register might be valid only at some points and it might // move between machine registers and stack. static void WriteDebugLocEntry(const MethodDebugInfo* method_info, + const std::vector& dex_register_maps, uint16_t vreg, bool is64bitValue, - uint32_t compilation_unit_low_pc, + uint64_t compilation_unit_code_address, uint32_t dex_pc_low, uint32_t dex_pc_high, InstructionSet isa, @@ -169,14 +178,16 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, std::vector* debug_loc_buffer, std::vector* debug_ranges_buffer) { using Kind = DexRegisterLocation::Kind; - if (!method_info->IsFromOptimizingCompiler()) { + if (method_info->code_info == nullptr || dex_register_maps.empty()) { return; } std::vector variable_locations = GetVariableLocations( method_info, + dex_register_maps, vreg, is64bitValue, + compilation_unit_code_address, dex_pc_low, dex_pc_high); @@ -197,9 +208,8 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, const Kind kind = reg_loc.GetKind(); const int32_t value = reg_loc.GetValue(); if (kind == Kind::kInStack) { - const size_t frame_size = method_info->compiled_method->GetFrameSizeInBytes(); // The stack offset is relative to SP. Make it relative to CFA. - expr.WriteOpFbreg(value - frame_size); + expr.WriteOpFbreg(value - method_info->frame_size_in_bytes); if (piece == 0 && reg_hi.GetKind() == Kind::kInStack && reg_hi.GetValue() == value + 4) { break; // the high word is correctly implied by the low word. @@ -232,8 +242,7 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind(). // kInRegisterHigh and kInFpuRegisterHigh should be handled by // the special cases above and they should not occur alone. - LOG(ERROR) << "Unexpected register location kind: " - << DexRegisterLocation::PrettyDescriptor(kind); + LOG(ERROR) << "Unexpected register location kind: " << kind; break; } if (is64bitValue) { @@ -245,11 +254,11 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, if (expr.size() > 0) { if (is64bit) { - debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc); - debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc); + debug_loc.PushUint64(variable_location.low_pc); + debug_loc.PushUint64(variable_location.high_pc); } else { - debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc); - debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc); + debug_loc.PushUint32(variable_location.low_pc); + debug_loc.PushUint32(variable_location.high_pc); } // Write the expression. debug_loc.PushUint16(expr.size()); @@ -279,11 +288,11 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, high_pc = variable_locations[++i].high_pc; } if (is64bit) { - debug_ranges.PushUint64(low_pc - compilation_unit_low_pc); - debug_ranges.PushUint64(high_pc - compilation_unit_low_pc); + debug_ranges.PushUint64(low_pc); + debug_ranges.PushUint64(high_pc); } else { - debug_ranges.PushUint32(low_pc - compilation_unit_low_pc); - debug_ranges.PushUint32(high_pc - compilation_unit_low_pc); + debug_ranges.PushUint32(low_pc); + debug_ranges.PushUint32(high_pc); } } // Write end-of-list entry. diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index 01bd6797c964bbd16e8eab62947ad027882765c2..4dd802495cd47bd232b88954a8efdd65f8682206 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -39,32 +39,31 @@ void WriteDebugInfo(ElfBuilder* builder, const ArrayRef& method_infos, dwarf::CFIFormat cfi_format, bool write_oat_patches) { - // Add methods to .symtab. + // Write .strtab and .symtab. WriteDebugSymbols(builder, method_infos, true /* with_signature */); - // Generate CFI (stack unwinding information). + + // Write .debug_frame. WriteCFISection(builder, method_infos, cfi_format, write_oat_patches); - // Write DWARF .debug_* sections. - WriteDebugSections(builder, method_infos, write_oat_patches); -} -template -static void WriteDebugSections(ElfBuilder* builder, - const ArrayRef& method_infos, - bool write_oat_patches) { // Group the methods into compilation units based on source file. std::vector compilation_units; const char* last_source_file = nullptr; for (const MethodDebugInfo& mi : method_infos) { - auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index); - const char* source_file = mi.dex_file->GetSourceFile(dex_class_def); - if (compilation_units.empty() || source_file != last_source_file) { - compilation_units.push_back(ElfCompilationUnit()); + if (mi.dex_file != nullptr) { + auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index); + const char* source_file = mi.dex_file->GetSourceFile(dex_class_def); + if (compilation_units.empty() || source_file != last_source_file) { + compilation_units.push_back(ElfCompilationUnit()); + } + ElfCompilationUnit& cu = compilation_units.back(); + cu.methods.push_back(&mi); + // All methods must have the same addressing mode otherwise the min/max below does not work. + DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative); + cu.is_code_address_text_relative = mi.is_code_address_text_relative; + cu.code_address = std::min(cu.code_address, mi.code_address); + cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size); + last_source_file = source_file; } - ElfCompilationUnit& cu = compilation_units.back(); - cu.methods.push_back(&mi); - cu.low_pc = std::min(cu.low_pc, mi.low_pc); - cu.high_pc = std::max(cu.high_pc, mi.high_pc); - last_source_file = source_file; } // Write .debug_line section. @@ -91,28 +90,38 @@ static void WriteDebugSections(ElfBuilder* builder, std::vector MakeMiniDebugInfo( InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_size, size_t text_size, const ArrayRef& method_infos) { if (Is64BitInstructionSet(isa)) { - return MakeMiniDebugInfoInternal(isa, rodata_size, text_size, method_infos); + return MakeMiniDebugInfoInternal(isa, + features, + rodata_size, + text_size, + method_infos); } else { - return MakeMiniDebugInfoInternal(isa, rodata_size, text_size, method_infos); + return MakeMiniDebugInfoInternal(isa, + features, + rodata_size, + text_size, + method_infos); } } template -static ArrayRef WriteDebugElfFileForMethodInternal( - const MethodDebugInfo& method_info) { - const InstructionSet isa = method_info.compiled_method->GetInstructionSet(); +static ArrayRef WriteDebugElfFileForMethodsInternal( + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& method_infos) { std::vector buffer; buffer.reserve(KB); VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr> builder(new ElfBuilder(isa, &out)); + std::unique_ptr> builder(new ElfBuilder(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); WriteDebugInfo(builder.get(), - ArrayRef(&method_info, 1), + method_infos, dwarf::DW_DEBUG_FRAME_FORMAT, false /* write_oat_patches */); builder->End(); @@ -124,23 +133,27 @@ static ArrayRef WriteDebugElfFileForMethodInternal( return ArrayRef(result, buffer.size()); } -ArrayRef WriteDebugElfFileForMethod(const MethodDebugInfo& method_info) { - const InstructionSet isa = method_info.compiled_method->GetInstructionSet(); +ArrayRef WriteDebugElfFileForMethods( + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& method_infos) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodInternal(method_info); + return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); } else { - return WriteDebugElfFileForMethodInternal(method_info); + return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); } } template static ArrayRef WriteDebugElfFileForClassesInternal( - const InstructionSet isa, const ArrayRef& types) + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& types) SHARED_REQUIRES(Locks::mutator_lock_) { std::vector buffer; buffer.reserve(KB); VectorOutputStream out("Debug ELF file", &buffer); - std::unique_ptr> builder(new ElfBuilder(isa, &out)); + std::unique_ptr> builder(new ElfBuilder(isa, features, &out)); // No program headers since the ELF file is not linked and has no allocated sections. builder->Start(false /* write_program_headers */); ElfDebugInfoWriter info_writer(builder.get()); @@ -158,13 +171,39 @@ static ArrayRef WriteDebugElfFileForClassesInternal( return ArrayRef(result, buffer.size()); } -ArrayRef WriteDebugElfFileForClasses(const InstructionSet isa, +ArrayRef WriteDebugElfFileForClasses(InstructionSet isa, + const InstructionSetFeatures* features, const ArrayRef& types) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForClassesInternal(isa, types); + return WriteDebugElfFileForClassesInternal(isa, features, types); } else { - return WriteDebugElfFileForClassesInternal(isa, types); + return WriteDebugElfFileForClassesInternal(isa, features, types); + } +} + +std::vector MakeTrampolineInfos(const OatHeader& header) { + std::map trampolines = { + { "interpreterToInterpreterBridge", header.GetInterpreterToInterpreterBridgeOffset() }, + { "interpreterToCompiledCodeBridge", header.GetInterpreterToCompiledCodeBridgeOffset() }, + { "jniDlsymLookup", header.GetJniDlsymLookupOffset() }, + { "quickGenericJniTrampoline", header.GetQuickGenericJniTrampolineOffset() }, + { "quickImtConflictTrampoline", header.GetQuickImtConflictTrampolineOffset() }, + { "quickResolutionTrampoline", header.GetQuickResolutionTrampolineOffset() }, + { "quickToInterpreterBridge", header.GetQuickToInterpreterBridgeOffset() }, + }; + std::vector result; + for (const auto& it : trampolines) { + if (it.second != 0) { + MethodDebugInfo info = MethodDebugInfo(); + info.trampoline_name = it.first; + info.isa = header.GetInstructionSet(); + info.is_code_address_text_relative = true; + info.code_address = it.second - header.GetExecutableOffset(); + info.code_size = 0; // The symbol lasts until the next symbol. + result.push_back(std::move(info)); + } } + return result; } // Explicit instantiations diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index 103b50148998ded9b2a5c69cc12a35b33183d4ae..736370e2d35251dc1e9dbeaf4d9f17f257047ea9 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -17,6 +17,8 @@ #ifndef ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_ #define ART_COMPILER_DEBUG_ELF_DEBUG_WRITER_H_ +#include + #include "base/macros.h" #include "base/mutex.h" #include "debug/dwarf/dwarf_constants.h" @@ -24,6 +26,7 @@ #include "utils/array_ref.h" namespace art { +class OatHeader; namespace mirror { class Class; } @@ -31,22 +34,32 @@ namespace debug { struct MethodDebugInfo; template -void WriteDebugInfo(ElfBuilder* builder, - const ArrayRef& method_infos, - dwarf::CFIFormat cfi_format, - bool write_oat_patches); +void WriteDebugInfo( + ElfBuilder* builder, + const ArrayRef& method_infos, + dwarf::CFIFormat cfi_format, + bool write_oat_patches); -std::vector MakeMiniDebugInfo(InstructionSet isa, - size_t rodata_section_size, - size_t text_section_size, - const ArrayRef& method_infos); +std::vector MakeMiniDebugInfo( + InstructionSet isa, + const InstructionSetFeatures* features, + size_t rodata_section_size, + size_t text_section_size, + const ArrayRef& method_infos); -ArrayRef WriteDebugElfFileForMethod(const MethodDebugInfo& method_info); +ArrayRef WriteDebugElfFileForMethods( + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& method_infos); -ArrayRef WriteDebugElfFileForClasses(const InstructionSet isa, - const ArrayRef& types) +ArrayRef WriteDebugElfFileForClasses( + InstructionSet isa, + const InstructionSetFeatures* features, + const ArrayRef& types) SHARED_REQUIRES(Locks::mutator_lock_); +std::vector MakeTrampolineInfos(const OatHeader& oat_header); + } // namespace debug } // namespace art diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 5c7d1c72a447332480edbeb2aeca591757bf6292..fb63d62572ae842cf08e8cb3b7cd2784a1e8d727 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -79,13 +79,14 @@ static void XzCompress(const std::vector* src, std::vector* ds template static std::vector MakeMiniDebugInfoInternal( InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef& method_infos) { std::vector buffer; buffer.reserve(KB); VectorOutputStream out("Mini-debug-info ELF file", &buffer); - std::unique_ptr> builder(new ElfBuilder(isa, &out)); + std::unique_ptr> builder(new ElfBuilder(isa, features, &out)); builder->Start(); // Mirror .rodata and .text as NOBITS sections. // It is needed to detected relocations after compression. diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 41508f44b4c977a35df18fe0906b1388471575f6..045edddd77e5e4d84d4ed31b3db6e20c1b2f90ad 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -39,7 +39,7 @@ template static void WriteDebugSymbols(ElfBuilder* builder, const ArrayRef& method_infos, bool with_signature) { - bool generated_mapping_symbol = false; + uint64_t mapping_symbol_address = std::numeric_limits::max(); auto* strtab = builder->GetStrTab(); auto* symtab = builder->GetSymTab(); @@ -47,12 +47,12 @@ static void WriteDebugSymbols(ElfBuilder* builder, return; } - // Find all addresses (low_pc) which contain deduped methods. + // Find all addresses which contain deduped methods. // The first instance of method is not marked deduped_, but the rest is. - std::unordered_set deduped_addresses; + std::unordered_set deduped_addresses; for (const MethodDebugInfo& info : method_infos) { if (info.deduped) { - deduped_addresses.insert(info.low_pc); + deduped_addresses.insert(info.code_address); } } @@ -64,40 +64,37 @@ static void WriteDebugSymbols(ElfBuilder* builder, if (info.deduped) { continue; // Add symbol only for the first instance. } - std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature); - if (deduped_addresses.find(info.low_pc) != deduped_addresses.end()) { - name += " [DEDUPED]"; + size_t name_offset; + if (info.trampoline_name != nullptr) { + name_offset = strtab->Write(info.trampoline_name); + } else { + DCHECK(info.dex_file != nullptr); + std::string name = PrettyMethod(info.dex_method_index, *info.dex_file, with_signature); + if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) { + name += " [DEDUPED]"; + } + // If we write method names without signature, we might see the same name multiple times. + name_offset = (name == last_name ? last_name_offset : strtab->Write(name)); + last_name = std::move(name); + last_name_offset = name_offset; } - // If we write method names without signature, we might see the same name multiple times. - size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name)); - const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr; - const bool is_relative = (text != nullptr); - uint32_t low_pc = info.low_pc; + const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr; + uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0); // Add in code delta, e.g., thumb bit 0 for Thumb2 code. - low_pc += info.compiled_method->CodeDelta(); - symtab->Add(name_offset, - text, - low_pc, - is_relative, - info.high_pc - info.low_pc, - STB_GLOBAL, - STT_FUNC); + address += CompiledMethod::CodeDelta(info.isa); + symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC); // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2 // instructions, so that disassembler tools can correctly disassemble. // Note that even if we generate just a single mapping symbol, ARM's Streamline // requires it to match function symbol. Just address 0 does not work. - if (info.compiled_method->GetInstructionSet() == kThumb2) { - if (!generated_mapping_symbol || !kGenerateSingleArmMappingSymbol) { - symtab->Add(strtab->Write("$t"), text, info.low_pc & ~1, - is_relative, 0, STB_LOCAL, STT_NOTYPE); - generated_mapping_symbol = true; + if (info.isa == kThumb2) { + if (address < mapping_symbol_address || !kGenerateSingleArmMappingSymbol) { + symtab->Add(strtab->Write("$t"), text, address & ~1, 0, STB_LOCAL, STT_NOTYPE); + mapping_symbol_address = address; } } - - last_name = std::move(name); - last_name_offset = name_offset; } strtab->End(); diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h index 6b3dd8c528a9209c7ff0a5ecc8058e9f01624c89..ed1da2c26e9dc855db03eabca3bce7c134551288 100644 --- a/compiler/debug/method_debug_info.h +++ b/compiler/debug/method_debug_info.h @@ -24,22 +24,22 @@ namespace art { namespace debug { struct MethodDebugInfo { - const DexFile* dex_file; + const char* trampoline_name; + const DexFile* dex_file; // Native methods (trampolines) do not reference dex file. size_t class_def_index; uint32_t dex_method_index; uint32_t access_flags; const DexFile::CodeItem* code_item; + InstructionSet isa; bool deduped; - uintptr_t low_pc; - uintptr_t high_pc; - CompiledMethod* compiled_method; - - bool IsFromOptimizingCompiler() const { - return compiled_method->GetQuickCode().size() > 0 && - compiled_method->GetVmapTable().size() > 0 && - compiled_method->GetGcMap().size() == 0 && - code_item != nullptr; - } + bool is_native_debuggable; + bool is_optimized; + bool is_code_address_text_relative; // Is the address offset from start of .text section? + uint64_t code_address; + uint32_t code_size; + uint32_t frame_size_in_bytes; + const void* code_info; + ArrayRef cfi; }; } // namespace debug diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index b78b3d7d7551607cae3556ebb8faa36fb8719b0f..8800e4b08f31cd7370ba9e41ed0af60dfbf9a14a 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -569,6 +569,7 @@ enum MemBarrierKind { kStoreStore, kAnyAny, kNTStoreStore, + kLastBarrierKind = kNTStoreStore }; std::ostream& operator<<(std::ostream& os, const MemBarrierKind& kind); diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 209f1011996478e9aec6efb319285721b21dfef0..48c4356cfd8000d6eeb82f94151af5c896275fe1 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -37,6 +37,8 @@ namespace { // anonymous namespace static constexpr bool kIntrinsicIsStatic[] = { true, // kIntrinsicDoubleCvt true, // kIntrinsicFloatCvt + true, // kIntrinsicFloat2Int + true, // kIntrinsicDouble2Long true, // kIntrinsicFloatIsInfinite true, // kIntrinsicDoubleIsInfinite true, // kIntrinsicFloatIsNaN @@ -99,6 +101,14 @@ static constexpr bool kIntrinsicIsStatic[] = { false, // kIntrinsicCas false, // kIntrinsicUnsafeGet false, // kIntrinsicUnsafePut + false, // kIntrinsicUnsafeGetAndAddInt, + false, // kIntrinsicUnsafeGetAndAddLong, + false, // kIntrinsicUnsafeGetAndSetInt, + false, // kIntrinsicUnsafeGetAndSetLong, + false, // kIntrinsicUnsafeGetAndSetObject, + false, // kIntrinsicUnsafeLoadFence, + false, // kIntrinsicUnsafeStoreFence, + false, // kIntrinsicUnsafeFullFence, true, // kIntrinsicSystemArrayCopyCharArray true, // kIntrinsicSystemArrayCopy }; @@ -106,6 +116,8 @@ static_assert(arraysize(kIntrinsicIsStatic) == kInlineOpNop, "arraysize of kIntrinsicIsStatic unexpected"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleCvt], "DoubleCvt must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatCvt], "FloatCvt must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicFloat2Int], "Float2Int must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicDouble2Long], "Double2Long must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsInfinite], "FloatIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsInfinite], "DoubleIsInfinite must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsNaN], "FloatIsNaN must be static"); @@ -173,6 +185,14 @@ static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet must not be static"); static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddInt], "UnsafeGetAndAddInt must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndAddLong], "UnsafeGetAndAddLong must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetInt], "UnsafeGetAndSetInt must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetLong], "UnsafeGetAndSetLong must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGetAndSetObject], "UnsafeGetAndSetObject must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeLoadFence], "UnsafeLoadFence must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeStoreFence], "UnsafeStoreFence must not be static"); +static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeFullFence], "UnsafeFullFence must not be static"); static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray], "SystemArrayCopyCharArray must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopy], @@ -277,6 +297,8 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "equals", // kNameCacheEquals "getCharsNoCheck", // kNameCacheGetCharsNoCheck "isEmpty", // kNameCacheIsEmpty + "floatToIntBits", // kNameCacheFloatToIntBits + "doubleToLongBits", // kNameCacheDoubleToLongBits "isInfinite", // kNameCacheIsInfinite "isNaN", // kNameCacheIsNaN "indexOf", // kNameCacheIndexOf @@ -312,6 +334,14 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "putObject", // kNameCachePutObject "putObjectVolatile", // kNameCachePutObjectVolatile "putOrderedObject", // kNameCachePutOrderedObject + "getAndAddInt", // kNameCacheGetAndAddInt, + "getAndAddLong", // kNameCacheGetAndAddLong, + "getAndSetInt", // kNameCacheGetAndSetInt, + "getAndSetLong", // kNameCacheGetAndSetLong, + "getAndSetObject", // kNameCacheGetAndSetObject, + "loadFence", // kNameCacheLoadFence, + "storeFence", // kNameCacheStoreFence, + "fullFence", // kNameCacheFullFence, "arraycopy", // kNameCacheArrayCopy "bitCount", // kNameCacheBitCount "compare", // kNameCacheCompare @@ -398,10 +428,14 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { kClassCacheJavaLangObject, kClassCacheJavaLangObject } }, // kProtoCacheObjectJ_I { kClassCacheInt, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJI_I + { kClassCacheInt, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, // kProtoCacheObjectJI_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheInt } }, // kProtoCacheObjectJ_J { kClassCacheLong, 2, { kClassCacheJavaLangObject, kClassCacheLong } }, + // kProtoCacheObjectJJ_J + { kClassCacheLong, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, // kProtoCacheObjectJJ_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheLong } }, // kProtoCacheObjectJ_Object @@ -409,6 +443,9 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { // kProtoCacheObjectJObject_V { kClassCacheVoid, 3, { kClassCacheJavaLangObject, kClassCacheLong, kClassCacheJavaLangObject } }, + // kProtoCacheObjectJObject_Object + { kClassCacheJavaLangObject, 3, { kClassCacheJavaLangObject, kClassCacheLong, + kClassCacheJavaLangObject } }, // kProtoCacheCharArrayICharArrayII_V { kClassCacheVoid, 5, {kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheJavaLangCharArray, kClassCacheInt, kClassCacheInt} }, @@ -472,6 +509,9 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint), + INTRINSIC(JavaLangFloat, FloatToIntBits, F_I, kIntrinsicFloat2Int, 0), + INTRINSIC(JavaLangDouble, DoubleToLongBits, D_J, kIntrinsicDouble2Long, 0), + INTRINSIC(JavaLangFloat, IsInfinite, F_Z, kIntrinsicFloatIsInfinite, 0), INTRINSIC(JavaLangDouble, IsInfinite, D_Z, kIntrinsicDoubleIsInfinite, 0), INTRINSIC(JavaLangFloat, IsNaN, F_Z, kIntrinsicFloatIsNaN, 0), @@ -565,6 +605,13 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangString, IndexOf, I_I, kIntrinsicIndexOf, kIntrinsicFlagBase0), INTRINSIC(JavaLangString, Length, _I, kIntrinsicIsEmptyOrLength, kIntrinsicFlagLength), + INTRINSIC(JavaLangStringFactory, NewStringFromBytes, ByteArrayIII_String, + kIntrinsicNewStringFromBytes, kIntrinsicFlagNone), + INTRINSIC(JavaLangStringFactory, NewStringFromChars, IICharArray_String, + kIntrinsicNewStringFromChars, kIntrinsicFlagNone), + INTRINSIC(JavaLangStringFactory, NewStringFromString, String_String, + kIntrinsicNewStringFromString, kIntrinsicFlagNone), + INTRINSIC(JavaLangThread, CurrentThread, _Thread, kIntrinsicCurrentThread, 0), INTRINSIC(LibcoreIoMemory, PeekByte, J_B, kIntrinsicPeek, kSignedByte), @@ -600,6 +647,16 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods UNSAFE_GET_PUT(Object, Object, kIntrinsicFlagIsObject), #undef UNSAFE_GET_PUT + // 1.8 + INTRINSIC(SunMiscUnsafe, GetAndAddInt, ObjectJI_I, kIntrinsicUnsafeGetAndAddInt, 0), + INTRINSIC(SunMiscUnsafe, GetAndAddLong, ObjectJJ_J, kIntrinsicUnsafeGetAndAddLong, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetInt, ObjectJI_I, kIntrinsicUnsafeGetAndSetInt, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetLong, ObjectJJ_J, kIntrinsicUnsafeGetAndSetLong, 0), + INTRINSIC(SunMiscUnsafe, GetAndSetObject, ObjectJObject_Object, kIntrinsicUnsafeGetAndSetObject, 0), + INTRINSIC(SunMiscUnsafe, LoadFence, _V, kIntrinsicUnsafeLoadFence, 0), + INTRINSIC(SunMiscUnsafe, StoreFence, _V, kIntrinsicUnsafeStoreFence, 0), + INTRINSIC(SunMiscUnsafe, FullFence, _V, kIntrinsicUnsafeFullFence, 0), + INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray, 0), INTRINSIC(JavaLangSystem, ArrayCopy, ObjectIObjectII_V , kIntrinsicSystemArrayCopy, @@ -791,6 +848,8 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { intrinsic.d.data & kIntrinsicFlagIsOrdered); case kIntrinsicSystemArrayCopyCharArray: return backend->GenInlinedArrayCopyCharArray(info); + case kIntrinsicFloat2Int: + case kIntrinsicDouble2Long: case kIntrinsicFloatIsInfinite: case kIntrinsicDoubleIsInfinite: case kIntrinsicFloatIsNaN: @@ -804,6 +863,14 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { case kIntrinsicRotateRight: case kIntrinsicRotateLeft: case kIntrinsicSignum: + case kIntrinsicUnsafeGetAndAddInt: + case kIntrinsicUnsafeGetAndAddLong: + case kIntrinsicUnsafeGetAndSetInt: + case kIntrinsicUnsafeGetAndSetLong: + case kIntrinsicUnsafeGetAndSetObject: + case kIntrinsicUnsafeLoadFence: + case kIntrinsicUnsafeStoreFence: + case kIntrinsicUnsafeFullFence: case kIntrinsicSystemArrayCopy: return false; // not implemented in quick. default: diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h index 59b8a533ae6ff8fc16e7546ac4cc8b3f75781945..34b56cd49440ea186e3a689522efa89a6ace02be 100644 --- a/compiler/dex/quick/dex_file_method_inliner.h +++ b/compiler/dex/quick/dex_file_method_inliner.h @@ -190,6 +190,8 @@ class DexFileMethodInliner { kNameCacheEquals, kNameCacheGetCharsNoCheck, kNameCacheIsEmpty, + kNameCacheFloatToIntBits, + kNameCacheDoubleToLongBits, kNameCacheIsInfinite, kNameCacheIsNaN, kNameCacheIndexOf, @@ -225,6 +227,14 @@ class DexFileMethodInliner { kNameCachePutObject, kNameCachePutObjectVolatile, kNameCachePutOrderedObject, + kNameCacheGetAndAddInt, + kNameCacheGetAndAddLong, + kNameCacheGetAndSetInt, + kNameCacheGetAndSetLong, + kNameCacheGetAndSetObject, + kNameCacheLoadFence, + kNameCacheStoreFence, + kNameCacheFullFence, kNameCacheArrayCopy, kNameCacheBitCount, kNameCacheCompare, @@ -280,11 +290,14 @@ class DexFileMethodInliner { kProtoCacheObjectJJJ_Z, kProtoCacheObjectJObjectObject_Z, kProtoCacheObjectJ_I, + kProtoCacheObjectJI_I, kProtoCacheObjectJI_V, kProtoCacheObjectJ_J, + kProtoCacheObjectJJ_J, kProtoCacheObjectJJ_V, kProtoCacheObjectJ_Object, kProtoCacheObjectJObject_V, + kProtoCacheObjectJObject_Object, kProtoCacheCharArrayICharArrayII_V, kProtoCacheObjectIObjectII_V, kProtoCacheIICharArrayI_V, diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 422d82ffa23070d7af9b1b42737288fdfadfda30..11d0c9af8acfd6f6a220c84779e407ef86c5af03 100755 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1140,6 +1140,12 @@ bool Mir2Lir::GenInlinedStringFactoryNewStringFromChars(CallInfo* info) { RegLocation rl_offset = info->args[0]; RegLocation rl_count = info->args[1]; RegLocation rl_data = info->args[2]; + // No need to emit code checking whether `rl_data` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. CallRuntimeHelperRegLocationRegLocationRegLocation( kQuickAllocStringFromChars, rl_offset, rl_count, rl_data, true); RegLocation rl_return = GetReturn(kRefReg); @@ -1357,6 +1363,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { LoadValueDirectFixed(rl_start, reg_start); } RegStorage r_tgt = LoadHelper(kQuickIndexOf); + CheckEntrypointTypes(); GenExplicitNullCheck(reg_ptr, info->opt_flags); LIR* high_code_point_branch = rl_char.is_const ? nullptr : OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, nullptr); diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc index 0cd41bbf4cde8cec8672c4b7db58a7172f021f1e..6c6c9cfb1ebc38211596309c061144942628b999 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 efdc3332613dded9efb16f54f197b8bfa8812377..ff0ecea94ca511dcb706054ef6fdf568983af745 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( 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 2e2d1f99f33f734d89810c87fbede74aa90450a7..0695cb56b31e227f00a8786d9ae4d115cae21a8e 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.cc b/compiler/driver/compiler_driver.cc index 5ed4c252476ef75e09b013db2483c25b83889fec..5d8e3baacba33446b1d6fbf69428608fbf1fddbc 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -340,12 +340,15 @@ CompilerDriver::CompilerDriver( Compiler::Kind compiler_kind, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, - bool boot_image, std::unordered_set* image_classes, + bool boot_image, + std::unordered_set* image_classes, std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, - size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer, int swap_fd, - const std::unordered_map* 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), @@ -372,7 +375,6 @@ 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); @@ -1676,12 +1678,6 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType use_dex_cache = true; } } - if (!use_dex_cache && IsBootImage()) { - if (!AreInSameOatFile(&(const_cast(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; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index d8f23f7a7387738ba1d8dd38651f3f1a14af1d1d..42a5bc15e4d900828a565a95fbf462e4c27f554b 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* image_classes, std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, - size_t thread_count, bool dump_stats, bool dump_passes, - CumulativeLogger* timer, int swap_fd, - const std::unordered_map* 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(); } - // 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& dex_files, TimingLogger* timings) @@ -701,9 +689,6 @@ class CompilerDriver { // List of dex files that will be stored in the oat file. const std::vector* dex_files_for_oat_file_; - // Map from dex files to the oat file (name) they will be compiled into. - const std::unordered_map* dex_file_oat_filename_map_; - CompiledMethodStorage compiled_method_storage_; // Info for profile guided compilation. diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index a2209592885cb22da64a5181cce188095c97a17a..4db82a638d7410f72f56c0bfff34b5d61a7ca703 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -159,10 +159,16 @@ class CompilerOptions FINAL { size_t GetInlineDepthLimit() const { return inline_depth_limit_; } + void SetInlineDepthLimit(size_t limit) { + inline_depth_limit_ = limit; + } size_t GetInlineMaxCodeUnits() const { return inline_max_code_units_; } + void SetInlineMaxCodeUnits(size_t units) { + inline_max_code_units_ = units; + } double GetTopKProfileThreshold() const { return top_k_profile_threshold_; diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index b673eeb3b6abd1ea7028bad60ad0a0f2b34beacd..ef44a6fe1cfa79ba140e4cf5558dccc017422d27 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -20,6 +20,7 @@ #include #include "arch/instruction_set.h" +#include "arch/mips/instruction_set_features_mips.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/unix_file/fd_file.h" @@ -38,6 +39,7 @@ namespace art { // .rodata - DEX files and oat metadata. // .text - Compiled code. // .bss - Zero-initialized writeable section. +// .MIPS.abiflags - MIPS specific section. // .dynstr - Names for .dynsym. // .dynsym - A few oat-specific dynamic symbols. // .hash - Hash-table for .dynsym. @@ -86,12 +88,24 @@ class ElfBuilder FINAL { // Base class of all sections. class Section : public OutputStream { public: - Section(ElfBuilder* 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* 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; @@ -151,12 +165,6 @@ class ElfBuilder FINAL { } } - // Returns true if the section was written to disk. - // (Used to check whether we have .text when writing JIT debug info) - bool Exists() const { - return finished_; - } - // Get the location of this section in virtual memory. Elf_Addr GetAddress() const { CHECK(started_); @@ -228,12 +236,84 @@ class ElfBuilder FINAL { DISALLOW_COPY_AND_ASSIGN(Section); }; - // Writer of .dynstr .strtab and .shstrtab sections. + class CachedSection : public Section { + public: + CachedSection(ElfBuilder* 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(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 cache_; + }; + + // Writer of .dynstr section. + class CachedStringSection FINAL : public CachedSection { + public: + CachedStringSection(ElfBuilder* 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* owner, const std::string& name, - Elf_Word flags, Elf_Word align) - : Section(owner, name, SHT_STRTAB, flags, nullptr, 0, align, 0), + StringSection(ElfBuilder* 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,46 +332,135 @@ class ElfBuilder FINAL { }; // Writer of .dynsym and .symtab sections. - class SymbolSection FINAL : public Section { + class SymbolSection FINAL : public CachedSection { public: - SymbolSection(ElfBuilder* 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* 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, + Elf_Word size, + uint8_t binding, + uint8_t type) { + Elf_Word section_index; + if (section != nullptr) { + DCHECK_LE(section->GetAddress(), addr); + DCHECK_LE(addr, section->GetAddress() + section->GetSize()); + section_index = section->GetSectionIndex(); + } else { + section_index = static_cast(SHN_ABS); + } + Add(name, section_index, addr, size, binding, type); + } + + void Add(Elf_Word name, + Elf_Word section_index, + Elf_Addr addr, + Elf_Word size, + uint8_t binding, + uint8_t type) { 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(SHN_ABS)); + sym.st_other = 0; + sym.st_shndx = section_index; sym.st_info = (binding << 4) + (type & 0xf); - symbols_.push_back(sym); + CachedSection::Add(&sym, sizeof(sym)); + } + }; + + class AbiflagsSection FINAL : public Section { + public: + // Section with Mips abiflag info. + static constexpr uint8_t MIPS_AFL_REG_NONE = 0; // no registers + static constexpr uint8_t MIPS_AFL_REG_32 = 1; // 32-bit registers + static constexpr uint8_t MIPS_AFL_REG_64 = 2; // 64-bit registers + static constexpr uint32_t MIPS_AFL_FLAGS1_ODDSPREG = 1; // Uses odd single-prec fp regs + static constexpr uint8_t MIPS_ABI_FP_DOUBLE = 1; // -mdouble-float + static constexpr uint8_t MIPS_ABI_FP_XX = 5; // -mfpxx + static constexpr uint8_t MIPS_ABI_FP_64A = 7; // -mips32r* -mfp64 -mno-odd-spreg + + AbiflagsSection(ElfBuilder* owner, + const std::string& name, + Elf_Word type, + Elf_Word flags, + const Section* link, + Elf_Word info, + Elf_Word align, + Elf_Word entsize, + InstructionSet isa, + const InstructionSetFeatures* features) + : Section(owner, name, type, flags, link, info, align, entsize) { + if (isa == kMips || isa == kMips64) { + bool fpu32 = false; // assume mips64 values + uint8_t isa_rev = 6; // assume mips64 values + if (isa == kMips) { + // adjust for mips32 values + fpu32 = features->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint(); + isa_rev = features->AsMipsInstructionSetFeatures()->IsR6() + ? 6 + : features->AsMipsInstructionSetFeatures()->IsMipsIsaRevGreaterThanEqual2() + ? (fpu32 ? 2 : 5) + : 1; + } + abiflags_.version = 0; // version of flags structure + abiflags_.isa_level = (isa == kMips) ? 32 : 64; + abiflags_.isa_rev = isa_rev; + abiflags_.gpr_size = (isa == kMips) ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64; + abiflags_.cpr1_size = fpu32 ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64; + abiflags_.cpr2_size = MIPS_AFL_REG_NONE; + // Set the fp_abi to MIPS_ABI_FP_64A for mips32 with 64-bit FPUs (ie: mips32 R5 and R6). + // Otherwise set to MIPS_ABI_FP_DOUBLE. + abiflags_.fp_abi = (isa == kMips && !fpu32) ? MIPS_ABI_FP_64A : MIPS_ABI_FP_DOUBLE; + abiflags_.isa_ext = 0; + abiflags_.ases = 0; + // To keep the code simple, we are not using odd FP reg for single floats for both + // mips32 and mips64 ART. Therefore we are not setting the MIPS_AFL_FLAGS1_ODDSPREG bit. + abiflags_.flags1 = 0; + abiflags_.flags2 = 0; + } + } + + Elf_Word GetSize() const { + return sizeof(abiflags_); } 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(); + this->WriteFully(&abiflags_, sizeof(abiflags_)); } private: - std::vector symbols_; + struct { + uint16_t version; // version of this structure + uint8_t isa_level, isa_rev, gpr_size, cpr1_size, cpr2_size; + uint8_t fp_abi; + uint32_t isa_ext, ases, flags1, flags2; + } abiflags_; }; - ElfBuilder(InstructionSet isa, OutputStream* output) + ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output) : isa_(isa), + features_(features), stream_(output), rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), @@ -308,13 +477,18 @@ class ElfBuilder FINAL { debug_info_(this, ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0), debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0), shstrtab_(this, ".shstrtab", 0, 1), + abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0, + isa, features), 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; dynamic_.phdr_flags_ = PF_R | PF_W; dynamic_.phdr_type_ = PT_DYNAMIC; eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME; + abiflags_.phdr_type_ = PT_MIPS_ABIFLAGS; } ~ElfBuilder() {} @@ -380,6 +554,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(""); @@ -408,7 +590,7 @@ class ElfBuilder FINAL { stream_.Flush(); // The main ELF header. - Elf_Ehdr elf_header = MakeElfHeader(isa_); + Elf_Ehdr elf_header = MakeElfHeader(isa_, features_); elf_header.e_shoff = section_headers_offset; elf_header.e_shnum = shdrs.size(); elf_header.e_shstrndx = shstrtab_.GetSectionIndex(); @@ -434,45 +616,63 @@ 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(kPageSize)); + DCHECK_EQ(text_.header_.sh_addralign, static_cast(kPageSize)); + DCHECK_EQ(bss_.header_.sh_addralign, static_cast(kPageSize)); + DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast(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 abiflags_address = RoundUp(bss_address + bss_size, kPageSize); + Elf_Word abiflags_size = 0; + if (isa_ == kMips || isa_ == kMips64) { + abiflags_size = abiflags_.GetSize(); + } + Elf_Word dynstr_address = RoundUp(abiflags_address + abiflags_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 hash; hash.push_back(1); // Number of buckets. hash.push_back(count); // Number of chains. @@ -484,21 +684,50 @@ 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(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_; + } + + void WriteMIPSabiflagsSection() { + abiflags_.Start(); + abiflags_.Write(); + abiflags_.End(); } // Returns true if all writes and seeks on the output stream succeeded. @@ -520,7 +749,7 @@ class ElfBuilder FINAL { } private: - static Elf_Ehdr MakeElfHeader(InstructionSet isa) { + static Elf_Ehdr MakeElfHeader(InstructionSet isa, const InstructionSetFeatures* features) { Elf_Ehdr elf_header = Elf_Ehdr(); switch (isa) { case kArm: @@ -548,18 +777,20 @@ class ElfBuilder FINAL { case kMips: { elf_header.e_machine = EM_MIPS; elf_header.e_flags = (EF_MIPS_NOREORDER | - EF_MIPS_PIC | - EF_MIPS_CPIC | - EF_MIPS_ABI_O32 | - EF_MIPS_ARCH_32R2); + EF_MIPS_PIC | + EF_MIPS_CPIC | + EF_MIPS_ABI_O32 | + features->AsMipsInstructionSetFeatures()->IsR6() + ? EF_MIPS_ARCH_32R6 + : EF_MIPS_ARCH_32R2); break; } case kMips64: { elf_header.e_machine = EM_MIPS; elf_header.e_flags = (EF_MIPS_NOREORDER | - EF_MIPS_PIC | - EF_MIPS_CPIC | - EF_MIPS_ARCH_64R6); + EF_MIPS_PIC | + EF_MIPS_CPIC | + EF_MIPS_ARCH_64R6); break; } case kNone: { @@ -670,16 +901,17 @@ class ElfBuilder FINAL { } InstructionSet isa_; + const InstructionSetFeatures* features_; ErrorDelayingOutputStream stream_; 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_; @@ -688,18 +920,21 @@ class ElfBuilder FINAL { Section debug_info_; Section debug_line_; StringSection shstrtab_; + AbiflagsSection abiflags_; std::vector> other_sections_; // List of used section in the order in which they were written. std::vector 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 d50a08cb206ef380add8759b40efd18e1d432be3..c9ea0083d52439532d805f9ddbc7dd1f10e5eace 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& method_infos) = 0; + virtual void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) = 0; + virtual void PrepareDebugInfo(const ArrayRef& 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& method_infos) = 0; virtual void WritePatchLocations(const ArrayRef& 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 1d71e572d7a3c6ffb449ebdd86c7dc6fb7a65525..bed864b5348953bf98333de72d7c79a90f3647cc 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -51,10 +51,12 @@ constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT; class DebugInfoTask : public Task { public: DebugInfoTask(InstructionSet isa, + const InstructionSetFeatures* features, size_t rodata_section_size, size_t text_section_size, const ArrayRef& method_infos) : isa_(isa), + instruction_set_features_(features), rodata_section_size_(rodata_section_size), text_section_size_(text_section_size), method_infos_(method_infos) { @@ -62,6 +64,7 @@ class DebugInfoTask : public Task { void Run(Thread*) { result_ = debug::MakeMiniDebugInfo(isa_, + instruction_set_features_, rodata_section_size_, text_section_size_, method_infos_); @@ -73,6 +76,7 @@ class DebugInfoTask : public Task { private: InstructionSet isa_; + const InstructionSetFeatures* instruction_set_features_; size_t rodata_section_size_; size_t text_section_size_; const ArrayRef& method_infos_; @@ -83,19 +87,18 @@ template class ElfWriterQuick FINAL : public ElfWriter { public: ElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file); ~ElfWriterQuick(); void Start() OVERRIDE; - void PrepareDebugInfo(size_t rodata_section_size, - size_t text_section_size, - const ArrayRef& method_infos) OVERRIDE; + void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) OVERRIDE; + void PrepareDebugInfo(const ArrayRef& 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& method_infos) OVERRIDE; void WritePatchLocations(const ArrayRef& patch_locations) OVERRIDE; @@ -103,12 +106,18 @@ class ElfWriterQuick FINAL : public ElfWriter { virtual OutputStream* GetStream() OVERRIDE; + size_t GetLoadedSize() OVERRIDE; + static void EncodeOatPatches(const std::vector& locations, std::vector* buffer); private: + const InstructionSetFeatures* instruction_set_features_; const CompilerOptions* const compiler_options_; File* const elf_file_; + size_t rodata_size_; + size_t text_size_; + size_t bss_size_; std::unique_ptr output_stream_; std::unique_ptr> builder_; std::unique_ptr debug_info_task_; @@ -118,24 +127,36 @@ class ElfWriterQuick FINAL : public ElfWriter { }; std::unique_ptr CreateElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file) { if (Is64BitInstructionSet(instruction_set)) { - return MakeUnique>(instruction_set, compiler_options, elf_file); + return MakeUnique>(instruction_set, + features, + compiler_options, + elf_file); } else { - return MakeUnique>(instruction_set, compiler_options, elf_file); + return MakeUnique>(instruction_set, + features, + compiler_options, + elf_file); } } template ElfWriterQuick::ElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file) : ElfWriter(), + instruction_set_features_(features), compiler_options_(compiler_options), elf_file_(elf_file), + rodata_size_(0u), + text_size_(0u), + bss_size_(0u), output_stream_(MakeUnique(MakeUnique(elf_file))), - builder_(new ElfBuilder(instruction_set, output_stream_.get())) {} + builder_(new ElfBuilder(instruction_set, features, output_stream_.get())) {} template ElfWriterQuick::~ElfWriterQuick() {} @@ -145,6 +166,19 @@ void ElfWriterQuick::Start() { builder_->Start(); } +template +void ElfWriterQuick::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 OutputStream* ElfWriterQuick::StartRoData() { auto* rodata = builder_->GetRoData(); @@ -171,31 +205,28 @@ void ElfWriterQuick::EndText(OutputStream* text) { builder_->GetText()->End(); } -template -void ElfWriterQuick::SetBssSize(size_t bss_size) { - auto* bss = builder_->GetBss(); - if (bss_size != 0u) { - bss->WriteNoBitsSection(bss_size); - } -} - template void ElfWriterQuick::WriteDynamicSection() { - builder_->WriteDynamicSection(elf_file_->GetPath()); + if (bss_size_ != 0u) { + builder_->GetBss()->WriteNoBitsSection(bss_size_); + } + if (builder_->GetIsa() == kMips || builder_->GetIsa() == kMips64) { + builder_->WriteMIPSabiflagsSection(); + } + builder_->WriteDynamicSection(); } template void ElfWriterQuick::PrepareDebugInfo( - size_t rodata_section_size, - size_t text_section_size, const ArrayRef& 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( new DebugInfoTask(builder_->GetIsa(), - rodata_section_size, - text_section_size, + instruction_set_features_, + rodata_size_, + text_size_, method_infos)); debug_info_thread_pool_ = std::unique_ptr( new ThreadPool("Mini-debug-info writer", 1)); @@ -245,6 +276,11 @@ OutputStream* ElfWriterQuick::GetStream() { return builder_->GetStream(); } +template +size_t ElfWriterQuick::GetLoadedSize() { + return builder_->GetLoadedSize(); +} + // Explicit instantiations template class ElfWriterQuick; template class ElfWriterQuick; diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h index 347d372fe2f6f6f404ef0e339ca39d472d7f036f..3d5dd39a66983c2c9685d59d7f124f548aa84c9b 100644 --- a/compiler/elf_writer_quick.h +++ b/compiler/elf_writer_quick.h @@ -26,8 +26,10 @@ namespace art { class CompilerOptions; +class InstructionSetFeatures; std::unique_ptr CreateElfWriterQuick(InstructionSet instruction_set, + const InstructionSetFeatures* features, const CompilerOptions* compiler_options, File* elf_file); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 6214deb979d1ad980731e86ddcfc2e190d2ab535..7779e4451933d65462867fc6b2c76cbc684bb936 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -24,10 +24,12 @@ #include "class_linker-inl.h" #include "common_compiler_test.h" #include "debug/method_debug_info.h" +#include "driver/compiler_options.h" #include "elf_writer.h" #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" @@ -47,8 +49,12 @@ class ImageTest : public CommonCompilerTest { }; void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { - // TODO: Test does not currently work with optimizing. - CreateCompilerDriver(Compiler::kQuick, kRuntimeISA); + CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); + + // Set inline filter values. + compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit); + compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // Enable write for dex2dex. for (const DexFile* dex_file : class_linker->GetBootClassPath()) { @@ -72,10 +78,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 dex_file_to_oat_filename_map; + std::unordered_map dex_file_to_oat_index_map; std::vector 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 writer(new ImageWriter(*compiler_driver_, requested_image_base, @@ -83,7 +89,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. { { @@ -98,6 +104,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { const std::vector& dex_files = class_linker->GetBootClassPath(); std::unique_ptr elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), &compiler_driver_->GetCompilerOptions(), oat_file.GetFile()); elf_writer->Start(); @@ -123,10 +130,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 +158,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 +177,10 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { std::vector 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 " @@ -271,14 +288,17 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { } TEST_F(ImageTest, WriteReadUncompressed) { + TEST_DISABLED_FOR_READ_BARRIER(); // b/27578460 TestWriteRead(ImageHeader::kStorageModeUncompressed); } TEST_F(ImageTest, WriteReadLZ4) { + TEST_DISABLED_FOR_READ_BARRIER(); // b/27578460 TestWriteRead(ImageHeader::kStorageModeLZ4); } TEST_F(ImageTest, WriteReadLZ4HC) { + TEST_DISABLED_FOR_READ_BARRIER(); // b/27578460 TestWriteRead(ImageHeader::kStorageModeLZ4HC); } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 9d793c89910a05c2ae0b80a52731113646c54fce..b1b971f6bae0d0c7aa07dfeea8ed688040613b9e 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -163,9 +163,7 @@ bool ImageWriter::PrepareImageAddressSpace() { bool ImageWriter::Write(int image_fd, const std::vector& image_filenames, - int oat_fd, - const std::vector& oat_filenames, - const std::string& oat_location) { + const std::vector& 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()); @@ -173,95 +171,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 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(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); } } @@ -274,8 +190,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 image_file; if (image_fd != kInvalidFd) { if (strlen(image_filename) == 0u) { @@ -425,8 +340,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); @@ -446,8 +361,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; } @@ -490,8 +405,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); @@ -510,8 +425,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(), @@ -533,9 +448,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 }); } } @@ -650,8 +565,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). @@ -687,8 +602,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"; } @@ -704,16 +619,15 @@ ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const DCHECK_LE(offset, std::numeric_limits::max()); BinSlot bin_slot(static_cast(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), @@ -1006,8 +920,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(); @@ -1034,8 +947,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)) { @@ -1064,7 +977,7 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) { AssignImageBinSlot(obj); } -ObjectArray* ImageWriter::CreateImageRoots(const char* oat_filename) const { +ObjectArray* ImageWriter::CreateImageRoots(size_t oat_index) const { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); Thread* self = Thread::Current(); @@ -1073,10 +986,10 @@ ObjectArray* ImageWriter::CreateImageRoots(const char* oat_filename) con class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); std::unordered_set 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); } } @@ -1201,8 +1114,8 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { LengthPrefixedArray* 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. @@ -1220,8 +1133,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) { @@ -1231,8 +1147,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); } } @@ -1265,13 +1182,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; } @@ -1299,14 +1216,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_); } @@ -1341,9 +1258,8 @@ void ImageWriter::CalculateNewObjectOffsets() { Thread* const self = Thread::Current(); StackHandleScopeCollection handles(self); std::vector>> 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(); @@ -1369,12 +1285,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::ComputeSize( @@ -1386,15 +1302,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"; @@ -1405,8 +1320,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; @@ -1426,8 +1340,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]; @@ -1444,8 +1357,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++; } @@ -1454,7 +1366,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]; } @@ -1503,15 +1415,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(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]; @@ -1522,7 +1430,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(idx) << " " << section; @@ -1551,7 +1459,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), @@ -1570,8 +1478,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(image_info.image_begin_ + it->second.offset); } @@ -1600,14 +1508,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; @@ -1653,7 +1560,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)) { @@ -1758,7 +1665,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; } } @@ -1771,8 +1678,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(image_info.image_->Begin() + offset); DCHECK_LT(offset, image_info.image_end_); const auto* src = reinterpret_cast(obj); @@ -1864,7 +1771,7 @@ T* ImageWriter::NativeLocationInImage(T* obj) { CHECK(it != native_object_relocations_.end()) << obj << " spaces " << Runtime::Current()->GetHeap()->DumpSpaces(); const NativeObjectRelocation& relocation = it->second; - ImageInfo& image_info = GetImageInfo(relocation.oat_filename); + ImageInfo& image_info = GetImageInfo(relocation.oat_index); return reinterpret_cast(image_info.image_begin_ + relocation.offset); } } @@ -1874,8 +1781,8 @@ 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(image_info.image_->Begin() + NativeOffsetInImage(obj)); } } @@ -2168,34 +2075,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(elf->Begin() + data_sec_offset); -} - -void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) { - std::string error_msg; - std::unique_ptr 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(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], @@ -2226,19 +2105,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(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(last_image_end) + image_info.oat_offset_; -} - ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) { switch (type) { case kNativeObjectRelocationTypeArtField: @@ -2256,91 +2122,110 @@ 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 oat_filenames, - const std::unordered_map& dex_file_oat_filename_map) + const std::vector& oat_filenames, + const std::unordered_map& dex_file_oat_index_map) : compiler_driver_(compiler_driver), global_image_begin_(reinterpret_cast(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"; diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 1da56df89ae08ec2fbc7bcf9cc2021ea91746c06..f204b28380400411f51368b01dcb5f6970af77bb 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -27,6 +27,7 @@ #include #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 oat_filenames, - const std::unordered_map& dex_file_oat_filename_map); + const std::vector& oat_filenames, + const std::unordered_map& 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 @@ -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(image_info.image_begin_ + GetImageOffset(object)); } } @@ -91,9 +91,9 @@ class ImageWriter FINAL { template 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( @@ -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& image_filenames, - int oat_fd, - const std::vector& oat_filenames, - const std::string& oat_location) + const std::vector& oat_filenames) REQUIRES(!Locks::mutator_lock_); - uintptr_t GetOatDataBegin(const char* oat_filename) { - return reinterpret_cast(GetImageInfo(oat_filename).oat_data_begin_); + uintptr_t GetOatDataBegin(size_t oat_index) { + return reinterpret_cast(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 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(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* CreateImageRoots(const char* oat_filename) const + mirror::ObjectArray* 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,7 +421,7 @@ 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. @@ -443,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. @@ -465,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 pointer_arrays_; @@ -483,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 image_info_map_; + // Image data indexed by the oat file index. + dchecked_vector 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; @@ -522,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& dex_file_oat_filename_map_; - const std::vector oat_filenames_; - const char* default_oat_filename_; + // The file names of oat files. + const std::vector& oat_filenames_; + + // Map of dex files to the indexes of oat files that they were compiled into. + const std::unordered_map& 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 909d6822a8302e1ae94e12d9f16122376cf639aa..cda2e274ced7533bb49eb7b50f317512966e9825 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -69,7 +69,8 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou DCHECK(jit_compiler != nullptr); if (jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo()) { const ArrayRef types_array(types, count); - ArrayRef elf_file = debug::WriteDebugElfFileForClasses(kRuntimeISA, types_array); + ArrayRef elf_file = debug::WriteDebugElfFileForClasses( + kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array); CreateJITCodeEntry(std::unique_ptr(elf_file.data()), elf_file.size()); } } @@ -85,7 +86,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { exit(EXIT_FAILURE); } -JitCompiler::JitCompiler() : total_time_(0) { +JitCompiler::JitCompiler() { compiler_options_.reset(new CompilerOptions( CompilerOptions::kDefaultCompilerFilter, CompilerOptions::kDefaultHugeMethodThreshold, @@ -163,19 +164,19 @@ 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); compiler_driver_->SetSupportBootImageFixup(false); + size_t thread_count = compiler_driver_->GetThreadCount(); if (compiler_options_->GetGenerateDebugInfo()) { #ifdef __ANDROID__ const char* prefix = "/data/misc/trace"; #else const char* prefix = "/tmp"; #endif - DCHECK_EQ(compiler_driver_->GetThreadCount(), 1u) + DCHECK_EQ(thread_count, 1u) << "Generating debug info only works with one compiler thread"; std::string perf_filename = std::string(prefix) + "/perf-" + std::to_string(getpid()) + ".map"; perf_file_.reset(OS::CreateEmptyFileWriteOnly(perf_filename.c_str())); @@ -184,6 +185,10 @@ JitCompiler::JitCompiler() : total_time_(0) { " Are you on a user build? Perf only works on userdebug/eng builds"; } } + + size_t inline_depth_limit = compiler_driver_->GetCompilerOptions().GetInlineDepthLimit(); + DCHECK_LT(thread_count * inline_depth_limit, std::numeric_limits::max()) + << "ProfilingInfo's inline counter can potentially overflow"; } JitCompiler::~JitCompiler() { @@ -196,7 +201,6 @@ JitCompiler::~JitCompiler() { bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { DCHECK(!method->IsProxyMethod()); TimingLogger logger("JIT compiler timing logger", true, VLOG_IS_ON(jit)); - const uint64_t start_time = NanoTime(); StackHandleScope<2> hs(self); self->AssertNoPendingException(); Runtime* runtime = Runtime::Current(); @@ -231,13 +235,12 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { } // Trim maps to reduce memory usage. - // TODO: measure how much this increases compile time. + // TODO: move this to an idle phase. { TimingLogger::ScopedTiming t2("TrimMaps", &logger); - runtime->GetArenaPool()->TrimMaps(); + runtime->GetJitArenaPool()->TrimMaps(); } - total_time_ += NanoTime() - start_time; runtime->GetJit()->AddTimingLogger(logger); return success; } diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index 5294d0ee35d18400b54a0d1bd51f60cc6d39d3e9..533dccf2163afcfa54a8e49966a462a0e8302288 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -18,13 +18,10 @@ #define ART_COMPILER_JIT_JIT_COMPILER_H_ #include "base/mutex.h" -#include "compiler_callbacks.h" #include "compiled_method.h" -#include "dex/verification_results.h" #include "dex/quick/dex_file_to_method_inliner_map.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" -#include "oat_file.h" namespace art { @@ -37,23 +34,22 @@ class JitCompiler { public: static JitCompiler* Create(); virtual ~JitCompiler(); + + // Compilation entrypoint. Returns whether the compilation succeeded. bool CompileMethod(Thread* self, ArtMethod* method, bool osr) SHARED_REQUIRES(Locks::mutator_lock_); - CompilerCallbacks* GetCompilerCallbacks() const; - size_t GetTotalCompileTime() const { - return total_time_; - } + CompilerOptions* GetCompilerOptions() const { return compiler_options_.get(); } + CompilerDriver* GetCompilerDriver() const { + return compiler_driver_.get(); + } private: - uint64_t total_time_; std::unique_ptr compiler_options_; std::unique_ptr cumulative_logger_; - std::unique_ptr verification_results_; std::unique_ptr method_inliner_map_; - std::unique_ptr callbacks_; std::unique_ptr compiler_driver_; std::unique_ptr instruction_set_features_; std::unique_ptr perf_file_; @@ -62,8 +58,7 @@ class JitCompiler { // This is in the compiler since the runtime doesn't have access to the compiled method // structures. - bool AddToCodeCache(ArtMethod* method, - const CompiledMethod* compiled_method) + bool AddToCodeCache(ArtMethod* method, const CompiledMethod* compiled_method) SHARED_REQUIRES(Locks::mutator_lock_); DISALLOW_COPY_AND_ASSIGN(JitCompiler); diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index 73b0facf4b0c1aeeb1aba33d75eccb4fc54df8e0..682b008219d05886d8e81943a4d0402a11fd9648 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 f80dd962cee303a27d9ff7202b5481e9059fadc4..25fd35e1d6efc76c00aced194f7169d1beacf131 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 thunk_code, - uint32_t max_positive_displacement, uint32_t max_negative_displacement); + InstructionSet instruction_set, + std::vector 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 5f4f760c148c40956d74445e1561ef3cccff3337..c090dffc55251be5f32fefe589af0a95dbee9cbc 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* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void Thumb2RelativePatcher::PatchCall(std::vector* 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 006d6fb9d529adadcdf0cd1401e5974d503efb20..0d903c0b410d21c9109440890c68e1707924327f 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* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; - void PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; private: static std::vector CompileThunkCode(); diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 3d4c2184f1b4d3fcb720a785de6477bd3033cad8..a81c85c707325fb97c614a849c3b9b8836345df8 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* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void Arm64RelativePatcher::PatchCall(std::vector* 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 2d07e75c85d3c5d8b6aa56be3a0e70e2542f3431..f9b76e62505c52f6b5889a91ae2426c5a5ff02e0 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* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; - void PatchDexCacheReference(std::vector* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector* code, + uint32_t literal_offset, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* code, + const LinkerPatch& patch, + uint32_t patch_offset, + uint32_t target_offset) OVERRIDE; private: static std::vector CompileThunkCode(); diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc new file mode 100644 index 0000000000000000000000000000000000000000..e9e242b658890e716e9f3ce3ff0cedbc4d9eb419 --- /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 MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset( + MethodReference ref) { + auto it = map.find(ref); + if (it == map.end()) { + return std::pair(false, 0u); + } else { + return std::pair(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 0000000000000000000000000000000000000000..1727d529fc766158f1f86ba9fe418ec7423666c6 --- /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::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* 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* 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 FindMethodOffset(MethodReference ref) OVERRIDE; + SafeMap map; + }; + + MethodOffsetMap method_offset_map_; + std::unique_ptr 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 0000000000000000000000000000000000000000..792cdfe8e9439c376d8cc7e65519d86f94f1606f --- /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 thunk(next_write_call_thunk_, 'c'); + bool success = WriteRelCallThunk(out, ArrayRef(thunk)); + CHECK(success); + next_write_call_thunk_ = 0u; + } + if (next_write_misc_thunk_ != 0u) { + offset += next_write_misc_thunk_; + std::vector thunk(next_write_misc_thunk_, 'm'); + bool success = WriteMiscThunk(out, ArrayRef(thunk)); + CHECK(success); + next_write_misc_thunk_ = 0u; + } + return offset; + } + + void PatchCall(std::vector* 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* 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 mock(new MockPatcher()); + mock_ = mock.get(); + patcher_.relative_patcher_ = std::move(mock); + } + + std::unique_ptr instruction_set_features_; + MultiOatRelativePatcher patcher_; + MockPatcher* mock_; +}; + +TEST_F(MultiOatRelativePatcherTest, Offsets) { + const DexFile* dex_file = reinterpret_cast(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(1); + MethodReference ref1(dex_file, 1u); + MethodReference ref2(dex_file, 2u); + MethodReference ref3(dex_file, 3u); + const CompiledMethod* method = reinterpret_cast(-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 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 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 82702dcf251637b764aec9eb07e811a5c99c5ecd..6727c1758376c7c228aa504438988c992793923c 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::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 8a9f3f8364e7807fe447ccfe6ff88d0cd298cfe1..ba374512a17190d39139f0e62b203ce438bae587 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* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) = 0; + virtual void PatchCall(std::vector* 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* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) = 0; + virtual void PatchDexCacheReference(std::vector* 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 bf8e786f64d27edd46bd0c22c1adb51a99b4075d..704135a7b58553afa2b0c85693a2782cefbdf209 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 0c881f00ba90fbe26b186d5e0b9b34ff1079b515..ddc244c26964094a6a1b9e7f79f28f2ae7e35c2e 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* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* 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 bc285a78494095a8a3b56bd938a7c8c88abb7759..bf3a6482189de05e991c3c768d87b965f1b3092c 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* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) { +void X86BaseRelativePatcher::PatchCall(std::vector* 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 92007093981bc0aa4785723cf29969d8d744672c..ca83a72f48a08eb26239463d997ee5fd5a30852f 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* code, uint32_t literal_offset, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchCall(std::vector* 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 598f3ac4a886a09adc2e444839c2b7cd59c7bbe6..e571f50d2fdd587c0ed80947deb205e96b76dff5 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* 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 af687b4a2f4b0161891f98ff110d2e62bd912261..feecb3a2ad40f5bc8545d54732d2d1a67c5ad8f3 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* code, const LinkerPatch& patch, - uint32_t patch_offset, uint32_t target_offset) OVERRIDE; + void PatchDexCacheReference(std::vector* 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 fead839263a42e09a6a7644004304f3ad3214def..d22044aca39dda6a241c85edff489a84d22da541 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, @@ -176,6 +176,7 @@ class OatTest : public CommonCompilerTest { bool verify) { std::unique_ptr elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), &compiler_driver_->GetCompilerOptions(), file); elf_writer->Start(); @@ -200,7 +201,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 +223,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()); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 47dcfd56f8714a14b202e505f37f6e0f6e3cd90b..c2f19c9d61604fd7ac7593964c98b465d331f5ac 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& dex_files) { + const std::vector& 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); } } @@ -807,20 +806,29 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } } - if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) { + const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions(); + // Exclude quickened dex methods (code_size == 0) since they have no native code. + if (compiler_options.GenerateAnyDebugInfo() && code_size != 0) { + bool has_code_info = method_header->IsOptimized(); // Record debug information for this function if we are doing that. - const uint32_t quick_code_start = quick_code_offset - - writer_->oat_header_->GetExecutableOffset() - thumb_offset; - writer_->method_info_.push_back(debug::MethodDebugInfo { - dex_file_, - class_def_index_, - it.GetMemberIndex(), - it.GetMethodAccessFlags(), - it.GetMethodCodeItem(), - deduped, - quick_code_start, - quick_code_start + code_size, - compiled_method}); + debug::MethodDebugInfo info = debug::MethodDebugInfo(); + info.trampoline_name = nullptr; + info.dex_file = dex_file_; + info.class_def_index = class_def_index_; + info.dex_method_index = it.GetMemberIndex(); + info.access_flags = it.GetMethodAccessFlags(); + info.code_item = it.GetMethodCodeItem(); + info.isa = compiled_method->GetInstructionSet(); + info.deduped = deduped; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = method_header->IsOptimized(); + info.is_code_address_text_relative = true; + info.code_address = code_offset - writer_->oat_header_->GetExecutableOffset(); + info.code_size = code_size; + info.frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); + info.code_info = has_code_info ? compiled_method->GetVmapTable().data() : nullptr; + info.cfi = compiled_method->GetCFIInfo(); + writer_->method_info_.push_back(info); } if (kIsDebugBuild) { @@ -1106,27 +1114,29 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { patched_code_.assign(quick_code.begin(), quick_code.end()); quick_code = ArrayRef(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 +1182,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 +1216,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( - 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( + 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 +1279,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( + writer_->image_writer_->GetOatDataBegin(oat_index) + + static_cast(target_offset)); + address = PointerToLowMemUInt32(target); } DCHECK_LE(offset + 4, code->size()); uint8_t* data = &(*code)[offset]; @@ -1540,6 +1553,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 +1796,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(-1)) { @@ -1833,7 +1848,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 +2276,15 @@ bool OatWriter::WriteData(OutputStream* out, const void* data, size_t size) { return out->WriteFully(data, size); } -std::pair OatWriter::MethodOffsetMap::FindMethodOffset(MethodReference ref) { - auto it = map.find(ref); - if (it == map.end()) { - return std::pair(false, 0u); - } else { - return std::pair(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 5a55fc6c958fda7d828f2363e5ec1927b50430b3..5e7a4a37d19a887d1082637e2facb55d9c1a3e68 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& dex_files); + const std::vector& 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,12 +192,20 @@ class OatWriter { return bss_size_; } + size_t GetOatDataOffset() const { + return oat_data_offset_; + } + ArrayRef GetAbsolutePatchLocations() const { return ArrayRef(absolute_patch_locations_); } ~OatWriter(); + void AddMethodDebugInfos(const std::vector& infos) { + method_info_.insert(method_info_.end(), infos.begin(), infos.end()); + } + ArrayRef GetMethodDebugInfo() const { return ArrayRef(method_info_); } @@ -249,7 +262,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 +281,7 @@ class OatWriter { const std::vector>& 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 +372,12 @@ class OatWriter { uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; - std::unique_ptr 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 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 FindMethodOffset(MethodReference ref) OVERRIDE; - SafeMap 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 ba1b1683d7e9edf4800ba22456f3774035b6cfb3..f2929bcc189448dd10e6c64b400bc4fd32d1d3b0 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. @@ -525,6 +533,8 @@ class BCEVisitor : public HGraphVisitor { first_index_bounds_check_map_( std::less(), graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)), + dynamic_bce_standby_( + graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)), early_exit_loop_( std::less(), graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)), @@ -545,6 +555,13 @@ class BCEVisitor : public HGraphVisitor { } void Finish() { + // Retry dynamic bce candidates on standby that are still in the graph. + for (HBoundsCheck* bounds_check : dynamic_bce_standby_) { + if (bounds_check->IsInBlock()) { + TryDynamicBCE(bounds_check); + } + } + // Preserve SSA structure which may have been broken by adding one or more // new taken-test structures (see TransformLoopForDeoptimizationIfNeeded()). InsertPhiNodes(); @@ -553,6 +570,7 @@ class BCEVisitor : public HGraphVisitor { early_exit_loop_.clear(); taken_test_loop_.clear(); finite_loop_.clear(); + dynamic_bce_standby_.clear(); } private: @@ -1293,7 +1311,7 @@ class BCEVisitor : public HGraphVisitor { if (DynamicBCESeemsProfitable(loop, instruction->GetBlock()) && induction_range_.CanGenerateCode( instruction, index, &needs_finite_test, &needs_taken_test) && - CanHandleInfiniteLoop(loop, index, needs_finite_test) && + CanHandleInfiniteLoop(loop, instruction, index, needs_finite_test) && CanHandleLength(loop, length, needs_taken_test)) { // do this test last (may code gen) HInstruction* lower = nullptr; HInstruction* upper = nullptr; @@ -1425,7 +1443,7 @@ class BCEVisitor : public HGraphVisitor { * ensure the loop is finite. */ bool CanHandleInfiniteLoop( - HLoopInformation* loop, HInstruction* index, bool needs_infinite_test) { + HLoopInformation* loop, HBoundsCheck* check, HInstruction* index, bool needs_infinite_test) { if (needs_infinite_test) { // If we already forced the loop to be finite, allow directly. const uint32_t loop_id = loop->GetHeader()->GetBlockId(); @@ -1447,6 +1465,9 @@ class BCEVisitor : public HGraphVisitor { } } } + // If bounds check made it this far, it is worthwhile to check later if + // the loop was forced finite by another candidate. + dynamic_bce_standby_.push_back(check); return false; } return true; @@ -1668,6 +1689,9 @@ class BCEVisitor : public HGraphVisitor { // in a block that checks an index against that HArrayLength. ArenaSafeMap first_index_bounds_check_map_; + // Stand by list for dynamic bce. + ArenaVector dynamic_bce_standby_; + // Early-exit loop bookkeeping. ArenaSafeMap early_exit_loop_; @@ -1703,21 +1727,18 @@ void BoundsCheckElimination::Run() { // that value dominated by that instruction fits in that range. Range of that // value can be narrowed further down in the dominator tree. BCEVisitor visitor(graph_, side_effects_, induction_analysis_); - HBasicBlock* last_visited_block = nullptr; for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { HBasicBlock* current = it.Current(); - if (current == last_visited_block) { - // We may insert blocks into the reverse post order list when processing - // a loop header. Don't process it again. - DCHECK(current->IsLoopHeader()); - continue; - } if (visitor.IsAddedBlock(current)) { // Skip added blocks. Their effects are already taken care of. continue; } visitor.VisitBasicBlock(current); - last_visited_block = current; + // Skip forward to the current block in case new basic blocks were inserted + // (which always appear earlier in reverse post order) to avoid visiting the + // same basic block twice. + for ( ; !it.Done() && it.Current() != current; it.Advance()) { + } } // Perform cleanup. diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 05e1356ed8202a79d8d277b0d000fa4773a9c28a..57660c2623342968f3dd61dbe2c56411412a883e 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -282,7 +282,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Found a predecessor not covered by the same TryItem. Insert entering // boundary block. HTryBoundary* try_entry = - new (arena_) HTryBoundary(HTryBoundary::kEntry, try_block->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); LinkToCatchBlocks(try_entry, code_item, entry.second); break; @@ -316,7 +316,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // Insert TryBoundary and link to catch blocks. HTryBoundary* try_exit = - new (arena_) HTryBoundary(HTryBoundary::kExit, successor->GetDexPc()); + new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); LinkToCatchBlocks(try_exit, code_item, entry.second); } @@ -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) { @@ -2852,7 +2843,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_ENTER: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kEnter, + HMonitorOperation::OperationKind::kEnter, dex_pc)); break; } @@ -2860,7 +2851,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MONITOR_EXIT: { current_block_->AddInstruction(new (arena_) HMonitorOperation( LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc), - HMonitorOperation::kExit, + HMonitorOperation::OperationKind::kExit, dex_pc)); break; } diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index c2c8ccfc56fd680571229ea5743e02af91bb7833..af50363e3160c5002a6bd2216147bda97095e05d 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); @@ -537,8 +543,16 @@ void CodeGenerator::AllocateLocations(HInstruction* instruction) { DCHECK(CheckTypeConsistency(instruction)); LocationSummary* locations = instruction->GetLocations(); if (!instruction->IsSuspendCheckEntry()) { - if (locations != nullptr && locations->CanCall()) { - MarkNotLeaf(); + if (locations != nullptr) { + if (locations->CanCall()) { + MarkNotLeaf(); + } else if (locations->Intrinsified() && + instruction->IsInvokeStaticOrDirect() && + !instruction->AsInvokeStaticOrDirect()->HasCurrentMethodInput()) { + // A static method call that has been fully intrinsified, and cannot call on the slow + // path or refer to the current method directly, no longer needs current method. + return; + } } if (instruction->NeedsCurrentMethod()) { SetRequiresCurrentMethod(); @@ -733,7 +747,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 +823,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 49c193e7bfa483c9726c10d56d1c3c9f715a8626..9297fc956f7352ee108bb2247bfeed7d40ca6ce4 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -69,7 +69,7 @@ class CodeAllocator { class SlowPathCode : public ArenaObject { 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 { 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 87f52c6f218399cc29755ceed6c5dd8352318f70..0b7fefafdd93e83dcd3a40292657e4e09beaf435 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(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(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(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(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(); @@ -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(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) { @@ -5740,6 +5727,71 @@ void InstructionCodeGeneratorARM::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); } + +void LocationsBuilderARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + DCHECK(instruction->GetResultType() == Primitive::kPrimInt + || instruction->GetResultType() == Primitive::kPrimLong); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + Location out = locations->Out(); + + if (instruction->GetResultType() == Primitive::kPrimInt) { + Register first_reg = first.AsRegister(); + ShifterOperand second_reg(second.AsRegister()); + Register out_reg = out.AsRegister(); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ bic(out_reg, first_reg, second_reg); + break; + case HInstruction::kOr: + __ orn(out_reg, first_reg, second_reg); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + return; + + } else { + DCHECK_EQ(instruction->GetResultType(), Primitive::kPrimLong); + Register first_low = first.AsRegisterPairLow(); + Register first_high = first.AsRegisterPairHigh(); + ShifterOperand second_low(second.AsRegisterPairLow()); + ShifterOperand second_high(second.AsRegisterPairHigh()); + Register out_low = out.AsRegisterPairLow(); + Register out_high = out.AsRegisterPairHigh(); + + switch (instruction->GetOpKind()) { + case HInstruction::kAnd: + __ bic(out_low, first_low, second_low); + __ bic(out_high, first_high, second_high); + break; + case HInstruction::kOr: + __ orn(out_low, first_low, second_low); + __ orn(out_high, first_high, second_high); + break; + // There is no EON on arm. + case HInstruction::kXor: + default: + LOG(FATAL) << "Unexpected instruction " << instruction->DebugName(); + UNREACHABLE(); + } + } +} + void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). if (value == 0xffffffffu) { @@ -6426,6 +6478,33 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m return DeduplicateMethodLiteral(target_method, &call_patches_); } +void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { + LocationSummary* locations = instr->GetLocations(); + Register res = locations->Out().AsRegister(); + Register accumulator = + locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister(); + Register mul_left = + locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister(); + Register mul_right = + locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister(); + + if (instr->GetOpKind() == HInstruction::kAdd) { + __ mla(res, mul_left, mul_right, accumulator); + } else { + __ mls(res, mul_left, mul_right, accumulator); + } +} + void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) { // Nothing to do, this should be removed during prepare for register allocator. LOG(FATAL) << "Unreachable"; @@ -6580,7 +6659,7 @@ void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArmPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index cfd7a3bc1478e680f7fe943d331ce5e1b2fa6fec..06e7c0015c1a620ee0a8c515de02f9546a8c767d 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -159,6 +159,7 @@ class LocationsBuilderARM : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -197,6 +198,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -510,6 +512,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 435ae5e9549d8b4edb77dc4b85f8ce7e294524fb..89b9e2c599cbe021a9db72f36078a5f2af3b2894 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(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(); @@ -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(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(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(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_; @@ -1876,6 +1862,35 @@ void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) { HandleBinaryOp(instruction); } +void LocationsBuilderARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instr) { + DCHECK(Primitive::IsIntegralType(instr->GetType())) << instr->GetType(); + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr); + locations->SetInAt(0, Location::RequiresRegister()); + // There is no immediate variant of negated bitwise instructions in AArch64. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instr) { + Register dst = OutputRegister(instr); + Register lhs = InputRegisterAt(instr, 0); + Register rhs = InputRegisterAt(instr, 1); + + switch (instr->GetOpKind()) { + case HInstruction::kAnd: + __ Bic(dst, lhs, rhs); + break; + case HInstruction::kOr: + __ Orn(dst, lhs, rhs); + break; + case HInstruction::kXor: + __ Eon(dst, lhs, rhs); + break; + default: + LOG(FATAL) << "Unreachable"; + } +} + void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( HArm64DataProcWithShifterOp* instruction) { DCHECK(instruction->GetType() == Primitive::kPrimInt || @@ -1973,21 +1988,27 @@ void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress( Operand(InputOperandAt(instruction, 1))); } -void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall); - locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex, - Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); - locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); + HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (instr->GetOpKind() == HInstruction::kSub && + accumulator->IsConstant() && + accumulator->AsConstant()->IsZero()) { + // Don't allocate register for Mneg instruction. + } else { + locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex, + Location::RequiresRegister()); + } + locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister()); + locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister()); locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) { +void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) { Register res = OutputRegister(instr); - Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex); - Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex); - Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex); + Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex); + Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex); // Avoid emitting code that could trigger Cortex A53's erratum 835769. // This fixup should be carried out for all multiply-accumulate instructions: @@ -2007,10 +2028,17 @@ void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyA } if (instr->GetOpKind() == HInstruction::kAdd) { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); __ Madd(res, mul_left, mul_right, accumulator); } else { DCHECK(instr->GetOpKind() == HInstruction::kSub); - __ Msub(res, mul_left, mul_right, accumulator); + HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex); + if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) { + __ Mneg(res, mul_left, mul_right); + } else { + Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex); + __ Msub(res, mul_left, mul_right, accumulator); + } } } @@ -3057,11 +3085,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) { @@ -5031,7 +5059,7 @@ void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kArm64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 360488eb4a33b8435dddd64d4d983a1539f62631..10f1e7f0088b664c6efef9610ed78102a299a033 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_; } @@ -195,6 +196,7 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -284,6 +286,7 @@ class LocationsBuilderARM64 : public HGraphVisitor { FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION) FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION) + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION) #undef DECLARE_VISIT_INSTRUCTION @@ -532,6 +535,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 c500ea4408be05627e6597ad59ac025dd1d9ae19..f3c12efd8d04de68f486ac3b89f9237fc546266c 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(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(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(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(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) { @@ -5258,7 +5245,7 @@ void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kMipsPointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index dd0641c7cad2609cb06ddba1bbc5e01d68424d6f..605c7944210158754315ad77993d9456a949731c 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 e3a44f1c965e5fc3e76c5674d2cdde7d0a55b8c3..c2b84b433570fadc7da5e688d73f22b3fe0f4e3d 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(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(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(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(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 eb7315aa7a535486c3557413df3a4f447093593f..ba9eaff46f10ceb950eaf8d93344e05a87977a15 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 f032f516491da566ae4934ad2baf51a996fbb4d1..6b4a18c6883a6a3550a3a4278197755e61dbb15c 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(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(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(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(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(), - is_div); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86( + instruction, out.AsRegister(), is_div); codegen_->AddSlowPath(slow_path); Register second_reg = second.AsRegister(); @@ -4140,7 +4127,7 @@ void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 63e9b2fc9c8c29dbdd185a6a6e5621ed6d664108..0795f3b5304260d92ae2c7a7dd50cbd51803c197 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 f3c40b109f3c006270721db7568045f7c4da6c10..c132663016f1bb133a2113db59f93c1ace45ef67 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(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(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(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(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(); @@ -4012,7 +3998,7 @@ void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) { void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) { LocationSummary* locations = instruction->GetLocations(); uint32_t method_offset = 0; - if (instruction->GetTableKind() == HClassTableGet::kVTable) { + if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) { method_offset = mirror::Class::EmbeddedVTableEntryOffset( instruction->GetIndex(), kX86_64PointerSize).SizeValue(); } else { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 97f6f84236903083c6be69a8a8b1d543afe064ff..b3d27e194ad30227d6e5266b60b12b352fe70c0a 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/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index c0263e4e5b81dcaac7cf7dcc1ce0076a7dd308d8..4f1e90cd7f44edd2a34c0faf86bff3fc1f5db09f 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -436,6 +436,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit"); } +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) + void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } + + void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE { + StartAttributeStream("kind") << instruction->GetOpKind(); + } +#endif + #ifdef ART_ENABLE_CODEGEN_arm64 void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); @@ -443,10 +453,6 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("shift") << instruction->GetShiftAmount(); } } - - void VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instruction) OVERRIDE { - StartAttributeStream("kind") << instruction->GetOpKind(); - } #endif bool IsPass(const char* name) { diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 37f2d79536ea015b45b9d58e773d77a05279acd7..82a898a9f1edfe7328cde422dc6f8ce51f306ab3 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,14 +546,17 @@ 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. - if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLT)) || - (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGT)))) { + // Rewrite condition i != U into strict end condition i < U or i > U if this end condition + // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict + // condition would be always taken). + if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) || + (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) { cmp = stride_value > 0 ? kCondLT : kCondGT; } // Normalize a linear loop control with a nonzero stride: @@ -561,7 +564,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 +572,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 +615,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 +648,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 +675,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::min() - : std::numeric_limits::min(); - const int64_t max = type == Primitive::kPrimInt - ? std::numeric_limits::max() - : std::numeric_limits::max(); + const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits::min() + : std::numeric_limits::min(); + const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits::max() + : std::numeric_limits::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 +746,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 +763,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 +797,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 84d5d82568bec8aa2bfe8cb5f3323db162bd392c..94d2646aec543848c3c66740a12513d61b59fbfa 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 9566c29adf493c035354b910b4d2dfa6a75bd392..f9b6910acdea53fa7486ca26e6bd3ccf6d7851ed 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(c1) / static_cast(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(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,46 @@ 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)); + // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies + // (e.g. array length == maxint and c == 1 would yield minint). + if (request == kAtLeast) { + if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) { + *value = v_min.b_constant; + return true; + } + } + } + return false; +} + bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const { if (info != nullptr) { if (info->induction_class == HInductionVarAnalysis::kLinear) { @@ -206,12 +270,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 +281,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 +309,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(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(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(value))); } } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) { return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min); @@ -331,29 +394,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 +432,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 +533,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 +564,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 +705,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 +731,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 3cb7b4bfd592adb8a067dbcae808485a03f4982c..0af41560ff33e748a1b65cfd4d0ff10eb9b2c3ab 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 55a654e3018ef4b2424bd10da96dcc46ce75c3ba..c5c33bd9bcf7c8480e2fb5e1f5956d6002cb9008 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::min(); + const int32_t max_value = std::numeric_limits::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::min(); + const int32_t max_value = std::numeric_limits::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 0fa8782fbe3650baa86de605e4ed0a80b2cceca0..d861e39c8bcbe31d8b578ffd16762234a5fe910c 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -28,6 +28,8 @@ #include "driver/dex_compilation_unit.h" #include "instruction_simplifier.h" #include "intrinsics.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" #include "nodes.h" @@ -220,6 +222,33 @@ static uint32_t FindClassIndexIn(mirror::Class* cls, return index; } +class ScopedProfilingInfoInlineUse { + public: + explicit ScopedProfilingInfoInlineUse(ArtMethod* method, Thread* self) + : method_(method), + self_(self), + // Fetch the profiling info ahead of using it. If it's null when fetching, + // we should not call JitCodeCache::DoneInlining. + profiling_info_( + Runtime::Current()->GetJit()->GetCodeCache()->NotifyCompilerUse(method, self)) { + } + + ~ScopedProfilingInfoInlineUse() { + if (profiling_info_ != nullptr) { + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + DCHECK_EQ(profiling_info_, method_->GetProfilingInfo(pointer_size)); + Runtime::Current()->GetJit()->GetCodeCache()->DoneCompilerUse(method_, self_); + } + } + + ProfilingInfo* GetProfilingInfo() const { return profiling_info_; } + + private: + ArtMethod* const method_; + Thread* const self_; + ProfilingInfo* const profiling_info_; +}; + bool HInliner::TryInline(HInvoke* invoke_instruction) { if (invoke_instruction->IsInvokeUnresolved()) { return false; // Don't bother to move further if we know the method is unresolved. @@ -271,30 +300,32 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { // Check if we can use an inline cache. ArtMethod* caller = graph_->GetArtMethod(); - size_t pointer_size = class_linker->GetImagePointerSize(); - // Under JIT, we should always know the caller. - DCHECK(!Runtime::Current()->UseJit() || (caller != nullptr)); - if (caller != nullptr && caller->GetProfilingInfo(pointer_size) != nullptr) { - ProfilingInfo* profiling_info = caller->GetProfilingInfo(pointer_size); - const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()); - if (ic.IsUnitialized()) { - VLOG(compiler) << "Interface or virtual call to " - << PrettyMethod(method_index, caller_dex_file) - << " is not hit and not inlined"; - return false; - } else if (ic.IsMonomorphic()) { - MaybeRecordStat(kMonomorphicCall); - return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic); - } else if (ic.IsPolymorphic()) { - MaybeRecordStat(kPolymorphicCall); - return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic); - } else { - DCHECK(ic.IsMegamorphic()); - VLOG(compiler) << "Interface or virtual call to " - << PrettyMethod(method_index, caller_dex_file) - << " is megamorphic and not inlined"; - MaybeRecordStat(kMegamorphicCall); - return false; + if (Runtime::Current()->UseJit()) { + // Under JIT, we should always know the caller. + DCHECK(caller != nullptr); + ScopedProfilingInfoInlineUse spiis(caller, soa.Self()); + ProfilingInfo* profiling_info = spiis.GetProfilingInfo(); + if (profiling_info != nullptr) { + const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc()); + if (ic.IsUninitialized()) { + VLOG(compiler) << "Interface or virtual call to " + << PrettyMethod(method_index, caller_dex_file) + << " is not hit and not inlined"; + return false; + } else if (ic.IsMonomorphic()) { + MaybeRecordStat(kMonomorphicCall); + return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic); + } else if (ic.IsPolymorphic()) { + MaybeRecordStat(kPolymorphicCall); + return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic); + } else { + DCHECK(ic.IsMegamorphic()); + VLOG(compiler) << "Interface or virtual call to " + << PrettyMethod(method_index, caller_dex_file) + << " is megamorphic and not inlined"; + MaybeRecordStat(kMegamorphicCall); + return false; + } } } @@ -641,7 +672,8 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction, HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet( receiver_class, type, - invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable, + invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable + : HClassTableGet::TableKind::kIMTable, method_index, invoke_instruction->GetDexPc()); @@ -1009,6 +1041,8 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, // at runtime, we change this call as if it was a virtual call. invoke_type = kVirtual; } + + const int32_t caller_instruction_counter = graph_->GetCurrentInstructionId(); HGraph* callee_graph = new (graph_->GetArena()) HGraph( graph_->GetArena(), callee_dex_file, @@ -1018,7 +1052,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, invoke_type, graph_->IsDebuggable(), /* osr */ false, - graph_->GetCurrentInstructionId()); + caller_instruction_counter); callee_graph->SetArtMethod(resolved_method); OptimizingCompilerStats inline_stats; @@ -1218,7 +1252,16 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } number_of_inlined_instructions_ += number_of_instructions; + DCHECK_EQ(caller_instruction_counter, graph_->GetCurrentInstructionId()) + << "No instructions can be added to the outer graph while inner graph is being built"; + + const int32_t callee_instruction_counter = callee_graph->GetCurrentInstructionId(); + graph_->SetCurrentInstructionId(callee_instruction_counter); *return_replacement = callee_graph->InlineInto(graph_, invoke_instruction); + + DCHECK_EQ(callee_instruction_counter, callee_graph->GetCurrentInstructionId()) + << "No instructions can be added to the inner graph during inlining into the outer graph"; + return true; } diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 13d3f752c3a60488d0f8a3a4df7304ddb54a3c5c..049901b8825b420c5e8d6c192bad3140609436ec 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -70,6 +70,10 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) OVERRIDE; void VisitLessThan(HLessThan* condition) OVERRIDE; void VisitLessThanOrEqual(HLessThanOrEqual* condition) OVERRIDE; + void VisitBelow(HBelow* condition) OVERRIDE; + void VisitBelowOrEqual(HBelowOrEqual* condition) OVERRIDE; + void VisitAbove(HAbove* condition) OVERRIDE; + void VisitAboveOrEqual(HAboveOrEqual* condition) OVERRIDE; void VisitDiv(HDiv* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; void VisitNeg(HNeg* instruction) OVERRIDE; @@ -93,6 +97,8 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyStringEquals(HInvoke* invoke); void SimplifyCompare(HInvoke* invoke, bool has_zero_op); void SimplifyIsNaN(HInvoke* invoke); + void SimplifyFP2Int(HInvoke* invoke); + void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; @@ -557,6 +563,36 @@ void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) { block->RemoveInstruction(check); } +static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* arena, HInstruction* cond) { + HInstruction *lhs = cond->InputAt(0); + HInstruction *rhs = cond->InputAt(1); + switch (cond->GetKind()) { + case HInstruction::kEqual: + return new (arena) HEqual(rhs, lhs); + case HInstruction::kNotEqual: + return new (arena) HNotEqual(rhs, lhs); + case HInstruction::kLessThan: + return new (arena) HGreaterThan(rhs, lhs); + case HInstruction::kLessThanOrEqual: + return new (arena) HGreaterThanOrEqual(rhs, lhs); + case HInstruction::kGreaterThan: + return new (arena) HLessThan(rhs, lhs); + case HInstruction::kGreaterThanOrEqual: + return new (arena) HLessThanOrEqual(rhs, lhs); + case HInstruction::kBelow: + return new (arena) HAbove(rhs, lhs); + case HInstruction::kBelowOrEqual: + return new (arena) HAboveOrEqual(rhs, lhs); + case HInstruction::kAbove: + return new (arena) HBelow(rhs, lhs); + case HInstruction::kAboveOrEqual: + return new (arena) HBelowOrEqual(rhs, lhs); + default: + LOG(FATAL) << "Unknown ConditionType " << cond->GetKind(); + } + return nullptr; +} + void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) { HInstruction* input_const = equal->GetConstantRight(); if (input_const != nullptr) { @@ -980,13 +1016,47 @@ void InstructionSimplifierVisitor::VisitLessThanOrEqual(HLessThanOrEqual* condit VisitCondition(condition); } -// TODO: unsigned comparisons too? +void InstructionSimplifierVisitor::VisitBelow(HBelow* condition) { + VisitCondition(condition); +} + +void InstructionSimplifierVisitor::VisitBelowOrEqual(HBelowOrEqual* condition) { + VisitCondition(condition); +} + +void InstructionSimplifierVisitor::VisitAbove(HAbove* condition) { + VisitCondition(condition); +} + +void InstructionSimplifierVisitor::VisitAboveOrEqual(HAboveOrEqual* condition) { + VisitCondition(condition); +} void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) { - // Try to fold an HCompare into this HCondition. + // Reverse condition if left is constant. Our code generators prefer constant + // on the right hand side. + if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) { + HBasicBlock* block = condition->GetBlock(); + HCondition* replacement = GetOppositeConditionSwapOps(block->GetGraph()->GetArena(), condition); + // If it is a fp we must set the opposite bias. + if (replacement != nullptr) { + if (condition->IsLtBias()) { + replacement->SetBias(ComparisonBias::kGtBias); + } else if (condition->IsGtBias()) { + replacement->SetBias(ComparisonBias::kLtBias); + } + block->ReplaceAndRemoveInstructionWith(condition, replacement); + RecordSimplification(); + + condition = replacement; + } + } HInstruction* left = condition->GetLeft(); HInstruction* right = condition->GetRight(); + + // Try to fold an HCompare into this HCondition. + // We can only replace an HCondition which compares a Compare to 0. // Both 'dx' and 'jack' generate a compare to 0 when compiling a // condition with a long, float or double comparison as input. @@ -1562,26 +1632,86 @@ void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) { invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition); } +void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { + DCHECK(invoke->IsInvokeStaticOrDirect()); + uint32_t dex_pc = invoke->GetDexPc(); + HInstruction* x = invoke->InputAt(0); + Primitive::Type type = x->GetType(); + // Set proper bit pattern for NaN and replace intrinsic with raw version. + HInstruction* nan; + if (type == Primitive::kPrimDouble) { + nan = GetGraph()->GetLongConstant(0x7ff8000000000000L); + invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } else { + DCHECK_EQ(type, Primitive::kPrimFloat); + nan = GetGraph()->GetIntConstant(0x7fc00000); + invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits, + kNeedsEnvironmentOrCache, + kNoSideEffects, + kNoThrow); + } + // Test IsNaN(x), which is the same as x != x. + HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc); + condition->SetBias(ComparisonBias::kLtBias); + invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext()); + // Select between the two. + HInstruction* select = new (GetGraph()->GetArena()) HSelect(condition, nan, invoke, dex_pc); + invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext()); + invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 +} + +void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) { + uint32_t dex_pc = invoke->GetDexPc(); + HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier); +} + void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { - if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) { - SimplifyStringEquals(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) { - SimplifySystemArrayCopy(instruction); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateRight || - instruction->GetIntrinsic() == Intrinsics::kLongRotateRight) { - SimplifyRotate(instruction, false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft || - instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) { - SimplifyRotate(instruction, true); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare || - instruction->GetIntrinsic() == Intrinsics::kLongCompare) { - SimplifyCompare(instruction, /* is_signum */ false); - } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum || - instruction->GetIntrinsic() == Intrinsics::kLongSignum) { - SimplifyCompare(instruction, /* is_signum */ true); - } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN || - instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) { - SimplifyIsNaN(instruction); + switch (instruction->GetIntrinsic()) { + case Intrinsics::kStringEquals: + SimplifyStringEquals(instruction); + break; + case Intrinsics::kSystemArrayCopy: + SimplifySystemArrayCopy(instruction); + break; + case Intrinsics::kIntegerRotateRight: + case Intrinsics::kLongRotateRight: + SimplifyRotate(instruction, false); + break; + case Intrinsics::kIntegerRotateLeft: + case Intrinsics::kLongRotateLeft: + SimplifyRotate(instruction, true); + break; + case Intrinsics::kIntegerCompare: + case Intrinsics::kLongCompare: + SimplifyCompare(instruction, /* is_signum */ false); + break; + case Intrinsics::kIntegerSignum: + case Intrinsics::kLongSignum: + SimplifyCompare(instruction, /* is_signum */ true); + break; + case Intrinsics::kFloatIsNaN: + case Intrinsics::kDoubleIsNaN: + SimplifyIsNaN(instruction); + break; + case Intrinsics::kFloatFloatToIntBits: + case Intrinsics::kDoubleDoubleToLongBits: + SimplifyFP2Int(instruction); + break; + case Intrinsics::kUnsafeLoadFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny); + break; + case Intrinsics::kUnsafeStoreFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kAnyStore); + break; + case Intrinsics::kUnsafeFullFence: + SimplifyMemBarrier(instruction, MemBarrierKind::kAnyAny); + break; + default: + break; } } diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc new file mode 100644 index 0000000000000000000000000000000000000000..cd026b877064c35cdc891ddfa50a5233bd8237d8 --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_arm.h" +#include "instruction_simplifier_shared.h" + +namespace art { +namespace arm { + +void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { + if (TryCombineMultiplyAccumulate(instruction, kArm)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } +} + + +} // namespace arm +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h new file mode 100644 index 0000000000000000000000000000000000000000..14c940eb21de2cfac5e21327f248948047e0eee4 --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -0,0 +1,60 @@ +/* + * 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_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { +namespace arm { + +class InstructionSimplifierArmVisitor : public HGraphVisitor { + public: + InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats) + : HGraphVisitor(graph), stats_(stats) {} + + private: + void RecordSimplification() { + if (stats_ != nullptr) { + stats_->RecordStat(kInstructionSimplificationsArch); + } + } + + void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; + void VisitAnd(HAnd* instruction) OVERRIDE; + + OptimizingCompilerStats* stats_; +}; + + +class InstructionSimplifierArm : public HOptimization { + public: + InstructionSimplifierArm(HGraph* graph, OptimizingCompilerStats* stats) + : HOptimization(graph, "instruction_simplifier_arm", stats) {} + + void Run() OVERRIDE { + InstructionSimplifierArmVisitor visitor(graph_, stats_); + visitor.VisitReversePostOrder(); + } +}; + +} // namespace arm +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_ diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 4bcfc54791b39587400e5e1a7dc5a534b4fc6293..f00d960877ba26e5163dca61acf0133c79b6801f 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -17,6 +17,7 @@ #include "instruction_simplifier_arm64.h" #include "common_arm64.h" +#include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" namespace art { @@ -179,65 +180,10 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc return true; } -bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns( - HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) { - DCHECK(Primitive::IsIntOrLongType(mul->GetType())); - DCHECK(input_binop->IsAdd() || input_binop->IsSub()); - DCHECK_NE(input_binop, input_other); - if (!input_binop->HasOnlyOneNonEnvironmentUse()) { - return false; - } - - // Try to interpret patterns like - // a * (b <+/-> 1) - // as - // (a * b) <+/-> a - HInstruction* input_a = input_other; - HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. - HInstruction::InstructionKind op_kind; - - if (input_binop->IsAdd()) { - if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { - // Interpret - // a * (b + 1) - // as - // (a * b) + a - input_b = input_binop->GetLeastConstantLeft(); - op_kind = HInstruction::kAdd; - } - } else { - DCHECK(input_binop->IsSub()); - if (input_binop->GetRight()->IsConstant() && - input_binop->GetRight()->AsConstant()->IsMinusOne()) { - // Interpret - // a * (b - (-1)) - // as - // a + (a * b) - input_b = input_binop->GetLeft(); - op_kind = HInstruction::kAdd; - } else if (input_binop->GetLeft()->IsConstant() && - input_binop->GetLeft()->AsConstant()->IsOne()) { - // Interpret - // a * (1 - b) - // as - // a - (a * b) - input_b = input_binop->GetRight(); - op_kind = HInstruction::kSub; - } - } - - if (input_b == nullptr) { - // We did not find a pattern we can optimize. - return false; +void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); } - - HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate( - mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); - - mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); - input_binop->GetBlock()->RemoveInstruction(input_binop); - - return false; } void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { @@ -255,75 +201,14 @@ void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { - Primitive::Type type = instruction->GetType(); - if (!Primitive::IsIntOrLongType(type)) { - return; - } - - HInstruction* use = instruction->HasNonEnvironmentUses() - ? instruction->GetUses().GetFirst()->GetUser() - : nullptr; - - if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) { - // Replace code looking like - // MUL tmp, x, y - // SUB dst, acc, tmp - // with - // MULSUB dst, acc, x, y - // Note that we do not want to (unconditionally) perform the merge when the - // multiplication has multiple uses and it can be merged in all of them. - // Multiple uses could happen on the same control-flow path, and we would - // then increase the amount of work. In the future we could try to evaluate - // whether all uses are on different control-flow paths (using dominance and - // reverse-dominance information) and only perform the merge when they are. - HInstruction* accumulator = nullptr; - HBinaryOperation* binop = use->AsBinaryOperation(); - HInstruction* binop_left = binop->GetLeft(); - HInstruction* binop_right = binop->GetRight(); - // Be careful after GVN. This should not happen since the `HMul` has only - // one use. - DCHECK_NE(binop_left, binop_right); - if (binop_right == instruction) { - accumulator = binop_left; - } else if (use->IsAdd()) { - DCHECK_EQ(binop_left, instruction); - accumulator = binop_right; - } - - if (accumulator != nullptr) { - HArm64MultiplyAccumulate* mulacc = - new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type, - binop->GetKind(), - accumulator, - instruction->GetLeft(), - instruction->GetRight()); - - binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); - DCHECK(!instruction->HasUses()); - instruction->GetBlock()->RemoveInstruction(instruction); - RecordSimplification(); - return; - } - } - - // Use multiply accumulate instruction for a few simple patterns. - // We prefer not applying the following transformations if the left and - // right inputs perform the same operation. - // We rely on GVN having squashed the inputs if appropriate. However the - // results are still correct even if that did not happen. - if (instruction->GetLeft() == instruction->GetRight()) { - return; + if (TryCombineMultiplyAccumulate(instruction, kArm64)) { + RecordSimplification(); } +} - HInstruction* left = instruction->GetLeft(); - HInstruction* right = instruction->GetRight(); - if ((right->IsAdd() || right->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) { - return; - } - if ((left->IsAdd() || left->IsSub()) && - TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) { - return; +void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); } } @@ -359,5 +244,11 @@ void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) { } } +void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } +} + } // namespace arm64 } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index b7f490bb8c77dc2d5beb1060278ae9b7a3af8989..338120bbbc3715d410adc1edf496cdeaf2e880e4 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -51,18 +51,17 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { return TryMergeIntoShifterOperand(use, bitfield_op, true); } - bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, - HBinaryOperation* input_binop, - HInstruction* input_other); - // HInstruction visitors, sorted alphabetically. + void VisitAnd(HAnd* instruction) OVERRIDE; void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; void VisitShl(HShl* instruction) OVERRIDE; void VisitShr(HShr* instruction) OVERRIDE; void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; void VisitUShr(HUShr* instruction) OVERRIDE; + void VisitXor(HXor* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc new file mode 100644 index 0000000000000000000000000000000000000000..a11b5bd5c3fb5f16f8f2ff45f5a9bf5579fe2e2a --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "instruction_simplifier_shared.h" + +namespace art { + +namespace { + +bool TrySimpleMultiplyAccumulatePatterns(HMul* mul, + HBinaryOperation* input_binop, + HInstruction* input_other) { + DCHECK(Primitive::IsIntOrLongType(mul->GetType())); + DCHECK(input_binop->IsAdd() || input_binop->IsSub()); + DCHECK_NE(input_binop, input_other); + if (!input_binop->HasOnlyOneNonEnvironmentUse()) { + return false; + } + + // Try to interpret patterns like + // a * (b <+/-> 1) + // as + // (a * b) <+/-> a + HInstruction* input_a = input_other; + HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize. + HInstruction::InstructionKind op_kind; + + if (input_binop->IsAdd()) { + if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) { + // Interpret + // a * (b + 1) + // as + // (a * b) + a + input_b = input_binop->GetLeastConstantLeft(); + op_kind = HInstruction::kAdd; + } + } else { + DCHECK(input_binop->IsSub()); + if (input_binop->GetRight()->IsConstant() && + input_binop->GetRight()->AsConstant()->IsMinusOne()) { + // Interpret + // a * (b - (-1)) + // as + // a + (a * b) + input_b = input_binop->GetLeft(); + op_kind = HInstruction::kAdd; + } else if (input_binop->GetLeft()->IsConstant() && + input_binop->GetLeft()->AsConstant()->IsOne()) { + // Interpret + // a * (1 - b) + // as + // a - (a * b) + input_b = input_binop->GetRight(); + op_kind = HInstruction::kSub; + } + } + + if (input_b == nullptr) { + // We did not find a pattern we can optimize. + return false; + } + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate( + mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc()); + + mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc); + input_binop->GetBlock()->RemoveInstruction(input_binop); + + return true; +} + +} // namespace + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) { + Primitive::Type type = mul->GetType(); + switch (isa) { + case kArm: + case kThumb2: + if (type != Primitive::kPrimInt) { + return false; + } + break; + case kArm64: + if (!Primitive::IsIntOrLongType(type)) { + return false; + } + break; + default: + return false; + } + + HInstruction* use = mul->HasNonEnvironmentUses() + ? mul->GetUses().GetFirst()->GetUser() + : nullptr; + + ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena(); + + if (mul->HasOnlyOneNonEnvironmentUse()) { + if (use->IsAdd() || use->IsSub()) { + // Replace code looking like + // MUL tmp, x, y + // SUB dst, acc, tmp + // with + // MULSUB dst, acc, x, y + // Note that we do not want to (unconditionally) perform the merge when the + // multiplication has multiple uses and it can be merged in all of them. + // Multiple uses could happen on the same control-flow path, and we would + // then increase the amount of work. In the future we could try to evaluate + // whether all uses are on different control-flow paths (using dominance and + // reverse-dominance information) and only perform the merge when they are. + HInstruction* accumulator = nullptr; + HBinaryOperation* binop = use->AsBinaryOperation(); + HInstruction* binop_left = binop->GetLeft(); + HInstruction* binop_right = binop->GetRight(); + // Be careful after GVN. This should not happen since the `HMul` has only + // one use. + DCHECK_NE(binop_left, binop_right); + if (binop_right == mul) { + accumulator = binop_left; + } else if (use->IsAdd()) { + DCHECK_EQ(binop_left, mul); + accumulator = binop_right; + } + + if (accumulator != nullptr) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + binop->GetKind(), + accumulator, + mul->GetLeft(), + mul->GetRight()); + + binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } else if (use->IsNeg() && isa != kArm) { + HMultiplyAccumulate* mulacc = + new (arena) HMultiplyAccumulate(type, + HInstruction::kSub, + mul->GetBlock()->GetGraph()->GetConstant(type, 0), + mul->GetLeft(), + mul->GetRight()); + + use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc); + DCHECK(!mul->HasUses()); + mul->GetBlock()->RemoveInstruction(mul); + return true; + } + } + + // Use multiply accumulate instruction for a few simple patterns. + // We prefer not applying the following transformations if the left and + // right inputs perform the same operation. + // We rely on GVN having squashed the inputs if appropriate. However the + // results are still correct even if that did not happen. + if (mul->GetLeft() == mul->GetRight()) { + return false; + } + + HInstruction* left = mul->GetLeft(); + HInstruction* right = mul->GetRight(); + if ((right->IsAdd() || right->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) { + return true; + } + if ((left->IsAdd() || left->IsSub()) && + TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) { + return true; + } + return false; +} + + +bool TryMergeNegatedInput(HBinaryOperation* op) { + DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName(); + HInstruction* left = op->GetLeft(); + HInstruction* right = op->GetRight(); + + // Only consider the case where there is exactly one Not, with 2 Not's De + // Morgan's laws should be applied instead. + if (left->IsNot() ^ right->IsNot()) { + HInstruction* hnot = (left->IsNot() ? left : right); + HInstruction* hother = (left->IsNot() ? right : left); + + // Only do the simplification if the Not has only one use and can thus be + // safely removed. Even though ARM64 negated bitwise operations do not have + // an immediate variant (only register), we still do the simplification when + // `hother` is a constant, because it removes an instruction if the constant + // cannot be encoded as an immediate: + // mov r0, #large_constant + // neg r2, r1 + // and r0, r0, r2 + // becomes: + // mov r0, #large_constant + // bic r0, r0, r1 + if (hnot->HasOnlyOneNonEnvironmentUse()) { + // Replace code looking like + // NOT tmp, mask + // AND dst, src, tmp (respectively ORR, EOR) + // with + // BIC dst, src, mask (respectively ORN, EON) + HInstruction* src = hnot->AsNot()->GetInput(); + + HBitwiseNegatedRight* neg_op = new (hnot->GetBlock()->GetGraph()->GetArena()) + HBitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc()); + + op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op); + hnot->GetBlock()->RemoveInstruction(hnot); + return true; + } + } + + return false; +} + +} // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h new file mode 100644 index 0000000000000000000000000000000000000000..b1fe8f4756ea2fb9cb90c925b97277bb1594dd88 --- /dev/null +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -0,0 +1,31 @@ +/* + * 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_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ + +#include "nodes.h" + +namespace art { + +bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); +// For bitwise operations (And/Or/Xor) with a negated input, try to use +// a negated bitwise instruction. +bool TryMergeNegatedInput(HBinaryOperation* op); + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 316e86b4c9b6bec8cddeabdc7a39f4efd54829f2..5d4c4e29509b74ac661b0246567739b2d72ef793 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -130,6 +130,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicFloatCvt: return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; + case kIntrinsicFloat2Int: + return Intrinsics::kFloatFloatToIntBits; + case kIntrinsicDouble2Long: + return Intrinsics::kDoubleDoubleToLongBits; // Floating-point tests. case kIntrinsicFloatIsInfinite: @@ -468,6 +472,24 @@ static Intrinsics GetIntrinsic(InlineMethod method) { break; } + // 1.8. + case kIntrinsicUnsafeGetAndAddInt: + return Intrinsics::kUnsafeGetAndAddInt; + case kIntrinsicUnsafeGetAndAddLong: + return Intrinsics::kUnsafeGetAndAddLong; + case kIntrinsicUnsafeGetAndSetInt: + return Intrinsics::kUnsafeGetAndSetInt; + case kIntrinsicUnsafeGetAndSetLong: + return Intrinsics::kUnsafeGetAndSetLong; + case kIntrinsicUnsafeGetAndSetObject: + return Intrinsics::kUnsafeGetAndSetObject; + case kIntrinsicUnsafeLoadFence: + return Intrinsics::kUnsafeLoadFence; + case kIntrinsicUnsafeStoreFence: + return Intrinsics::kUnsafeStoreFence; + case kIntrinsicUnsafeFullFence: + return Intrinsics::kUnsafeFullFence; + // Virtual cases. case kIntrinsicReferenceGetReferent: diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 2ab50bb436302e2e4014265bc0c447ad1c481b34..3da82851a6d2afae1e06b34ea3febb5fbc93386a 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -193,6 +193,49 @@ class SystemArrayCopyOptimizations : public IntrinsicOptimizations { #undef INTRISIC_OPTIMIZATION +// +// Macros for use in the intrinsics code generators. +// + +// Defines an unimplemented intrinsic: that is, a method call that is recognized as an +// intrinsic to exploit e.g. no side-effects or exceptions, but otherwise not handled +// by this architecture-specific intrinsics code generator. Eventually it is implemented +// as a true method call. +#define UNIMPLEMENTED_INTRINSIC(Arch, Name) \ +void IntrinsicLocationsBuilder ## Arch::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} \ +void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} + +// Defines a list of unreached intrinsics: that is, method calls that are recognized as +// an intrinsic, and then always converted into HIR instructions before they reach any +// architecture-specific intrinsics code generator. +#define UNREACHABLE_INTRINSIC(Arch, Name) \ +void IntrinsicLocationsBuilder ## Arch::Visit ## Name(HInvoke* invoke) { \ + LOG(FATAL) << "Unreachable: intrinsic " << invoke->GetIntrinsic() \ + << " should have been converted to HIR"; \ +} \ +void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke) { \ + LOG(FATAL) << "Unreachable: intrinsic " << invoke->GetIntrinsic() \ + << " should have been converted to HIR"; \ +} +#define UNREACHABLE_INTRINSICS(Arch) \ +UNREACHABLE_INTRINSIC(Arch, FloatFloatToIntBits) \ +UNREACHABLE_INTRINSIC(Arch, DoubleDoubleToLongBits) \ +UNREACHABLE_INTRINSIC(Arch, FloatIsNaN) \ +UNREACHABLE_INTRINSIC(Arch, DoubleIsNaN) \ +UNREACHABLE_INTRINSIC(Arch, IntegerRotateLeft) \ +UNREACHABLE_INTRINSIC(Arch, LongRotateLeft) \ +UNREACHABLE_INTRINSIC(Arch, IntegerRotateRight) \ +UNREACHABLE_INTRINSIC(Arch, LongRotateRight) \ +UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \ +UNREACHABLE_INTRINSIC(Arch, LongCompare) \ +UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \ +UNREACHABLE_INTRINSIC(Arch, LongSignum) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \ +UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence) + } // namespace art #endif // ART_COMPILER_OPTIMIZING_INTRINSICS_H_ diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index ea8669fa18effd7ba3534e50448571aef7c33248..4b94c94f396b93de1b5991ccb427c57591fbd5ba 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1151,6 +1151,7 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke, __ LoadFromOffset(kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pIndexOf).Int32Value()); + CheckEntrypointTypes(); __ blx(LR); if (slow_path != nullptr) { @@ -1223,8 +1224,9 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromBytes(HInvoke* invoke) { __ LoadFromOffset( kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromBytes).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ blx(LR); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1242,10 +1244,17 @@ void IntrinsicLocationsBuilderARM::VisitStringNewStringFromChars(HInvoke* invoke void IntrinsicCodeGeneratorARM::VisitStringNewStringFromChars(HInvoke* invoke) { ArmAssembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ LoadFromOffset( kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromChars).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ blx(LR); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void IntrinsicLocationsBuilderARM::VisitStringNewStringFromString(HInvoke* invoke) { @@ -1269,8 +1278,9 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) __ LoadFromOffset(kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocStringFromString).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ blx(LR); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1825,58 +1835,184 @@ void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) { GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -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) -UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat) -UNIMPLEMENTED_INTRINSIC(MathMinLongLong) -UNIMPLEMENTED_INTRINSIC(MathMaxLongLong) -UNIMPLEMENTED_INTRINSIC(MathCeil) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(MathFloor) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(MathRint) -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe? -UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +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 in = locations->InAt(0).AsRegister(); + + __ 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 in_reg_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_reg_lo = locations->Out().AsRegisterPairLow(); + Register out_reg_hi = locations->Out().AsRegisterPairHigh(); + + __ 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 in = locations->InAt(0).AsRegister(); + + __ 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 in_reg_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_reg_lo = locations->Out().AsRegisterPairLow(); + Register out_reg_hi = locations->Out().AsRegisterPairHigh(); + + __ 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 in = locations->InAt(0).AsRegister(); + + __ revsh(out, in); +} + +void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); + + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = locations->InAt(0).AsRegister(); + Register srcBegin = locations->InAt(1).AsRegister(); + Register srcEnd = locations->InAt(2).AsRegister(); + Register dstObj = locations->InAt(3).AsRegister(); + Register dstBegin = locations->InAt(4).AsRegister(); + + Register src_ptr = locations->GetTemp(0).AsRegister(); + Register src_ptr_end = locations->GetTemp(1).AsRegister(); + Register dst_ptr = locations->GetTemp(2).AsRegister(); + Register tmp = locations->GetTemp(3).AsRegister(); + + // src range to copy. + __ add(src_ptr, srcObj, ShifterOperand(value_offset)); + __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1)); + __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1)); + + // dst to be copied. + __ add(dst_ptr, dstObj, ShifterOperand(data_offset)); + __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1)); + + // Do the copy. + Label loop, done; + __ Bind(&loop); + __ cmp(src_ptr, ShifterOperand(src_ptr_end)); + __ b(&done, EQ); + __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex)); + __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex)); + __ b(&loop); + __ Bind(&done); +} + +UNIMPLEMENTED_INTRINSIC(ARM, IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(ARM, LongBitCount) +UNIMPLEMENTED_INTRINSIC(ARM, MathMinDoubleDouble) +UNIMPLEMENTED_INTRINSIC(ARM, MathMinFloatFloat) +UNIMPLEMENTED_INTRINSIC(ARM, MathMaxDoubleDouble) +UNIMPLEMENTED_INTRINSIC(ARM, MathMaxFloatFloat) +UNIMPLEMENTED_INTRINSIC(ARM, MathMinLongLong) +UNIMPLEMENTED_INTRINSIC(ARM, MathMaxLongLong) +UNIMPLEMENTED_INTRINSIC(ARM, MathCeil) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, MathFloor) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, MathRint) +UNIMPLEMENTED_INTRINSIC(ARM, MathRoundDouble) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, MathRoundFloat) // Could be done by changing rounding mode, maybe? +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeCASLong) // High register pressure. +UNIMPLEMENTED_INTRINSIC(ARM, SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(ARM, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(ARM, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM, DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM, LongLowestOneBit) + +// 1.8. +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject) + +UNREACHABLE_INTRINSICS(ARM) #undef __ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 8741fd284fb5aa6589b352f726a007c34862a2ab..5de230650673c2f0d99b97391e1bedb0a4156837 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -46,6 +46,7 @@ using helpers::RegisterFrom; using helpers::SRegisterFrom; using helpers::WRegisterFrom; using helpers::XRegisterFrom; +using helpers::InputRegisterAt; namespace { @@ -99,7 +100,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(codegen_in); @@ -366,6 +368,40 @@ void IntrinsicCodeGeneratorARM64::VisitLongReverse(HInvoke* invoke) { GenReverse(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler()); } +static void GenBitCount(HInvoke* instr, bool is_long, vixl::MacroAssembler* masm) { + DCHECK(instr->GetType() == Primitive::kPrimInt); + DCHECK((is_long && instr->InputAt(0)->GetType() == Primitive::kPrimLong) || + (!is_long && instr->InputAt(0)->GetType() == Primitive::kPrimInt)); + + Location out = instr->GetLocations()->Out(); + UseScratchRegisterScope temps(masm); + + Register src = InputRegisterAt(instr, 0); + Register dst = is_long ? XRegisterFrom(out) : WRegisterFrom(out); + FPRegister fpr = is_long ? temps.AcquireD() : temps.AcquireS(); + + __ Fmov(fpr, src); + __ Cnt(fpr.V8B(), fpr.V8B()); + __ Addv(fpr.B(), fpr.V8B()); + __ Fmov(dst, fpr); +} + +void IntrinsicLocationsBuilderARM64::VisitLongBitCount(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM64::VisitLongBitCount(HInvoke* invoke) { + GenBitCount(invoke, /* is_long */ true, GetVIXLAssembler()); +} + +void IntrinsicLocationsBuilderARM64::VisitIntegerBitCount(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorARM64::VisitIntegerBitCount(HInvoke* invoke) { + GenBitCount(invoke, /* is_long */ false, GetVIXLAssembler()); +} + static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { LocationSummary* locations = new (arena) LocationSummary(invoke, LocationSummary::kNoCall, @@ -1300,6 +1336,7 @@ static void GenerateVisitStringIndexOf(HInvoke* invoke, } __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pIndexOf).Int32Value())); + CheckEntrypointTypes(); __ Blr(lr); if (slow_path != nullptr) { @@ -1372,8 +1409,9 @@ void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromBytes(HInvoke* invoke) __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromBytes).Int32Value())); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Blr(lr); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1391,21 +1429,25 @@ void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromChars(HInvoke* invo void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromChars(HInvoke* invoke) { vixl::MacroAssembler* masm = GetVIXLAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromChars).Int32Value())); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Blr(lr); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void IntrinsicLocationsBuilderARM64::VisitStringNewStringFromString(HInvoke* invoke) { - // The inputs plus one temp. LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, kIntrinsified); InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); - locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); - locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot)); } @@ -1421,8 +1463,9 @@ void IntrinsicCodeGeneratorARM64::VisitStringNewStringFromString(HInvoke* invoke __ Ldr(lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pAllocStringFromString).Int32Value())); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + CheckEntrypointTypes(); __ Blr(lr); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1601,42 +1644,323 @@ void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter); } -// Unimplemented intrinsics. +void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); } -UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(LongBitCount) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) +void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Check assumption that sizeof(Char) is 2 (used in scaling below). + const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + DCHECK_EQ(char_size, 2u); -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) + // Location of data in char array buffer. + const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value(); + + // Location of char array data in string. + const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value(); + + // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin); + // Since getChars() calls getCharsNoCheck() - we use registers rather than constants. + Register srcObj = XRegisterFrom(locations->InAt(0)); + Register srcBegin = XRegisterFrom(locations->InAt(1)); + Register srcEnd = XRegisterFrom(locations->InAt(2)); + Register dstObj = XRegisterFrom(locations->InAt(3)); + Register dstBegin = XRegisterFrom(locations->InAt(4)); + + Register src_ptr = XRegisterFrom(locations->GetTemp(0)); + Register src_ptr_end = XRegisterFrom(locations->GetTemp(1)); + + UseScratchRegisterScope temps(masm); + Register dst_ptr = temps.AcquireX(); + Register tmp = temps.AcquireW(); + + // src range to copy. + __ Add(src_ptr, srcObj, Operand(value_offset)); + __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1)); + __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1)); + + // dst to be copied. + __ Add(dst_ptr, dstObj, Operand(data_offset)); + __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1)); + + // Do the copy. + vixl::Label loop, done; + __ Bind(&loop); + __ Cmp(src_ptr, src_ptr_end); + __ B(&done, eq); + __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex)); + __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex)); + __ B(&loop); + __ Bind(&done); +} + +// Mirrors ARRAYCOPY_SHORT_CHAR_ARRAY_THRESHOLD in libcore, so we can choose to use the native +// implementation there for longer copy lengths. +static constexpr int32_t kSystemArrayCopyThreshold = 32; + +static void SetSystemArrayCopyLocationRequires(LocationSummary* locations, + uint32_t at, + HInstruction* input) { + HIntConstant* const_input = input->AsIntConstant(); + if (const_input != nullptr && !vixl::Assembler::IsImmAddSub(const_input->GetValue())) { + locations->SetInAt(at, Location::RequiresRegister()); + } else { + locations->SetInAt(at, Location::RegisterOrConstant(input)); + } +} + +void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopyChar(HInvoke* invoke) { + // Check to see if we have known failures that will cause us to have to bail out + // to the runtime, and just generate the runtime call directly. + HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); + HIntConstant* dst_pos = invoke->InputAt(3)->AsIntConstant(); + + // The positions must be non-negative. + if ((src_pos != nullptr && src_pos->GetValue() < 0) || + (dst_pos != nullptr && dst_pos->GetValue() < 0)) { + // We will have to fail anyways. + return; + } + + // The length must be >= 0 and not so long that we would (currently) prefer libcore's + // native implementation. + HIntConstant* length = invoke->InputAt(4)->AsIntConstant(); + if (length != nullptr) { + int32_t len = length->GetValue(); + if (len < 0 || len > kSystemArrayCopyThreshold) { + // Just call as normal. + return; + } + } + + ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetArena(); + LocationSummary* locations = new (allocator) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + // arraycopy(char[] src, int src_pos, char[] dst, int dst_pos, int length). + locations->SetInAt(0, Location::RequiresRegister()); + SetSystemArrayCopyLocationRequires(locations, 1, invoke->InputAt(1)); + locations->SetInAt(2, Location::RequiresRegister()); + SetSystemArrayCopyLocationRequires(locations, 3, invoke->InputAt(3)); + SetSystemArrayCopyLocationRequires(locations, 4, invoke->InputAt(4)); + + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +static void CheckSystemArrayCopyPosition(vixl::MacroAssembler* masm, + const Location& pos, + const Register& input, + const Location& length, + SlowPathCodeARM64* slow_path, + const Register& input_len, + const Register& temp, + bool length_is_input_length = false) { + const int32_t length_offset = mirror::Array::LengthOffset().Int32Value(); + if (pos.IsConstant()) { + int32_t pos_const = pos.GetConstant()->AsIntConstant()->GetValue(); + if (pos_const == 0) { + if (!length_is_input_length) { + // Check that length(input) >= length. + __ Ldr(temp, MemOperand(input, length_offset)); + __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt)); + __ B(slow_path->GetEntryLabel(), lt); + } + } else { + // Check that length(input) >= pos. + __ Ldr(input_len, MemOperand(input, length_offset)); + __ Subs(temp, input_len, pos_const); + __ B(slow_path->GetEntryLabel(), lt); + + // Check that (length(input) - pos) >= length. + __ Cmp(temp, OperandFrom(length, Primitive::kPrimInt)); + __ B(slow_path->GetEntryLabel(), lt); + } + } else if (length_is_input_length) { + // The only way the copy can succeed is if pos is zero. + __ Cbnz(WRegisterFrom(pos), slow_path->GetEntryLabel()); + } else { + // Check that pos >= 0. + Register pos_reg = WRegisterFrom(pos); + __ Tbnz(pos_reg, pos_reg.size() - 1, slow_path->GetEntryLabel()); + + // Check that pos <= length(input) && (length(input) - pos) >= length. + __ Ldr(temp, MemOperand(input, length_offset)); + __ Subs(temp, temp, pos_reg); + // Ccmp if length(input) >= pos, else definitely bail to slow path (N!=V == lt). + __ Ccmp(temp, OperandFrom(length, Primitive::kPrimInt), NFlag, ge); + __ B(slow_path->GetEntryLabel(), lt); + } +} + +// Compute base source address, base destination address, and end source address +// for System.arraycopy* intrinsics. +static void GenSystemArrayCopyAddresses(vixl::MacroAssembler* masm, + Primitive::Type type, + const Register& src, + const Location& src_pos, + const Register& dst, + const Location& dst_pos, + const Location& copy_length, + const Register& src_base, + const Register& dst_base, + const Register& src_end) { + DCHECK(type == Primitive::kPrimNot || type == Primitive::kPrimChar) + << "Unexpected element type: " + << type; + const int32_t char_size = Primitive::ComponentSize(type); + const int32_t char_size_shift = Primitive::ComponentSizeShift(type); + + uint32_t offset = mirror::Array::DataOffset(char_size).Uint32Value(); + if (src_pos.IsConstant()) { + int32_t constant = src_pos.GetConstant()->AsIntConstant()->GetValue(); + __ Add(src_base, src, char_size * constant + offset); + } else { + __ Add(src_base, src, offset); + __ Add(src_base, + src_base, + Operand(XRegisterFrom(src_pos), LSL, char_size_shift)); + } + + if (dst_pos.IsConstant()) { + int32_t constant = dst_pos.GetConstant()->AsIntConstant()->GetValue(); + __ Add(dst_base, dst, char_size * constant + offset); + } else { + __ Add(dst_base, dst, offset); + __ Add(dst_base, + dst_base, + Operand(XRegisterFrom(dst_pos), LSL, char_size_shift)); + } + + if (copy_length.IsConstant()) { + int32_t constant = copy_length.GetConstant()->AsIntConstant()->GetValue(); + __ Add(src_end, src_base, char_size * constant); + } else { + __ Add(src_end, + src_base, + Operand(XRegisterFrom(copy_length), LSL, char_size_shift)); + } +} + +void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopyChar(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + Register src = XRegisterFrom(locations->InAt(0)); + Location src_pos = locations->InAt(1); + Register dst = XRegisterFrom(locations->InAt(2)); + Location dst_pos = locations->InAt(3); + Location length = locations->InAt(4); + + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + + // If source and destination are the same, take the slow path. Overlapping copy regions must be + // copied in reverse and we can't know in all cases if it's needed. + __ Cmp(src, dst); + __ B(slow_path->GetEntryLabel(), eq); + + // Bail out if the source is null. + __ Cbz(src, slow_path->GetEntryLabel()); + + // Bail out if the destination is null. + __ Cbz(dst, slow_path->GetEntryLabel()); + + if (!length.IsConstant()) { + // If the length is negative, bail out. + __ Tbnz(WRegisterFrom(length), kWRegSize - 1, slow_path->GetEntryLabel()); + // If the length > 32 then (currently) prefer libcore's native implementation. + __ Cmp(WRegisterFrom(length), kSystemArrayCopyThreshold); + __ B(slow_path->GetEntryLabel(), gt); + } else { + // We have already checked in the LocationsBuilder for the constant case. + DCHECK_GE(length.GetConstant()->AsIntConstant()->GetValue(), 0); + DCHECK_LE(length.GetConstant()->AsIntConstant()->GetValue(), 32); + } + + Register src_curr_addr = WRegisterFrom(locations->GetTemp(0)); + Register dst_curr_addr = WRegisterFrom(locations->GetTemp(1)); + Register src_stop_addr = WRegisterFrom(locations->GetTemp(2)); + + CheckSystemArrayCopyPosition(masm, + src_pos, + src, + length, + slow_path, + src_curr_addr, + dst_curr_addr, + false); + + CheckSystemArrayCopyPosition(masm, + dst_pos, + dst, + length, + slow_path, + src_curr_addr, + dst_curr_addr, + false); + + src_curr_addr = src_curr_addr.X(); + dst_curr_addr = dst_curr_addr.X(); + src_stop_addr = src_stop_addr.X(); + + GenSystemArrayCopyAddresses(masm, + Primitive::kPrimChar, + src, + src_pos, + dst, + dst_pos, + length, + src_curr_addr, + dst_curr_addr, + src_stop_addr); + + // Iterate over the arrays and do a raw copy of the chars. + const int32_t char_size = Primitive::ComponentSize(Primitive::kPrimChar); + UseScratchRegisterScope temps(masm); + Register tmp = temps.AcquireW(); + vixl::Label loop, done; + __ Bind(&loop); + __ Cmp(src_curr_addr, src_stop_addr); + __ B(&done, eq); + __ Ldrh(tmp, MemOperand(src_curr_addr, char_size, vixl::PostIndex)); + __ Strh(tmp, MemOperand(dst_curr_addr, char_size, vixl::PostIndex)); + __ B(&loop); + __ Bind(&done); + + __ Bind(slow_path->GetExitLabel()); +} -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM64, SystemArrayCopy) +UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(ARM64, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM64, DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(ARM64, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM64, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM64, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(ARM64, LongLowestOneBit) -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) +// 1.8. +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject) -#undef UNIMPLEMENTED_INTRINSIC +UNREACHABLE_INTRINSICS(ARM64) #undef __ diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 88217b308ea892f89311bfe52382304cff38266b..dd9294d48682507edfdfc10ccf6423f782263b93 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -19,14 +19,16 @@ // All intrinsics supported by the optimizing compiler. Format is name, then whether it is expected // to be a HInvokeStaticOrDirect node (compared to HInvokeVirtual), then whether it requires an -// environment. +// environment, may have side effects, or may throw exceptions. #define INTRINSICS_LIST(V) \ V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ @@ -126,6 +128,14 @@ V(UnsafePutLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(UnsafePutLongOrdered, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(UnsafePutLongVolatile, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndAddInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndAddLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetInt, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetLong, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeGetAndSetObject, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeLoadFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeStoreFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ + V(UnsafeFullFence, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(ReferenceGetReferent, kDirect, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) #endif // ART_COMPILER_OPTIMIZING_INTRINSICS_LIST_H_ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index c8629644b62321532d9c6f84320ce1add798f3b4..c306cf93a1c9f97f94eb2270a9ca51f298e4c35a 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(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,27 +484,25 @@ 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, bool is64bit, bool isR6, - bool isR2OrNewer, MipsAssembler* assembler) { Register out = locations->Out().AsRegister(); Register in_lo; Register in; if (is64bit) { - MipsLabel done; Register in_hi = locations->InAt(0).AsRegisterPairHigh(); in_lo = locations->InAt(0).AsRegisterPairLow(); // If in_lo is zero then count the number of trailing zeroes in in_hi; // otherwise count the number of trailing zeroes in in_lo. - // AT = in_lo ? in_lo : in_hi; + // out = in_lo ? in_lo : in_hi; if (isR6) { __ Seleqz(out, in_hi, in_lo); __ Selnez(TMP, in_lo, in_lo); @@ -523,50 +521,26 @@ static void GenNumberOfTrailingZeroes(LocationSummary* locations, in_lo = in; } - // We don't have an instruction to count the number of trailing zeroes. - // Start by flipping the bits end-for-end so we can count the number of - // leading zeroes instead. - if (isR2OrNewer) { + if (isR6) { + // We don't have an instruction to count the number of trailing zeroes. + // Start by flipping the bits end-for-end so we can count the number of + // leading zeroes instead. __ Rotr(out, in, 16); __ Wsbh(out, out); - } else { - // MIPS32r1 - // __ Rotr(out, in, 16); - __ Sll(TMP, in, 16); - __ Srl(out, in, 16); - __ Or(out, out, TMP); - // __ Wsbh(out, out); - __ LoadConst32(AT, 0x00FF00FF); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 8); - __ Srl(out, out, 8); - __ And(out, out, AT); - __ Or(out, out, TMP); - } - - if (isR6) { __ Bitswap(out, out); __ ClzR6(out, out); } else { - __ LoadConst32(AT, 0x0F0F0F0F); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 4); - __ Srl(out, out, 4); - __ And(out, out, AT); - __ Or(out, TMP, out); - __ LoadConst32(AT, 0x33333333); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 2); - __ Srl(out, out, 2); - __ And(out, out, AT); - __ Or(out, TMP, out); - __ LoadConst32(AT, 0x55555555); - __ And(TMP, out, AT); - __ Sll(TMP, TMP, 1); - __ Srl(out, out, 1); - __ And(out, out, AT); - __ Or(out, TMP, out); + // Convert trailing zeroes to trailing ones, and bits to their left + // to zeroes. + __ Addiu(TMP, in, -1); + __ Xor(out, TMP, in); + __ And(out, out, TMP); + // Count number of leading zeroes. __ ClzR2(out, out); + // Subtract number of leading zeroes from 32 to get number of trailing ones. + // Remember that the trailing ones were formerly trailing zeroes. + __ LoadConst32(TMP, 32); + __ Subu(out, TMP, out); } if (is64bit) { @@ -588,7 +562,7 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* i } void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), false, IsR6(), IsR2OrNewer(), GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler()); } // int java.lang.Long.numberOfTrailingZeros(long i) @@ -597,231 +571,722 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invo } void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeroes(invoke->GetLocations(), true, IsR6(), IsR2OrNewer(), GetAssembler()); + GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler()); } -enum RotationDirection { - kRotateRight, - kRotateLeft, -}; +// int java.lang.Integer.reverse(int) +void IntrinsicLocationsBuilderMIPS::VisitIntegerReverse(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} -static void GenRotate(HInvoke* invoke, - Primitive::Type type, - bool isR2OrNewer, - RotationDirection direction, - MipsAssembler* assembler) { +void IntrinsicCodeGeneratorMIPS::VisitIntegerReverse(HInvoke* invoke) { + GenReverse(invoke->GetLocations(), + Primitive::kPrimInt, + IsR2OrNewer(), + IsR6(), + /* reverseBits */ true, + GetAssembler()); +} + +// long java.lang.Long.reverse(long) +void IntrinsicLocationsBuilderMIPS::VisitLongReverse(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) { + GenReverse(invoke->GetLocations(), + Primitive::kPrimLong, + IsR2OrNewer(), + IsR6(), + /* 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 GenBitCount(LocationSummary* locations, + Primitive::Type type, + bool isR6, + MipsAssembler* assembler) { DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); - LocationSummary* locations = invoke->GetLocations(); - if (invoke->InputAt(1)->IsIntConstant()) { - int32_t shift = static_cast(invoke->InputAt(1)->AsIntConstant()->GetValue()); - if (type == Primitive::kPrimInt) { - Register in = locations->InAt(0).AsRegister(); - Register out = locations->Out().AsRegister(); + Register out = locations->Out().AsRegister(); + + // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + // + // A generalization of the best bit counting method to integers of + // bit-widths up to 128 (parameterized by type T) is this: + // + // v = v - ((v >> 1) & (T)~(T)0/3); // temp + // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp + // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp + // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; // count + // + // For comparison, for 32-bit quantities, this algorithm can be executed + // using 20 MIPS instructions (the calls to LoadConst32() generate two + // machine instructions each for the values being used in this algorithm). + // A(n unrolled) loop-based algorithm required 25 instructions. + // + // For 64-bit quantities, this algorithm gets executed twice, (once + // for in_lo, and again for in_hi), but saves a few instructions + // because the mask values only have to be loaded once. Using this + // algorithm the count for a 64-bit operand can be performed in 33 + // instructions compared to a loop-based algorithm which required 47 + // instructions. + + if (type == Primitive::kPrimInt) { + Register in = locations->InAt(0).AsRegister(); + + __ Srl(TMP, in, 1); + __ LoadConst32(AT, 0x55555555); + __ And(TMP, TMP, AT); + __ Subu(TMP, in, TMP); + __ LoadConst32(AT, 0x33333333); + __ And(out, TMP, AT); + __ Srl(TMP, TMP, 2); + __ And(TMP, TMP, AT); + __ Addu(TMP, out, TMP); + __ Srl(out, TMP, 4); + __ Addu(out, out, TMP); + __ LoadConst32(AT, 0x0F0F0F0F); + __ And(out, out, AT); + __ LoadConst32(TMP, 0x01010101); + if (isR6) { + __ MulR6(out, out, TMP); + } else { + __ MulR2(out, out, TMP); + } + __ Srl(out, out, 24); + } else if (type == Primitive::kPrimLong) { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register tmp_hi = locations->GetTemp(0).AsRegister(); + Register out_hi = locations->GetTemp(1).AsRegister(); + Register tmp_lo = TMP; + Register out_lo = out; + + __ Srl(tmp_lo, in_lo, 1); + __ Srl(tmp_hi, in_hi, 1); + + __ LoadConst32(AT, 0x55555555); + + __ And(tmp_lo, tmp_lo, AT); + __ Subu(tmp_lo, in_lo, tmp_lo); + + __ And(tmp_hi, tmp_hi, AT); + __ Subu(tmp_hi, in_hi, tmp_hi); + + __ LoadConst32(AT, 0x33333333); + + __ And(out_lo, tmp_lo, AT); + __ Srl(tmp_lo, tmp_lo, 2); + __ And(tmp_lo, tmp_lo, AT); + __ Addu(tmp_lo, out_lo, tmp_lo); + __ Srl(out_lo, tmp_lo, 4); + __ Addu(out_lo, out_lo, tmp_lo); + + __ And(out_hi, tmp_hi, AT); + __ Srl(tmp_hi, tmp_hi, 2); + __ And(tmp_hi, tmp_hi, AT); + __ Addu(tmp_hi, out_hi, tmp_hi); + __ Srl(out_hi, tmp_hi, 4); + __ Addu(out_hi, out_hi, tmp_hi); + + __ LoadConst32(AT, 0x0F0F0F0F); + + __ And(out_lo, out_lo, AT); + __ And(out_hi, out_hi, AT); + + __ LoadConst32(AT, 0x01010101); + + if (isR6) { + __ MulR6(out_lo, out_lo, AT); + + __ MulR6(out_hi, out_hi, AT); + } else { + __ MulR2(out_lo, out_lo, AT); + + __ MulR2(out_hi, out_hi, AT); + } + + __ Srl(out_lo, out_lo, 24); + __ Srl(out_hi, out_hi, 24); + + __ Addu(out, out_hi, out_lo); + } +} + +// int java.lang.Integer.bitCount(int) +void IntrinsicLocationsBuilderMIPS::VisitIntegerBitCount(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitIntegerBitCount(HInvoke* invoke) { + GenBitCount(invoke->GetLocations(), Primitive::kPrimInt, IsR6(), GetAssembler()); +} + +// int java.lang.Long.bitCount(int) +void IntrinsicLocationsBuilderMIPS::VisitLongBitCount(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS::VisitLongBitCount(HInvoke* invoke) { + GenBitCount(invoke->GetLocations(), Primitive::kPrimLong, IsR6(), GetAssembler()); +} + +static void MathAbsFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) { + FRegister in = locations->InAt(0).AsFpuRegister(); + FRegister out = locations->Out().AsFpuRegister(); + + 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 in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + // 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 out = locations->Out().AsRegister(); + + __ 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 a = locations->InAt(0).AsFpuRegister(); + FRegister b = locations->InAt(1).AsFpuRegister(); - shift &= 0x1f; - if (direction == kRotateLeft) { - shift = (32 - shift) & 0x1F; + 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); } - if (isR2OrNewer) { - if ((shift != 0) || (out != in)) { - __ Rotr(out, in, shift); - } + __ B(&done); + + __ Bind(&noNaNs); + + if (is_min) { + __ MinD(out, a, b); } else { - if (shift == 0) { - if (out != in) { - __ Move(out, in); - } - } else { - __ Srl(AT, in, shift); - __ Sll(out, in, 32 - shift); - __ Or(out, out, AT); - } + __ 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 } - } else { // Primitive::kPrimLong - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + } + + __ 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 a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); Register out_lo = locations->Out().AsRegisterPairLow(); Register out_hi = locations->Out().AsRegisterPairHigh(); - shift &= 0x3f; - if (direction == kRotateLeft) { - shift = (64 - shift) & 0x3F; - } + MipsLabel compare_done; - if (shift == 0) { - __ Move(out_lo, in_lo); - __ Move(out_hi, in_hi); - } else if (shift == 32) { - __ Move(out_lo, in_hi); - __ Move(out_hi, in_lo); - } else if (shift < 32) { - __ Srl(AT, in_lo, shift); - __ Sll(out_lo, in_hi, 32 - shift); - __ Or(out_lo, out_lo, AT); - __ Srl(AT, in_hi, shift); - __ Sll(out_hi, in_lo, 32 - shift); - __ Or(out_hi, out_hi, AT); + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } } else { - __ Sll(AT, in_lo, 64 - shift); - __ Srl(out_lo, in_hi, shift - 32); + __ 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); - __ Sll(AT, in_hi, 64 - shift); - __ Srl(out_hi, in_lo, shift - 32); + 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 { // !invoke->InputAt(1)->IsIntConstant() - Register shamt = locations->InAt(1).AsRegister(); - if (type == Primitive::kPrimInt) { - Register in = locations->InAt(0).AsRegister(); + } else { + DCHECK_EQ(type, Primitive::kPrimInt); + Register a = locations->InAt(0).AsRegister(); + Register b = locations->InAt(1).AsRegister(); Register out = locations->Out().AsRegister(); - if (isR2OrNewer) { - if (direction == kRotateRight) { - __ Rotrv(out, in, shamt); - } else { - // negu tmp, shamt - __ Subu(TMP, ZERO, shamt); - __ Rotrv(out, in, TMP); + if (a == b) { + if (out != a) { + __ Move(out, a); } } else { - if (direction == kRotateRight) { - __ Srlv(AT, in, shamt); - __ Subu(TMP, ZERO, shamt); - __ Sllv(out, in, TMP); - __ Or(out, out, AT); + __ Slt(AT, b, a); + if (is_min) { + __ Seleqz(TMP, a, AT); + __ Selnez(AT, b, AT); } else { - __ Sllv(AT, in, shamt); - __ Subu(TMP, ZERO, shamt); - __ Srlv(out, in, TMP); - __ Or(out, out, AT); + __ Selnez(TMP, a, AT); + __ Seleqz(AT, b, AT); } + __ Or(out, TMP, AT); } - } else { // Primitive::kPrimLong - Register in_lo = locations->InAt(0).AsRegisterPairLow(); - Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + } + } else { + if (type == Primitive::kPrimLong) { + Register a_lo = locations->InAt(0).AsRegisterPairLow(); + Register a_hi = locations->InAt(0).AsRegisterPairHigh(); + Register b_lo = locations->InAt(1).AsRegisterPairLow(); + Register b_hi = locations->InAt(1).AsRegisterPairHigh(); Register out_lo = locations->Out().AsRegisterPairLow(); Register out_hi = locations->Out().AsRegisterPairHigh(); - MipsLabel done; + MipsLabel compare_done; - if (direction == kRotateRight) { - __ Nor(TMP, ZERO, shamt); - __ Srlv(AT, in_lo, shamt); - __ Sll(out_lo, in_hi, 1); - __ Sllv(out_lo, out_lo, TMP); - __ Or(out_lo, out_lo, AT); - __ Srlv(AT, in_hi, shamt); - __ Sll(out_hi, in_lo, 1); - __ Sllv(out_hi, out_hi, TMP); - __ Or(out_hi, out_hi, AT); + if (a_lo == b_lo) { + if (out_lo != a_lo) { + __ Move(out_lo, a_lo); + __ Move(out_hi, a_hi); + } } else { - __ Nor(TMP, ZERO, shamt); - __ Sllv(AT, in_lo, shamt); - __ Srl(out_lo, in_hi, 1); - __ Srlv(out_lo, out_lo, TMP); - __ Or(out_lo, out_lo, AT); - __ Sllv(AT, in_hi, shamt); - __ Srl(out_hi, in_lo, 1); - __ Srlv(out_hi, out_hi, TMP); - __ Or(out_hi, out_hi, AT); - } + __ Slt(TMP, a_hi, b_hi); + __ Bne(a_hi, b_hi, &compare_done); + + __ Sltu(TMP, a_lo, b_lo); - __ Andi(TMP, shamt, 32); - __ Beqz(TMP, &done); - __ Move(TMP, out_hi); - __ Move(out_hi, out_lo); - __ Move(out_lo, TMP); + __ 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 b = locations->InAt(1).AsRegister(); + Register out = locations->Out().AsRegister(); - __ Bind(&done); + 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.Integer.rotateRight(int i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitIntegerRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +// int java.lang.Math.min(int, int) +void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); } -void IntrinsicCodeGeneratorMIPS::VisitIntegerRotateRight(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimInt, IsR2OrNewer(), kRotateRight, GetAssembler()); +void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimInt, + IsR6(), + GetAssembler()); } -// long java.lang.Long.rotateRight(long i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitLongRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +// long java.lang.Math.min(long, long) +void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); } -void IntrinsicCodeGeneratorMIPS::VisitLongRotateRight(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimLong, IsR2OrNewer(), kRotateRight, GetAssembler()); +void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ true, + Primitive::kPrimLong, + IsR6(), + GetAssembler()); } -// int java.lang.Integer.rotateLeft(int i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitIntegerRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +// int java.lang.Math.max(int, int) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); } -void IntrinsicCodeGeneratorMIPS::VisitIntegerRotateLeft(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimInt, IsR2OrNewer(), kRotateLeft, GetAssembler()); +void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimInt, + IsR6(), + GetAssembler()); } -// long java.lang.Long.rotateLeft(long i, int distance) -void IntrinsicLocationsBuilderMIPS::VisitLongRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +// long java.lang.Math.max(long, long) +void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); } -void IntrinsicCodeGeneratorMIPS::VisitLongRotateLeft(HInvoke* invoke) { - GenRotate(invoke, Primitive::kPrimLong, IsR2OrNewer(), kRotateLeft, GetAssembler()); +void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), + /* is_min */ false, + Primitive::kPrimLong, + IsR6(), + GetAssembler()); } -// int java.lang.Integer.reverse(int) -void IntrinsicLocationsBuilderMIPS::VisitIntegerReverse(HInvoke* invoke) { - CreateIntToIntLocations(arena_, invoke); +// double java.lang.Math.sqrt(double) +void IntrinsicLocationsBuilderMIPS::VisitMathSqrt(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); } -void IntrinsicCodeGeneratorMIPS::VisitIntegerReverse(HInvoke* invoke) { - GenReverse(invoke->GetLocations(), - Primitive::kPrimInt, - IsR2OrNewer(), - IsR6(), - true, - GetAssembler()); -} - -// long java.lang.Long.reverse(long) -void IntrinsicLocationsBuilderMIPS::VisitLongReverse(HInvoke* invoke) { - CreateIntToIntLocations(arena_, invoke); -} +void IntrinsicCodeGeneratorMIPS::VisitMathSqrt(HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + MipsAssembler* assembler = GetAssembler(); + FRegister in = locations->InAt(0).AsFpuRegister(); + FRegister out = locations->Out().AsFpuRegister(); -void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) { - GenReverse(invoke->GetLocations(), - Primitive::kPrimLong, - IsR2OrNewer(), - IsR6(), - true, - GetAssembler()); + __ SqrtD(out, in); } // byte libcore.io.Memory.peekByte(long address) @@ -992,6 +1457,24 @@ void IntrinsicCodeGeneratorMIPS::VisitMemoryPokeLongNative(HInvoke* invoke) { } } +// Thread java.lang.Thread.currentThread() +void IntrinsicLocationsBuilderMIPS::VisitThreadCurrentThread(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetOut(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS::VisitThreadCurrentThread(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + Register out = invoke->GetLocations()->Out().AsRegister(); + + __ LoadFromOffset(kLoadWord, + out, + TR, + Thread::PeerOffset().Int32Value()); +} + // char java.lang.String.charAt(int index) void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -999,7 +1482,9 @@ void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); + // The inputs will be considered live at the last instruction and restored. This will overwrite + // the output with kNoOutputOverlap. + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) { @@ -1038,6 +1523,40 @@ void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +// int java.lang.String.compareTo(String anotherString) +void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringCompareTo(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + Register argument = locations->InAt(1).AsRegister(); + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqz(argument, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadWord, + T9, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, + pStringCompareTo).Int32Value()); + __ Jalr(T9); + __ Nop(); + __ Bind(slow_path->GetExitLabel()); +} + // boolean java.lang.String.equals(Object anObject) void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -1140,101 +1659,439 @@ void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) { __ Bind(&end); } +static void GenerateStringIndexOf(HInvoke* invoke, + bool start_at_zero, + MipsAssembler* assembler, + CodeGeneratorMIPS* codegen, + ArenaAllocator* allocator) { + LocationSummary* locations = invoke->GetLocations(); + Register tmp_reg = start_at_zero ? locations->GetTemp(0).AsRegister() : TMP; + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheckOn(invoke->InputAt(0))); + + // Check for code points > 0xFFFF. Either a slow-path check when we + // don't know statically, or directly dispatch if we have a constant. + SlowPathCodeMIPS* slow_path = nullptr; + if (invoke->InputAt(1)->IsIntConstant()) { + if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) { + // Always needs the slow-path. We could directly dispatch to it, + // but this case should be rare, so for simplicity just put the + // full slow-path down and branch unconditionally. + slow_path = new (allocator) IntrinsicSlowPathMIPS(invoke); + codegen->AddSlowPath(slow_path); + __ B(slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + return; + } + } else { + Register char_reg = locations->InAt(1).AsRegister(); + // The "bltu" conditional branch tests to see if the character value + // fits in a valid 16-bit (MIPS halfword) value. If it doesn't then + // the character being searched for, if it exists in the string, is + // encoded using UTF-16 and stored in the string as two (16-bit) + // halfwords. Currently the assembly code used to implement this + // intrinsic doesn't support searching for a character stored as + // two halfwords so we fallback to using the generic implementation + // of indexOf(). + __ LoadConst32(tmp_reg, std::numeric_limits::max()); + slow_path = new (allocator) IntrinsicSlowPathMIPS(invoke); + codegen->AddSlowPath(slow_path); + __ Bltu(tmp_reg, char_reg, slow_path->GetEntryLabel()); + } + + if (start_at_zero) { + DCHECK_EQ(tmp_reg, A2); + // Start-index = 0. + __ Clear(tmp_reg); + } + + __ LoadFromOffset(kLoadWord, + T9, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pIndexOf).Int32Value()); + __ Jalr(T9); + __ Nop(); + + if (slow_path != nullptr) { + __ Bind(slow_path->GetExitLabel()); + } +} + +// int java.lang.String.indexOf(int ch) +void IntrinsicLocationsBuilderMIPS::VisitStringIndexOf(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime + // calling convention. So it's best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); + + // Need a temp for slow-path codepoint compare, and need to send start-index=0. + locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2))); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringIndexOf(HInvoke* invoke) { + GenerateStringIndexOf(invoke, + /* start_at_zero */ true, + GetAssembler(), + codegen_, + GetAllocator()); +} + +// int java.lang.String.indexOf(int ch, int fromIndex) +void IntrinsicLocationsBuilderMIPS::VisitStringIndexOfAfter(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + // We have a hand-crafted assembly stub that follows the runtime + // calling convention. So it's best to align the inputs accordingly. + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); + + // Need a temp for slow-path codepoint compare. + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringIndexOfAfter(HInvoke* invoke) { + GenerateStringIndexOf(invoke, + /* start_at_zero */ false, + GetAssembler(), + codegen_, + GetAllocator()); +} + +// java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount) +void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + locations->SetInAt(3, Location::RegisterLocation(calling_convention.GetRegisterAt(3))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromBytes(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register byte_array = locations->InAt(0).AsRegister(); + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqz(byte_array, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadWord, + T9, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromBytes).Int32Value()); + __ Jalr(T9); + __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + +// java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) +void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromChars(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromChars(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. + + __ LoadFromOffset(kLoadWord, + T9, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromChars).Int32Value()); + __ Jalr(T9); + __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +// java.lang.StringFactory.newStringFromString(String toCopy) +void IntrinsicLocationsBuilderMIPS::VisitStringNewStringFromString(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); + locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); +} + +void IntrinsicCodeGeneratorMIPS::VisitStringNewStringFromString(HInvoke* invoke) { + MipsAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + Register string_to_copy = locations->InAt(0).AsRegister(); + SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); + codegen_->AddSlowPath(slow_path); + __ Beqz(string_to_copy, slow_path->GetEntryLabel()); + + __ LoadFromOffset(kLoadWord, + T9, + TR, + QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pAllocStringFromString).Int32Value()); + __ Jalr(T9); + __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + __ Bind(slow_path->GetExitLabel()); +} + +static void GenIsInfinite(LocationSummary* locations, + const Primitive::Type type, + const bool isR6, + MipsAssembler* assembler) { + FRegister in = locations->InAt(0).AsFpuRegister(); + Register out = locations->Out().AsRegister(); + + DCHECK(type == Primitive::kPrimFloat || type == Primitive::kPrimDouble); + + if (isR6) { + if (type == Primitive::kPrimDouble) { + __ ClassD(FTMP, in); + } else { + __ ClassS(FTMP, in); + } + __ Mfc1(out, FTMP); + __ Andi(out, out, kPositiveInfinity | kNegativeInfinity); + __ Sltu(out, ZERO, out); + } else { + // If one, or more, of the exponent bits is zero, then the number can't be infinite. + if (type == Primitive::kPrimDouble) { + __ MoveFromFpuHigh(TMP, in); + __ LoadConst32(AT, 0x7FF00000); + } else { + __ Mfc1(TMP, in); + __ LoadConst32(AT, 0x7F800000); + } + __ Xor(TMP, TMP, AT); + + __ Sll(TMP, TMP, 1); + + if (type == Primitive::kPrimDouble) { + __ Mfc1(AT, in); + __ Or(TMP, TMP, AT); + } + // If any of the significand bits are one, then the number is not infinite. + __ Sltiu(out, TMP, 1); + } +} + +// boolean java.lang.Float.isInfinite(float) +void IntrinsicLocationsBuilderMIPS::VisitFloatIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitFloatIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), Primitive::kPrimFloat, IsR6(), GetAssembler()); +} + +// boolean java.lang.Double.isInfinite(double) +void IntrinsicLocationsBuilderMIPS::VisitDoubleIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitDoubleIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), Primitive::kPrimDouble, IsR6(), GetAssembler()); +} + +static void GenHighestOneBit(LocationSummary* locations, + const Primitive::Type type, + bool isR6, + MipsAssembler* assembler) { + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + if (type == Primitive::kPrimLong) { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + if (isR6) { + __ ClzR6(TMP, in_hi); + } else { + __ ClzR2(TMP, in_hi); + } + __ LoadConst32(AT, 0x80000000); + __ Srlv(out_hi, AT, TMP); + __ And(out_hi, out_hi, in_hi); + if (isR6) { + __ ClzR6(TMP, in_lo); + } else { + __ ClzR2(TMP, in_lo); + } + __ Srlv(out_lo, AT, TMP); + __ And(out_lo, out_lo, in_lo); + if (isR6) { + __ Seleqz(out_lo, out_lo, out_hi); + } else { + __ Movn(out_lo, ZERO, out_hi); + } + } else { + Register in = locations->InAt(0).AsRegister(); + Register out = locations->Out().AsRegister(); + + if (isR6) { + __ ClzR6(TMP, in); + } else { + __ ClzR2(TMP, in); + } + __ LoadConst32(AT, 0x80000000); + __ Srlv(AT, AT, TMP); // Srlv shifts in the range of [0;31] bits (lower 5 bits of arg). + __ And(out, AT, in); // So this is required for 0 (=shift by 32). + } +} + +// int java.lang.Integer.highestOneBit(int) +void IntrinsicLocationsBuilderMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitIntegerHighestOneBit(HInvoke* invoke) { + GenHighestOneBit(invoke->GetLocations(), Primitive::kPrimInt, IsR6(), GetAssembler()); +} + +// long java.lang.Long.highestOneBit(long) +void IntrinsicLocationsBuilderMIPS::VisitLongHighestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke, Location::kOutputOverlap); +} + +void IntrinsicCodeGeneratorMIPS::VisitLongHighestOneBit(HInvoke* invoke) { + GenHighestOneBit(invoke->GetLocations(), Primitive::kPrimLong, IsR6(), GetAssembler()); +} + +static void GenLowestOneBit(LocationSummary* locations, + const Primitive::Type type, + bool isR6, + MipsAssembler* assembler) { + DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); + + if (type == Primitive::kPrimLong) { + Register in_lo = locations->InAt(0).AsRegisterPairLow(); + Register in_hi = locations->InAt(0).AsRegisterPairHigh(); + Register out_lo = locations->Out().AsRegisterPairLow(); + Register out_hi = locations->Out().AsRegisterPairHigh(); + + __ Subu(TMP, ZERO, in_lo); + __ And(out_lo, TMP, in_lo); + __ Subu(TMP, ZERO, in_hi); + __ And(out_hi, TMP, in_hi); + if (isR6) { + __ Seleqz(out_hi, out_hi, out_lo); + } else { + __ Movn(out_hi, ZERO, out_lo); + } + } else { + Register in = locations->InAt(0).AsRegister(); + Register out = locations->Out().AsRegister(); + + __ Subu(TMP, ZERO, in); + __ And(out, TMP, in); + } +} + +// int java.lang.Integer.lowestOneBit(int) +void IntrinsicLocationsBuilderMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitIntegerLowestOneBit(HInvoke* invoke) { + GenLowestOneBit(invoke->GetLocations(), Primitive::kPrimInt, IsR6(), GetAssembler()); +} + +// long java.lang.Long.lowestOneBit(long) +void IntrinsicLocationsBuilderMIPS::VisitLongLowestOneBit(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitLongLowestOneBit(HInvoke* invoke) { + GenLowestOneBit(invoke->GetLocations(), Primitive::kPrimLong, IsR6(), GetAssembler()); +} + // Unimplemented intrinsics. -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -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) -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) -UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(ThreadCurrentThread) -UNIMPLEMENTED_INTRINSIC(UnsafeGet) -UNIMPLEMENTED_INTRINSIC(UnsafeGetVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeGetLong) -UNIMPLEMENTED_INTRINSIC(UnsafeGetLongVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeGetObject) -UNIMPLEMENTED_INTRINSIC(UnsafeGetObjectVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePut) -UNIMPLEMENTED_INTRINSIC(UnsafePutOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePutObject) -UNIMPLEMENTED_INTRINSIC(UnsafePutObjectOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutObjectVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafePutLong) -UNIMPLEMENTED_INTRINSIC(UnsafePutLongOrdered) -UNIMPLEMENTED_INTRINSIC(UnsafePutLongVolatile) -UNIMPLEMENTED_INTRINSIC(UnsafeCASInt) -UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) -UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) -UNIMPLEMENTED_INTRINSIC(StringCompareTo) -UNIMPLEMENTED_INTRINSIC(StringIndexOf) -UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromBytes) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromChars) -UNIMPLEMENTED_INTRINSIC(StringNewStringFromString) - -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) - -UNIMPLEMENTED_INTRINSIC(MathCos) -UNIMPLEMENTED_INTRINSIC(MathSin) -UNIMPLEMENTED_INTRINSIC(MathAcos) -UNIMPLEMENTED_INTRINSIC(MathAsin) -UNIMPLEMENTED_INTRINSIC(MathAtan) -UNIMPLEMENTED_INTRINSIC(MathAtan2) -UNIMPLEMENTED_INTRINSIC(MathCbrt) -UNIMPLEMENTED_INTRINSIC(MathCosh) -UNIMPLEMENTED_INTRINSIC(MathExp) -UNIMPLEMENTED_INTRINSIC(MathExpm1) -UNIMPLEMENTED_INTRINSIC(MathHypot) -UNIMPLEMENTED_INTRINSIC(MathLog) -UNIMPLEMENTED_INTRINSIC(MathLog10) -UNIMPLEMENTED_INTRINSIC(MathNextAfter) -UNIMPLEMENTED_INTRINSIC(MathSinh) -UNIMPLEMENTED_INTRINSIC(MathTan) -UNIMPLEMENTED_INTRINSIC(MathTanh) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil) +UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor) +UNIMPLEMENTED_INTRINSIC(MIPS, MathRint) +UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGet) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLongVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObject) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObjectVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePut) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutOrdered) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObject) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectOrdered) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongOrdered) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject) + +UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck) +UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy) + +UNIMPLEMENTED_INTRINSIC(MIPS, MathCos) +UNIMPLEMENTED_INTRINSIC(MIPS, MathSin) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAcos) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAsin) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAtan) +UNIMPLEMENTED_INTRINSIC(MIPS, MathAtan2) +UNIMPLEMENTED_INTRINSIC(MIPS, MathCbrt) +UNIMPLEMENTED_INTRINSIC(MIPS, MathCosh) +UNIMPLEMENTED_INTRINSIC(MIPS, MathExp) +UNIMPLEMENTED_INTRINSIC(MIPS, MathExpm1) +UNIMPLEMENTED_INTRINSIC(MIPS, MathHypot) +UNIMPLEMENTED_INTRINSIC(MIPS, MathLog) +UNIMPLEMENTED_INTRINSIC(MIPS, MathLog10) +UNIMPLEMENTED_INTRINSIC(MIPS, MathNextAfter) +UNIMPLEMENTED_INTRINSIC(MIPS, MathSinh) +UNIMPLEMENTED_INTRINSIC(MIPS, MathTan) +UNIMPLEMENTED_INTRINSIC(MIPS, MathTanh) + +// 1.8. +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject) + +UNREACHABLE_INTRINSICS(MIPS) #undef __ diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index cf3a3657dea5e4a5aaf898cfb45ab8c6be20582c..cf973aa8419117c55e4b1dd2a86da900786c8070 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(codegen_in); @@ -339,130 +340,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitLongNumberOfTrailingZeros(HInvoke* invok GenNumberOfTrailingZeroes(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); } -static void GenRotateRight(HInvoke* invoke, - Primitive::Type type, - Mips64Assembler* assembler) { - DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); - - LocationSummary* locations = invoke->GetLocations(); - GpuRegister in = locations->InAt(0).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (invoke->InputAt(1)->IsIntConstant()) { - uint32_t shift = static_cast(invoke->InputAt(1)->AsIntConstant()->GetValue()); - if (type == Primitive::kPrimInt) { - shift &= 0x1f; - __ Rotr(out, in, shift); - } else { - shift &= 0x3f; - if (shift < 32) { - __ Drotr(out, in, shift); - } else { - shift &= 0x1f; - __ Drotr32(out, in, shift); - } - } - } else { - GpuRegister shamt = locations->InAt(1).AsRegister(); - if (type == Primitive::kPrimInt) { - __ Rotrv(out, in, shamt); - } else { - __ Drotrv(out, in, shamt); - } - } -} - -// int java.lang.Integer.rotateRight(int i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitIntegerRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateRight(HInvoke* invoke) { - GenRotateRight(invoke, Primitive::kPrimInt, GetAssembler()); -} - -// long java.lang.Long.rotateRight(long i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitLongRotateRight(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitLongRotateRight(HInvoke* invoke) { - GenRotateRight(invoke, Primitive::kPrimLong, GetAssembler()); -} - -static void GenRotateLeft(HInvoke* invoke, - Primitive::Type type, - Mips64Assembler* assembler) { - DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); - - LocationSummary* locations = invoke->GetLocations(); - GpuRegister in = locations->InAt(0).AsRegister(); - GpuRegister out = locations->Out().AsRegister(); - - if (invoke->InputAt(1)->IsIntConstant()) { - int32_t shift = -static_cast(invoke->InputAt(1)->AsIntConstant()->GetValue()); - if (type == Primitive::kPrimInt) { - shift &= 0x1f; - __ Rotr(out, in, shift); - } else { - shift &= 0x3f; - if (shift < 32) { - __ Drotr(out, in, shift); - } else { - shift &= 0x1f; - __ Drotr32(out, in, shift); - } - } - } else { - GpuRegister shamt = locations->InAt(1).AsRegister(); - if (type == Primitive::kPrimInt) { - __ Subu(TMP, ZERO, shamt); - __ Rotrv(out, in, TMP); - } else { - __ Dsubu(TMP, ZERO, shamt); - __ Drotrv(out, in, TMP); - } - } -} - -// int java.lang.Integer.rotateLeft(int i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitIntegerRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateLeft(HInvoke* invoke) { - GenRotateLeft(invoke, Primitive::kPrimInt, GetAssembler()); -} - -// long java.lang.Long.rotateLeft(long i, int distance) -void IntrinsicLocationsBuilderMIPS64::VisitLongRotateLeft(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kNoCall, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1))); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS64::VisitLongRotateLeft(HInvoke* invoke) { - GenRotateLeft(invoke, Primitive::kPrimLong, GetAssembler()); -} - static void GenReverse(LocationSummary* locations, Primitive::Type type, Mips64Assembler* assembler) { @@ -580,25 +457,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 rhs = locations->InAt(1).AsFpuRegister(); + FpuRegister a = locations->InAt(0).AsFpuRegister(); + FpuRegister b = locations->InAt(1).AsFpuRegister(); FpuRegister out = locations->Out().AsFpuRegister(); - 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 +539,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 +548,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 +557,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 +566,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 +576,55 @@ static void GenMinMax(LocationSummary* locations, GpuRegister rhs = locations->InAt(1).AsRegister(); GpuRegister out = locations->Out().AsRegister(); - // 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) { @@ -1427,10 +1356,10 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringCompareTo(HInvoke* invoke) { __ Beqzc(argument, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pStringCompareTo).Int32Value()); - __ Jalr(TMP); + __ Jalr(T9); __ Nop(); __ Bind(slow_path->GetExitLabel()); } @@ -1574,16 +1503,14 @@ static void GenerateStringIndexOf(HInvoke* invoke, DCHECK_EQ(tmp_reg, A2); // Start-index = 0. __ Clear(tmp_reg); - } else { - __ Slt(TMP, A2, ZERO); // if fromIndex < 0 - __ Seleqz(A2, A2, TMP); // fromIndex = 0 } __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pIndexOf).Int32Value()); - __ Jalr(TMP); + CheckEntrypointTypes(); + __ Jalr(T9); __ Nop(); if (slow_path != nullptr) { @@ -1632,7 +1559,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringIndexOfAfter(HInvoke* invoke) { invoke, GetAssembler(), codegen_, GetAllocator(), /* start_at_zero */ false); } -// java.lang.String.String(byte[] bytes) +// java.lang.StringFactory.newStringFromBytes(byte[] data, int high, int offset, int byteCount) void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -1656,17 +1583,18 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromBytes(HInvoke* invoke __ Beqzc(byte_array, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromBytes).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); - __ Jalr(TMP); + CheckEntrypointTypes(); + __ Jalr(T9); __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } -// java.lang.String.String(char[] value) +// java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, @@ -1682,25 +1610,30 @@ void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromChars(HInvoke* inv void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromChars(HInvoke* invoke) { Mips64Assembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromChars).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); - __ Jalr(TMP); + CheckEntrypointTypes(); + __ Jalr(T9); __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } -// java.lang.String.String(String original) +// java.lang.StringFactory.newStringFromString(String toCopy) void IntrinsicLocationsBuilderMIPS64::VisitStringNewStringFromString(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kCall, kIntrinsified); InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); - locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); - locations->SetInAt(2, Location::RegisterLocation(calling_convention.GetRegisterAt(2))); Location outLocation = calling_convention.GetReturnLocation(Primitive::kPrimInt); locations->SetOut(Location::RegisterLocation(outLocation.AsRegister())); } @@ -1715,70 +1648,93 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringNewStringFromString(HInvoke* invok __ Beqzc(string_to_copy, slow_path->GetEntryLabel()); __ LoadFromOffset(kLoadDoubleword, - TMP, + T9, TR, QUICK_ENTRYPOINT_OFFSET(kMips64DoublewordSize, pAllocStringFromString).Int32Value()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); - __ Jalr(TMP); + CheckEntrypointTypes(); + __ Jalr(T9); __ Nop(); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(IntegerBitCount) -UNIMPLEMENTED_INTRINSIC(LongBitCount) - -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) -UNIMPLEMENTED_INTRINSIC(MathRoundFloat) - -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) - -UNIMPLEMENTED_INTRINSIC(MathCos) -UNIMPLEMENTED_INTRINSIC(MathSin) -UNIMPLEMENTED_INTRINSIC(MathAcos) -UNIMPLEMENTED_INTRINSIC(MathAsin) -UNIMPLEMENTED_INTRINSIC(MathAtan) -UNIMPLEMENTED_INTRINSIC(MathAtan2) -UNIMPLEMENTED_INTRINSIC(MathCbrt) -UNIMPLEMENTED_INTRINSIC(MathCosh) -UNIMPLEMENTED_INTRINSIC(MathExp) -UNIMPLEMENTED_INTRINSIC(MathExpm1) -UNIMPLEMENTED_INTRINSIC(MathHypot) -UNIMPLEMENTED_INTRINSIC(MathLog) -UNIMPLEMENTED_INTRINSIC(MathLog10) -UNIMPLEMENTED_INTRINSIC(MathNextAfter) -UNIMPLEMENTED_INTRINSIC(MathSinh) -UNIMPLEMENTED_INTRINSIC(MathTan) -UNIMPLEMENTED_INTRINSIC(MathTanh) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +static void GenIsInfinite(LocationSummary* locations, + bool is64bit, + Mips64Assembler* assembler) { + FpuRegister in = locations->InAt(0).AsFpuRegister(); + GpuRegister out = locations->Out().AsRegister(); + + if (is64bit) { + __ ClassD(FTMP, in); + } else { + __ ClassS(FTMP, in); + } + __ Mfc1(out, FTMP); + __ Andi(out, out, kPositiveInfinity | kNegativeInfinity); + __ Sltu(out, ZERO, out); +} + +// boolean java.lang.Float.isInfinite(float) +void IntrinsicLocationsBuilderMIPS64::VisitFloatIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitFloatIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), /* is64bit */ false, GetAssembler()); +} + +// boolean java.lang.Double.isInfinite(double) +void IntrinsicLocationsBuilderMIPS64::VisitDoubleIsInfinite(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitDoubleIsInfinite(HInvoke* invoke) { + GenIsInfinite(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); +} + +UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(MIPS64, LongBitCount) + +UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathRoundFloat) + +UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(MIPS64, StringGetCharsNoCheck) +UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy) + +UNIMPLEMENTED_INTRINSIC(MIPS64, MathCos) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathSin) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAcos) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAsin) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathAtan2) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathCbrt) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathCosh) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathExp) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathExpm1) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathHypot) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathLog10) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathNextAfter) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathSinh) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathTan) +UNIMPLEMENTED_INTRINSIC(MIPS64, MathTanh) + +UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS64, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS64, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(MIPS64, LongLowestOneBit) + +// 1.8. +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject) + +UNREACHABLE_INTRINSICS(MIPS64) #undef __ diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h index e70afd29f0613a4a507c6f46400cb27b59ff2050..c1f9ae6425c0feacfe0758ba3626541bbadd9cf8 100644 --- a/compiler/optimizing/intrinsics_utils.h +++ b/compiler/optimizing/intrinsics_utils.h @@ -39,7 +39,7 @@ namespace art { template 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/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 260a8773fbb07073f57d299b9a1eb2b228997b47..95fdb9b3f61aefd4f7bf427f57f2ddcb9c23f0b5 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1546,6 +1546,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromBytes(HInvoke* invoke) { __ j(kEqual, slow_path->GetEntryLabel()); __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromBytes))); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1564,7 +1565,14 @@ void IntrinsicLocationsBuilderX86::VisitStringNewStringFromChars(HInvoke* invoke void IntrinsicCodeGeneratorX86::VisitStringNewStringFromChars(HInvoke* invoke) { X86Assembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromChars))); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } @@ -1588,6 +1596,7 @@ void IntrinsicCodeGeneratorX86::VisitStringNewStringFromString(HInvoke* invoke) __ j(kEqual, slow_path->GetEntryLabel()); __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocStringFromString))); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -2621,39 +2630,24 @@ void IntrinsicCodeGeneratorX86::VisitLongNumberOfTrailingZeros(HInvoke* invoke) GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(MathRoundDouble) -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) - -UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) -UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) -UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) -UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) - -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) - -#undef UNIMPLEMENTED_INTRINSIC +UNIMPLEMENTED_INTRINSIC(X86, MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(X86, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(X86, SystemArrayCopy) +UNIMPLEMENTED_INTRINSIC(X86, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86, DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) + +// 1.8. +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject) + +UNREACHABLE_INTRINSICS(X86) #undef __ diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 93e8c00e5ad7f89fc69c5e7f922f3987c8b6bf44..9e568f7b4f3c6873818a3c60ea9a0d157c84e588 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -1641,6 +1641,7 @@ void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromBytes(HInvoke* invoke __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromBytes), /* no_rip */ true)); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -1659,8 +1660,15 @@ void IntrinsicLocationsBuilderX86_64::VisitStringNewStringFromChars(HInvoke* inv void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromChars(HInvoke* invoke) { X86_64Assembler* assembler = GetAssembler(); + // No need to emit code checking whether `locations->InAt(2)` is a null + // pointer, as callers of the native method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // all include a null check on `data` before calling that method. __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromChars), /* no_rip */ true)); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } @@ -1685,6 +1693,7 @@ void IntrinsicCodeGeneratorX86_64::VisitStringNewStringFromString(HInvoke* invok __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocStringFromString), /* no_rip */ true)); + CheckEntrypointTypes(); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); __ Bind(slow_path->GetExitLabel()); } @@ -2705,32 +2714,18 @@ void IntrinsicCodeGeneratorX86_64::VisitLongNumberOfTrailingZeros(HInvoke* invok GenTrailingZeros(GetAssembler(), codegen_, invoke, /* is_long */ true); } -// Unimplemented intrinsics. - -#define UNIMPLEMENTED_INTRINSIC(Name) \ -void IntrinsicLocationsBuilderX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} \ -void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ -} - -UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) - -UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) -UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) -// Handled as HIR instructions. -UNIMPLEMENTED_INTRINSIC(FloatIsNaN) -UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) -UNIMPLEMENTED_INTRINSIC(IntegerCompare) -UNIMPLEMENTED_INTRINSIC(LongCompare) -UNIMPLEMENTED_INTRINSIC(IntegerSignum) -UNIMPLEMENTED_INTRINSIC(LongSignum) +// 1.8. +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddInt) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddLong) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetInt) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetLong) +UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetObject) -#undef UNIMPLEMENTED_INTRINSIC +UNREACHABLE_INTRINSICS(X86_64) #undef __ diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index d6ab0039997d63bd190a098b67d6518f33066266..98766a31a639b1a2bb8694926caad2369ef959ab 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -127,6 +127,9 @@ void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) { // Remove the block from the list of blocks, so that further analyses // never see it. blocks_[i] = nullptr; + if (block->IsExitBlock()) { + SetExitBlock(nullptr); + } } } } @@ -1178,19 +1181,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()) { @@ -1870,7 +1873,7 @@ void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) { DCHECK(block->GetPhis().IsEmpty()); if (block->IsExitBlock()) { - exit_block_ = nullptr; + SetExitBlock(nullptr); } RemoveElement(reverse_post_order_, block); @@ -1976,34 +1979,6 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { at->MergeWithInlined(first); exit_block_->ReplaceWith(to); - // Update all predecessors of the exit block (now the `to` block) - // to not `HReturn` but `HGoto` instead. - bool returns_void = to->GetPredecessors()[0]->GetLastInstruction()->IsReturnVoid(); - if (to->GetPredecessors().size() == 1) { - HBasicBlock* predecessor = to->GetPredecessors()[0]; - HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { - return_value = last->InputAt(0); - } - predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); - predecessor->RemoveInstruction(last); - } else { - if (!returns_void) { - // There will be multiple returns. - return_value = new (allocator) HPhi( - allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); - to->AddPhi(return_value->AsPhi()); - } - for (HBasicBlock* predecessor : to->GetPredecessors()) { - HInstruction* last = predecessor->GetLastInstruction(); - if (!returns_void) { - return_value->AsPhi()->AddInput(last->InputAt(0)); - } - predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); - predecessor->RemoveInstruction(last); - } - } - // Update the meta information surrounding blocks: // (1) the graph they are now in, // (2) the reverse post order of that graph, @@ -2048,11 +2023,36 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { // Only `to` can become a back edge, as the inlined blocks // are predecessors of `to`. UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true); - } - // Update the next instruction id of the outer graph, so that instructions - // added later get bigger ids than those in the inner graph. - outer_graph->SetCurrentInstructionId(GetNextInstructionId()); + // Update all predecessors of the exit block (now the `to` block) + // to not `HReturn` but `HGoto` instead. + bool returns_void = to->GetPredecessors()[0]->GetLastInstruction()->IsReturnVoid(); + if (to->GetPredecessors().size() == 1) { + HBasicBlock* predecessor = to->GetPredecessors()[0]; + HInstruction* last = predecessor->GetLastInstruction(); + if (!returns_void) { + return_value = last->InputAt(0); + } + predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); + predecessor->RemoveInstruction(last); + } else { + if (!returns_void) { + // There will be multiple returns. + return_value = new (allocator) HPhi( + allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke->GetType()), to->GetDexPc()); + to->AddPhi(return_value->AsPhi()); + } + for (HBasicBlock* predecessor : to->GetPredecessors()) { + HInstruction* last = predecessor->GetLastInstruction(); + if (!returns_void) { + DCHECK(last->IsReturn()); + return_value->AsPhi()->AddInput(last->InputAt(0)); + } + predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc())); + predecessor->RemoveInstruction(last); + } + } + } // Walk over the entry block and: // - Move constants from the entry block to the outer_graph's entry block, @@ -2181,7 +2181,9 @@ static void CheckAgainstUpperBound(ReferenceTypeInfo rti, ReferenceTypeInfo uppe DCHECK(upper_bound_rti.IsSupertypeOf(rti)) << " upper_bound_rti: " << upper_bound_rti << " rti: " << rti; - DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact()); + DCHECK(!upper_bound_rti.GetTypeHandle()->CannotBeAssignedFromOtherTypes() || rti.IsExact()) + << " upper_bound_rti: " << upper_bound_rti + << " rti: " << rti; } } @@ -2196,7 +2198,8 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) { CheckAgainstUpperBound(rti, AsBoundType()->GetUpperBound()); } } - reference_type_info_ = rti; + reference_type_handle_ = rti.GetTypeHandle(); + SetPackedFlag(rti.IsExact()); } void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) { @@ -2207,17 +2210,19 @@ void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be CheckAgainstUpperBound(GetReferenceTypeInfo(), upper_bound); } upper_bound_ = upper_bound; - upper_can_be_null_ = can_be_null; + SetPackedFlag(can_be_null); } -ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} - -ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) - : type_handle_(type_handle), is_exact_(is_exact) { +ReferenceTypeInfo ReferenceTypeInfo::Create(TypeHandle type_handle, bool is_exact) { if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); DCHECK(IsValidHandle(type_handle)); + if (!is_exact) { + DCHECK(!type_handle->CannotBeAssignedFromOtherTypes()) + << "Callers of ReferenceTypeInfo::Create should ensure is_exact is properly computed"; + } } + return ReferenceTypeInfo(type_handle, is_exact); } std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index dcbfbe0547baa6b48a420cc801542826e7974333..1bb5f5df51169e5e7249be338b30be62b14af987 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -26,6 +26,7 @@ #include "base/arena_object.h" #include "base/stl_util.h" #include "dex/compiler_enums.h" +#include "dex_instruction-inl.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" @@ -154,8 +155,9 @@ class ReferenceTypeInfo : ValueObject { public: typedef Handle TypeHandle; - static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) { - // The constructor will check that the type_handle is valid. + static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact); + + static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) { return ReferenceTypeInfo(type_handle, is_exact); } @@ -254,8 +256,9 @@ class ReferenceTypeInfo : ValueObject { } private: - ReferenceTypeInfo(); - ReferenceTypeInfo(TypeHandle type_handle, bool is_exact); + ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {} + ReferenceTypeInfo(TypeHandle type_handle, bool is_exact) + : type_handle_(type_handle), is_exact_(is_exact) { } // The class of the object. TypeHandle type_handle_; @@ -385,6 +388,7 @@ class HGraph : public ArenaObject { } void SetCurrentInstructionId(int32_t id) { + DCHECK_GE(id, current_instruction_id_); current_instruction_id_ = id; } @@ -505,6 +509,8 @@ class HGraph : public ArenaObject { // before cursor. HInstruction* InsertOppositeCondition(HInstruction* cond, HInstruction* cursor); + ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; } + private: void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const; void RemoveDeadBlocks(const ArenaBitVector& visited); @@ -1254,6 +1260,17 @@ class HLoopInformationOutwardIterator : public ValueObject { M(UShr, BinaryOperation) \ M(Xor, BinaryOperation) \ +/* + * Instructions, shared across several (not all) architectures. + */ +#if !defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_ENABLE_CODEGEN_arm64) +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) +#else +#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ + M(BitwiseNegatedRight, Instruction) \ + M(MultiplyAccumulate, Instruction) +#endif + #ifndef ART_ENABLE_CODEGEN_arm #define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) #else @@ -1266,8 +1283,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ M(Arm64DataProcWithShifterOp, Instruction) \ - M(Arm64IntermediateAddress, Instruction) \ - M(Arm64MultiplyAccumulate, Instruction) + M(Arm64IntermediateAddress, Instruction) #endif #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) @@ -1288,6 +1304,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION(M) \ FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \ + FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \ FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \ @@ -1844,13 +1861,15 @@ class HInstruction : public ArenaObject { dex_pc_(dex_pc), id_(-1), ssa_index_(-1), - emitted_at_use_site_(false), + packed_fields_(0u), environment_(nullptr), locations_(nullptr), live_interval_(nullptr), lifetime_position_(kNoLifetime), side_effects_(side_effects), - reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {} + reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) { + SetPackedFlag(ReferenceTypeInfo::CreateInvalid().IsExact()); + } virtual ~HInstruction() {} @@ -1919,7 +1938,8 @@ class HInstruction : public ArenaObject { ReferenceTypeInfo GetReferenceTypeInfo() const { DCHECK_EQ(GetType(), Primitive::kPrimNot); - return reference_type_info_; + return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_, + GetPackedFlag());; } void AddUseAt(HInstruction* user, size_t index) { @@ -2108,13 +2128,45 @@ class HInstruction : public ArenaObject { // The caller must ensure that this is safe to do. void RemoveEnvironmentUsers(); - bool IsEmittedAtUseSite() const { return emitted_at_use_site_; } - void MarkEmittedAtUseSite() { emitted_at_use_site_ = true; } + bool IsEmittedAtUseSite() const { return GetPackedFlag(); } + void MarkEmittedAtUseSite() { SetPackedFlag(true); } protected: + // If set, the machine code for this instruction is assumed to be generated by + // its users. Used by liveness analysis to compute use positions accordingly. + static constexpr size_t kFlagEmittedAtUseSite = 0u; + static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1; + static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + virtual const HUserRecord InputRecordAt(size_t i) const = 0; virtual void SetRawInputRecordAt(size_t index, const HUserRecord& input) = 0; + uint32_t GetPackedFields() const { + return packed_fields_; + } + + template + bool GetPackedFlag() const { + return (packed_fields_ & (1u << flag)) != 0u; + } + + template + void SetPackedFlag(bool value = true) { + packed_fields_ = (packed_fields_ & ~(1u << flag)) | ((value ? 1u : 0u) << flag); + } + + template + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint(static_cast(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + private: void RemoveEnvironmentUser(HUseListNode* use_node) { env_uses_.Remove(use_node); } @@ -2131,9 +2183,8 @@ class HInstruction : public ArenaObject { // When doing liveness analysis, instructions that have uses get an SSA index. int ssa_index_; - // If set, the machine code for this instruction is assumed to be generated by - // its users. Used by liveness analysis to compute use positions accordingly. - bool emitted_at_use_site_; + // Packed fields. + uint32_t packed_fields_; // List of instructions that have this instruction as input. HUseList uses_; @@ -2157,8 +2208,10 @@ class HInstruction : public ArenaObject { SideEffects side_effects_; + // The reference handle part of the reference type info. + // The IsExact() flag is stored in packed fields. // TODO: for primitive types this should be marked as invalid. - ReferenceTypeInfo reference_type_info_; + ReferenceTypeInfo::TypeHandle reference_type_handle_; friend class GraphChecker; friend class HBasicBlock; @@ -2284,13 +2337,23 @@ template class HExpression : public HTemplateInstruction { public: HExpression(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc) - : HTemplateInstruction(side_effects, dex_pc), type_(type) {} + : HTemplateInstruction(side_effects, dex_pc) { + this->template SetPackedField(type); + } virtual ~HExpression() {} - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { + return TypeField::Decode(this->GetPackedFields()); + } protected: - Primitive::Type type_; + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize; + static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using TypeField = BitField; }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -2580,13 +2643,16 @@ class HIf : public HTemplateInstruction<1> { // higher indices in no particular order. class HTryBoundary : public HTemplateInstruction<0> { public: - enum BoundaryKind { + enum class BoundaryKind { kEntry, kExit, + kLast = kExit }; explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc) - : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {} + : HTemplateInstruction(SideEffects::None(), dex_pc) { + SetPackedField(kind); + } bool IsControlFlow() const OVERRIDE { return true; } @@ -2612,14 +2678,22 @@ class HTryBoundary : public HTemplateInstruction<0> { } } - bool IsEntry() const { return kind_ == BoundaryKind::kEntry; } + BoundaryKind GetBoundaryKind() const { return GetPackedField(); } + bool IsEntry() const { return GetBoundaryKind() == BoundaryKind::kEntry; } bool HasSameExceptionHandlersAs(const HTryBoundary& other) const; DECLARE_INSTRUCTION(TryBoundary); private: - const BoundaryKind kind_; + static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldBoundaryKindSize = + MinimumBitsToStore(static_cast(BoundaryKind::kLast)); + static constexpr size_t kNumberOfTryBoundaryPackedBits = + kFieldBoundaryKind + kFieldBoundaryKindSize; + static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BoundaryKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HTryBoundary); }; @@ -2665,9 +2739,10 @@ class HCurrentMethod : public HExpression<0> { // of a class. class HClassTableGet : public HExpression<1> { public: - enum TableKind { + enum class TableKind { kVTable, kIMTable, + kLast = kIMTable }; HClassTableGet(HInstruction* cls, Primitive::Type type, @@ -2675,26 +2750,33 @@ class HClassTableGet : public HExpression<1> { size_t index, uint32_t dex_pc) : HExpression(type, SideEffects::None(), dex_pc), - index_(index), - table_kind_(kind) { + index_(index) { + SetPackedField(kind); SetRawInputAt(0, cls); } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { return other->AsClassTableGet()->GetIndex() == index_ && - other->AsClassTableGet()->GetTableKind() == table_kind_; + other->AsClassTableGet()->GetPackedFields() == GetPackedFields(); } - TableKind GetTableKind() const { return table_kind_; } + TableKind GetTableKind() const { return GetPackedField(); } size_t GetIndex() const { return index_; } DECLARE_INSTRUCTION(ClassTableGet); private: + static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTableKindSize = + MinimumBitsToStore(static_cast(TableKind::kLast)); + static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize; + static_assert(kNumberOfClassTableGetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using TableKindField = BitField; + // The index of the ArtMethod in the table. const size_t index_; - const TableKind table_kind_; DISALLOW_COPY_AND_ASSIGN(HClassTableGet); }; @@ -2828,20 +2910,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; @@ -2866,6 +2943,7 @@ enum class ComparisonBias { kNoBias, // bias is not applicable (i.e. for long operation) kGtBias, // return 1 for NaN comparisons kLtBias, // return -1 for NaN comparisons + kLast = kLtBias }; std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); @@ -2873,8 +2951,9 @@ std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs); class HCondition : public HBinaryOperation { public: HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc), - bias_(ComparisonBias::kNoBias) {} + : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc) { + SetPackedField(ComparisonBias::kNoBias); + } // For code generation purposes, returns whether this instruction is just before // `instruction`, and disregard moves in between. @@ -2886,27 +2965,49 @@ class HCondition : public HBinaryOperation { virtual IfCondition GetOppositeCondition() const = 0; - bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; } - ComparisonBias GetBias() const { return bias_; } - void SetBias(ComparisonBias bias) { bias_ = bias; } + bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; } + bool IsLtBias() const { return GetBias() == ComparisonBias::kLtBias; } + + ComparisonBias GetBias() const { return GetPackedField(); } + void SetBias(ComparisonBias bias) { SetPackedField(bias); } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCondition()->bias_; + return GetPackedFields() == other->AsCondition()->GetPackedFields(); } bool IsFPConditionTrueIfNaN() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); IfCondition if_cond = GetCondition(); - return IsGtBias() ? ((if_cond == kCondGT) || (if_cond == kCondGE)) : (if_cond == kCondNE); + if (if_cond == kCondNE) { + return true; + } else if (if_cond == kCondEQ) { + return false; + } + return ((if_cond == kCondGT) || (if_cond == kCondGE)) && IsGtBias(); } bool IsFPConditionFalseIfNaN() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); IfCondition if_cond = GetCondition(); - return IsGtBias() ? ((if_cond == kCondLT) || (if_cond == kCondLE)) : (if_cond == kCondEQ); + if (if_cond == kCondEQ) { + return true; + } else if (if_cond == kCondNE) { + return false; + } + return ((if_cond == kCondLT) || (if_cond == kCondLE)) && IsGtBias(); } protected: + // Needed if we merge a HCompare into a HCondition. + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast(ComparisonBias::kLast)); + static constexpr size_t kNumberOfConditionPackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfConditionPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField; + template int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); } @@ -2924,9 +3025,6 @@ class HCondition : public HBinaryOperation { } private: - // Needed if we merge a HCompare into a HCondition. - ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCondition); }; @@ -3339,8 +3437,8 @@ class HCompare : public HBinaryOperation { first, second, SideEffectsForArchRuntimeCalls(type), - dex_pc), - bias_(bias) { + dex_pc) { + SetPackedField(bias); DCHECK_EQ(type, first->GetType()); DCHECK_EQ(type, second->GetType()); } @@ -3375,16 +3473,16 @@ class HCompare : public HBinaryOperation { } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return bias_ == other->AsCompare()->bias_; + return GetPackedFields() == other->AsCompare()->GetPackedFields(); } - ComparisonBias GetBias() const { return bias_; } + ComparisonBias GetBias() const { return GetPackedField(); } // Does this compare instruction have a "gt bias" (vs an "lt bias")? - // Only meaninfgul for floating-point comparisons. + // Only meaningful for floating-point comparisons. bool IsGtBias() const { DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType(); - return bias_ == ComparisonBias::kGtBias; + return GetBias() == ComparisonBias::kGtBias; } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) { @@ -3395,6 +3493,15 @@ class HCompare : public HBinaryOperation { DECLARE_INSTRUCTION(Compare); protected: + static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldComparisonBiasSize = + MinimumBitsToStore(static_cast(ComparisonBias::kLast)); + static constexpr size_t kNumberOfComparePackedBits = + kFieldComparisonBias + kFieldComparisonBiasSize; + static_assert(kNumberOfComparePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ComparisonBiasField = + BitField; + // Return an integer constant containing the result of a comparison evaluated at compile time. HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const { DCHECK(value == -1 || value == 0 || value == 1) << value; @@ -3402,8 +3509,6 @@ class HCompare : public HBinaryOperation { } private: - const ComparisonBias bias_; - DISALLOW_COPY_AND_ASSIGN(HCompare); }; @@ -3471,9 +3576,9 @@ class HNewInstance : public HExpression<2> { : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc), type_index_(type_index), dex_file_(dex_file), - can_throw_(can_throw), - finalizable_(finalizable), entrypoint_(entrypoint) { + SetPackedFlag(can_throw); + SetPackedFlag(finalizable); SetRawInputAt(0, cls); SetRawInputAt(1, current_method); } @@ -3487,9 +3592,9 @@ class HNewInstance : public HExpression<2> { // It may throw when called on type that's not instantiable/accessible. // It can throw OOME. // TODO: distinguish between the two cases so we can for example allow allocation elimination. - bool CanThrow() const OVERRIDE { return can_throw_ || true; } + bool CanThrow() const OVERRIDE { return GetPackedFlag() || true; } - bool IsFinalizable() const { return finalizable_; } + bool IsFinalizable() const { return GetPackedFlag(); } bool CanBeNull() const OVERRIDE { return false; } @@ -3504,10 +3609,14 @@ class HNewInstance : public HExpression<2> { DECLARE_INSTRUCTION(NewInstance); private: + static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1; + static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; + static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const uint16_t type_index_; const DexFile& dex_file_; - const bool can_throw_; - const bool finalizable_; QuickEntrypointEnum entrypoint_; DISALLOW_COPY_AND_ASSIGN(HNewInstance); @@ -3557,12 +3666,14 @@ class HInvoke : public HInstruction { // inputs at the end of their list of inputs. uint32_t GetNumberOfArguments() const { return number_of_arguments_; } - Primitive::Type GetType() const OVERRIDE { return return_type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField(); } uint32_t GetDexMethodIndex() const { return dex_method_index_; } const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); } - InvokeType GetOriginalInvokeType() const { return original_invoke_type_; } + InvokeType GetOriginalInvokeType() const { + return GetPackedField(); + } Intrinsics GetIntrinsic() const { return intrinsic_; @@ -3577,7 +3688,7 @@ class HInvoke : public HInstruction { return GetEnvironment()->IsFromInlinedInvoke(); } - bool CanThrow() const OVERRIDE { return can_throw_; } + bool CanThrow() const OVERRIDE { return GetPackedFlag(); } bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); } @@ -3598,6 +3709,20 @@ class HInvoke : public HInstruction { DECLARE_ABSTRACT_INSTRUCTION(Invoke); protected: + static constexpr size_t kFieldOriginalInvokeType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldOriginalInvokeTypeSize = + MinimumBitsToStore(static_cast(kMaxInvokeType)); + static constexpr size_t kFieldReturnType = + kFieldOriginalInvokeType + kFieldOriginalInvokeTypeSize; + static constexpr size_t kFieldReturnTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize; + static constexpr size_t kNumberOfInvokePackedBits = kFlagCanThrow + 1; + static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using OriginalInvokeTypeField = + BitField; + using ReturnTypeField = BitField; + HInvoke(ArenaAllocator* arena, uint32_t number_of_arguments, uint32_t number_of_other_inputs, @@ -3610,12 +3735,12 @@ class HInvoke : public HInstruction { number_of_arguments_(number_of_arguments), inputs_(number_of_arguments + number_of_other_inputs, arena->Adapter(kArenaAllocInvokeInputs)), - return_type_(return_type), dex_method_index_(dex_method_index), - original_invoke_type_(original_invoke_type), - can_throw_(true), intrinsic_(Intrinsics::kNone), intrinsic_optimizations_(0) { + SetPackedField(return_type); + SetPackedField(original_invoke_type); + SetPackedFlag(true); } const HUserRecord InputRecordAt(size_t index) const OVERRIDE { @@ -3626,14 +3751,11 @@ class HInvoke : public HInstruction { inputs_[index] = input; } - void SetCanThrow(bool can_throw) { can_throw_ = can_throw; } + void SetCanThrow(bool can_throw) { SetPackedFlag(can_throw); } uint32_t number_of_arguments_; ArenaVector> inputs_; - const Primitive::Type return_type_; const uint32_t dex_method_index_; - const InvokeType original_invoke_type_; - bool can_throw_; Intrinsics intrinsic_; // A magic word holding optimizations for intrinsics. See intrinsics.h. @@ -3674,6 +3796,7 @@ class HInvokeStaticOrDirect : public HInvoke { kNone, // Class already initialized. kExplicit, // Static call having explicit clinit check as last input. kImplicit, // Static call implicitly requiring a clinit check. + kLast = kImplicit }; // Determines how to load the target ArtMethod*. @@ -3694,7 +3817,7 @@ class HInvokeStaticOrDirect : public HInvoke { // the image relocatable or not. kDirectAddressWithFixup, - // Load from resoved methods array in the dex cache using a PC-relative load. + // Load from resolved methods array in the dex cache using a PC-relative load. // Used when we need to use the dex cache, for example for invoke-static that // may cause class initialization (the entry may point to a resolution method), // and we know that we can access the dex cache arrays using a PC-relative load. @@ -3766,10 +3889,11 @@ class HInvokeStaticOrDirect : public HInvoke { dex_pc, method_index, original_invoke_type), - optimized_invoke_type_(optimized_invoke_type), - clinit_check_requirement_(clinit_check_requirement), target_method_(target_method), - dispatch_info_(dispatch_info) { } + dispatch_info_(dispatch_info) { + SetPackedField(optimized_invoke_type); + SetPackedField(clinit_check_requirement); + } void SetDispatchInfo(const DispatchInfo& dispatch_info) { bool had_current_method_input = HasCurrentMethodInput(); @@ -3801,7 +3925,7 @@ class HInvokeStaticOrDirect : public HInvoke { } bool CanBeNull() const OVERRIDE { - return return_type_ == Primitive::kPrimNot && !IsStringInit(); + return GetPackedField() == Primitive::kPrimNot && !IsStringInit(); } // Get the index of the special input, if any. @@ -3812,9 +3936,12 @@ class HInvokeStaticOrDirect : public HInvoke { uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); } bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); } - InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; } + InvokeType GetOptimizedInvokeType() const { + return GetPackedField(); + } + void SetOptimizedInvokeType(InvokeType invoke_type) { - optimized_invoke_type_ = invoke_type; + SetPackedField(invoke_type); } MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; } @@ -3861,7 +3988,9 @@ class HInvokeStaticOrDirect : public HInvoke { return dispatch_info_.direct_code_ptr; } - ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; } + ClinitCheckRequirement GetClinitCheckRequirement() const { + return GetPackedField(); + } // Is this instruction a call to a static method? bool IsStatic() const { @@ -3879,7 +4008,7 @@ class HInvokeStaticOrDirect : public HInvoke { DCHECK(last_input->IsLoadClass() || last_input->IsClinitCheck()) << last_input->DebugName(); RemoveAsUserOfInput(last_input_index); inputs_.pop_back(); - clinit_check_requirement_ = new_requirement; + SetPackedField(new_requirement); DCHECK(!IsStaticWithExplicitClinitCheck()); } @@ -3895,13 +4024,13 @@ class HInvokeStaticOrDirect : public HInvoke { // Is this a call to a static method whose declaring class has an // explicit initialization check in the graph? bool IsStaticWithExplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kExplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kExplicit); } // Is this a call to a static method whose declaring class has an // implicit intialization check requirement? bool IsStaticWithImplicitClinitCheck() const { - return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit); + return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kImplicit); } // Does this method load kind need the current method as an input? @@ -3930,8 +4059,23 @@ class HInvokeStaticOrDirect : public HInvoke { void RemoveInputAt(size_t index); private: - InvokeType optimized_invoke_type_; - ClinitCheckRequirement clinit_check_requirement_; + static constexpr size_t kFieldOptimizedInvokeType = kNumberOfInvokePackedBits; + static constexpr size_t kFieldOptimizedInvokeTypeSize = + MinimumBitsToStore(static_cast(kMaxInvokeType)); + static constexpr size_t kFieldClinitCheckRequirement = + kFieldOptimizedInvokeType + kFieldOptimizedInvokeTypeSize; + static constexpr size_t kFieldClinitCheckRequirementSize = + MinimumBitsToStore(static_cast(ClinitCheckRequirement::kLast)); + static constexpr size_t kNumberOfInvokeStaticOrDirectPackedBits = + kFieldClinitCheckRequirement + kFieldClinitCheckRequirementSize; + static_assert(kNumberOfInvokeStaticOrDirectPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using OptimizedInvokeTypeField = + BitField; + using ClinitCheckRequirementField = BitField; + // The target method may refer to different dex file or method index than the original // invoke. This happens for sharpened calls and for calls where a method was redeclared // in derived class to increase visibility. @@ -4312,8 +4456,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(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4358,8 +4500,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(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4405,8 +4545,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(y)`. HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); @@ -4442,21 +4580,12 @@ class HAnd : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template - auto Compute(T x, U y) const -> decltype(x & y) { return x & y; } + template 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()); @@ -4488,21 +4617,12 @@ class HOr : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template - auto Compute(T x, U y) const -> decltype(x | y) { return x | y; } + template 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()); @@ -4534,21 +4654,12 @@ class HXor : public HBinaryOperation { bool IsCommutative() const OVERRIDE { return true; } - template - auto Compute(T x, U y) const -> decltype(x ^ y) { return x ^ y; } + template 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()); @@ -4630,32 +4741,35 @@ class HParameterValue : public HExpression<0> { : HExpression(parameter_type, SideEffects::None(), kNoDexPc), dex_file_(dex_file), type_index_(type_index), - index_(index), - is_this_(is_this), - can_be_null_(!is_this) {} + index_(index) { + SetPackedFlag(is_this); + SetPackedFlag(!is_this); + } const DexFile& GetDexFile() const { return dex_file_; } uint16_t GetTypeIndex() const { return type_index_; } uint8_t GetIndex() const { return index_; } - bool IsThis() const { return is_this_; } + bool IsThis() const { return GetPackedFlag(); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag(can_be_null); } DECLARE_INSTRUCTION(ParameterValue); private: + // Whether or not the parameter value corresponds to 'this' argument. + static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1; + static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const DexFile& dex_file_; const uint16_t type_index_; // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; - // Whether or not the parameter value corresponds to 'this' argument. - const bool is_this_; - - bool can_be_null_; - DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; @@ -4780,14 +4894,14 @@ class HPhi : public HInstruction { uint32_t dex_pc = kNoDexPc) : HInstruction(SideEffects::None(), dex_pc), inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)), - reg_number_(reg_number), - type_(ToPhiType(type)), - // Phis are constructed live and marked dead if conflicting or unused. - // Individual steps of SsaBuilder should assume that if a phi has been - // marked dead, it can be ignored and will be removed by SsaPhiElimination. - is_live_(true), - can_be_null_(true) { - DCHECK_NE(type_, Primitive::kPrimVoid); + reg_number_(reg_number) { + SetPackedField(ToPhiType(type)); + DCHECK_NE(GetType(), Primitive::kPrimVoid); + // Phis are constructed live and marked dead if conflicting or unused. + // Individual steps of SsaBuilder should assume that if a phi has been + // marked dead, it can be ignored and will be removed by SsaPhiElimination. + SetPackedFlag(true); + SetPackedFlag(true); } // Returns a type equivalent to the given `type`, but that a `HPhi` can hold. @@ -4810,27 +4924,27 @@ class HPhi : public HInstruction { void AddInput(HInstruction* input); void RemoveInputAt(size_t index); - Primitive::Type GetType() const OVERRIDE { return type_; } + Primitive::Type GetType() const OVERRIDE { return GetPackedField(); } void SetType(Primitive::Type new_type) { // Make sure that only valid type changes occur. The following are allowed: // (1) int -> float/ref (primitive type propagation), // (2) long -> double (primitive type propagation). - DCHECK(type_ == new_type || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || - (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || - (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); - type_ = new_type; + DCHECK(GetType() == new_type || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) || + (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) || + (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble)); + SetPackedField(new_type); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } - void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag(); } + void SetCanBeNull(bool can_be_null) { SetPackedFlag(can_be_null); } uint32_t GetRegNumber() const { return reg_number_; } - void SetDead() { is_live_ = false; } - void SetLive() { is_live_ = true; } - bool IsDead() const { return !is_live_; } - bool IsLive() const { return is_live_; } + void SetDead() { SetPackedFlag(false); } + void SetLive() { SetPackedFlag(true); } + bool IsDead() const { return !IsLive(); } + bool IsLive() const { return GetPackedFlag(); } bool IsVRegEquivalentOf(HInstruction* other) const { return other != nullptr @@ -4865,11 +4979,17 @@ class HPhi : public HInstruction { } private: + static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize; + static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1; + static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeField = BitField; + ArenaVector > inputs_; const uint32_t reg_number_; - Primitive::Type type_; - bool is_live_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HPhi); }; @@ -5008,8 +5128,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag(true); SetRawInputAt(0, object); SetRawInputAt(1, value); } @@ -5023,14 +5143,18 @@ class HInstanceFieldSet : public HTemplateInstruction<2> { Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag(); } + void ClearValueCanBeNull() { SetPackedFlag(false); } DECLARE_INSTRUCTION(InstanceFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; @@ -5099,11 +5223,11 @@ class HArraySet : public HTemplateInstruction<3> { SideEffects::ArrayWriteOfType(expected_component_type).Union( SideEffectsForArchRuntimeCalls(value->GetType())).Union( additional_side_effects), - dex_pc), - expected_component_type_(expected_component_type), - needs_type_check_(value->GetType() == Primitive::kPrimNot), - value_can_be_null_(true), - static_type_of_array_is_object_array_(false) { + dex_pc) { + SetPackedField(expected_component_type); + SetPackedFlag(value->GetType() == Primitive::kPrimNot); + SetPackedFlag(true); + SetPackedFlag(false); SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); @@ -5111,11 +5235,11 @@ class HArraySet : public HTemplateInstruction<3> { bool NeedsEnvironment() const OVERRIDE { // We call a runtime method to throw ArrayStoreException. - return needs_type_check_; + return NeedsTypeCheck(); } // Can throw ArrayStoreException. - bool CanThrow() const OVERRIDE { return needs_type_check_; } + bool CanThrow() const OVERRIDE { return NeedsTypeCheck(); } bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE { // TODO: Same as for ArrayGet. @@ -5123,20 +5247,22 @@ class HArraySet : public HTemplateInstruction<3> { } void ClearNeedsTypeCheck() { - needs_type_check_ = false; + SetPackedFlag(false); } void ClearValueCanBeNull() { - value_can_be_null_ = false; + SetPackedFlag(false); } void SetStaticTypeOfArrayIsObjectArray() { - static_type_of_array_is_object_array_ = true; + SetPackedFlag(true); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - bool NeedsTypeCheck() const { return needs_type_check_; } - bool StaticTypeOfArrayIsObjectArray() const { return static_type_of_array_is_object_array_; } + bool GetValueCanBeNull() const { return GetPackedFlag(); } + bool NeedsTypeCheck() const { return GetPackedFlag(); } + bool StaticTypeOfArrayIsObjectArray() const { + return GetPackedFlag(); + } HInstruction* GetArray() const { return InputAt(0); } HInstruction* GetIndex() const { return InputAt(1); } @@ -5150,11 +5276,11 @@ class HArraySet : public HTemplateInstruction<3> { Primitive::Type value_type = GetValue()->GetType(); return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble)) ? value_type - : expected_component_type_; + : GetRawExpectedComponentType(); } Primitive::Type GetRawExpectedComponentType() const { - return expected_component_type_; + return GetPackedField(); } static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) { @@ -5164,12 +5290,20 @@ class HArraySet : public HTemplateInstruction<3> { DECLARE_INSTRUCTION(ArraySet); private: - const Primitive::Type expected_component_type_; - bool needs_type_check_; - bool value_can_be_null_; + static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits; + static constexpr size_t kFieldExpectedComponentTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kFlagNeedsTypeCheck = + kFieldExpectedComponentType + kFieldExpectedComponentTypeSize; + static constexpr size_t kFlagValueCanBeNull = kFlagNeedsTypeCheck + 1; // Cached information for the reference_type_info_ so that codegen // does not need to inspect the static type. - bool static_type_of_array_is_object_array_; + static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1; + static constexpr size_t kNumberOfArraySetPackedBits = + kFlagStaticTypeOfArrayIsObjectArray + 1; + static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using ExpectedComponentTypeField = + BitField; DISALLOW_COPY_AND_ASSIGN(HArraySet); }; @@ -5279,14 +5413,15 @@ class HLoadClass : public HExpression<1> { : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), type_index_(type_index), dex_file_(dex_file), - is_referrers_class_(is_referrers_class), - generate_clinit_check_(false), - needs_access_check_(needs_access_check), - is_in_dex_cache_(is_in_dex_cache), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { // Referrers class should not need access check. We never inline unverified // methods so we can't possibly end up in this situation. - DCHECK(!is_referrers_class_ || !needs_access_check_); + DCHECK(!is_referrers_class || !needs_access_check); + + SetPackedFlag(is_referrers_class); + SetPackedFlag(needs_access_check); + SetPackedFlag(is_in_dex_cache); + SetPackedFlag(false); SetRawInputAt(0, current_method); } @@ -5297,39 +5432,31 @@ class HLoadClass : public HExpression<1> { // Whether or not we need to generate the clinit check is processed in // prepare_for_register_allocator based on existing HInvokes and HClinitChecks. return other->AsLoadClass()->type_index_ == type_index_ && - other->AsLoadClass()->needs_access_check_ == needs_access_check_; + other->AsLoadClass()->GetPackedFields() == GetPackedFields(); } size_t ComputeHashCode() const OVERRIDE { return type_index_; } uint16_t GetTypeIndex() const { return type_index_; } - bool IsReferrersClass() const { return is_referrers_class_; } bool CanBeNull() const OVERRIDE { return false; } bool NeedsEnvironment() const OVERRIDE { return CanCallRuntime(); } - bool MustGenerateClinitCheck() const { - return generate_clinit_check_; - } - void SetMustGenerateClinitCheck(bool generate_clinit_check) { // The entrypoint the code generator is going to call does not do // clinit of the class. DCHECK(!NeedsAccessCheck()); - generate_clinit_check_ = generate_clinit_check; + SetPackedFlag(generate_clinit_check); } bool CanCallRuntime() const { return MustGenerateClinitCheck() || - (!is_referrers_class_ && !is_in_dex_cache_) || - needs_access_check_; + (!IsReferrersClass() && !IsInDexCache()) || + NeedsAccessCheck(); } - bool NeedsAccessCheck() const { - return needs_access_check_; - } bool CanThrow() const OVERRIDE { return CanCallRuntime(); @@ -5347,25 +5474,31 @@ class HLoadClass : public HExpression<1> { const DexFile& GetDexFile() { return dex_file_; } - bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !is_referrers_class_; } + bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } - bool IsInDexCache() const { return is_in_dex_cache_; } + bool IsReferrersClass() const { return GetPackedFlag(); } + bool NeedsAccessCheck() const { return GetPackedFlag(); } + bool IsInDexCache() const { return GetPackedFlag(); } + bool MustGenerateClinitCheck() const { return GetPackedFlag(); } DECLARE_INSTRUCTION(LoadClass); private: - const uint16_t type_index_; - const DexFile& dex_file_; - const bool is_referrers_class_; + static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1; + static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1; // Whether this instruction must generate the initialization check. // Used for code generation. - bool generate_clinit_check_; - const bool needs_access_check_; - const bool is_in_dex_cache_; + static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1; + static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1; + static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); + + const uint16_t type_index_; + const DexFile& dex_file_; ReferenceTypeInfo loaded_class_rti_; @@ -5379,8 +5512,8 @@ class HLoadString : public HExpression<1> { uint32_t dex_pc, bool is_in_dex_cache) : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), - string_index_(string_index), - is_in_dex_cache_(is_in_dex_cache) { + string_index_(string_index) { + SetPackedFlag(is_in_dex_cache); SetRawInputAt(0, current_method); } @@ -5399,18 +5532,22 @@ class HLoadString : public HExpression<1> { bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; } bool CanBeNull() const OVERRIDE { return false; } - bool IsInDexCache() const { return is_in_dex_cache_; } bool CanThrow() const OVERRIDE { return !IsInDexCache(); } static SideEffects SideEffectsForArchRuntimeCalls() { return SideEffects::CanTriggerGC(); } + bool IsInDexCache() const { return GetPackedFlag(); } + DECLARE_INSTRUCTION(LoadString); private: + static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1; + static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + const uint32_t string_index_; - const bool is_in_dex_cache_; DISALLOW_COPY_AND_ASSIGN(HLoadString); }; @@ -5517,8 +5654,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> { field_idx, declaring_class_def_index, dex_file, - dex_cache), - value_can_be_null_(true) { + dex_cache) { + SetPackedFlag(true); SetRawInputAt(0, cls); SetRawInputAt(1, value); } @@ -5529,14 +5666,18 @@ class HStaticFieldSet : public HTemplateInstruction<2> { bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } - bool GetValueCanBeNull() const { return value_can_be_null_; } - void ClearValueCanBeNull() { value_can_be_null_ = false; } + bool GetValueCanBeNull() const { return GetPackedFlag(); } + void ClearValueCanBeNull() { SetPackedFlag(false); } DECLARE_INSTRUCTION(StaticFieldSet); private: + static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; + static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1; + static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + const FieldInfo field_info_; - bool value_can_be_null_; DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; @@ -5574,8 +5715,8 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, obj); SetRawInputAt(1, value); @@ -5584,13 +5725,21 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); @@ -5626,8 +5775,8 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { uint32_t field_index, uint32_t dex_pc) : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc), - field_type_(field_type), field_index_(field_index) { + SetPackedField(field_type); DCHECK_EQ(field_type, value->GetType()); SetRawInputAt(0, value); } @@ -5635,13 +5784,21 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> { bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } - Primitive::Type GetFieldType() const { return field_type_; } + Primitive::Type GetFieldType() const { return GetPackedField(); } uint32_t GetFieldIndex() const { return field_index_; } DECLARE_INSTRUCTION(UnresolvedStaticFieldSet); private: - const Primitive::Type field_type_; + static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldFieldTypeSize = + MinimumBitsToStore(static_cast(Primitive::kPrimLast)); + static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits = + kFieldFieldType + kFieldFieldTypeSize; + static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using FieldTypeField = BitField; + const uint32_t field_index_; DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet); @@ -5705,7 +5862,8 @@ enum class TypeCheckKind { kAbstractClassCheck, // Can just walk the super class chain, starting one up. kInterfaceCheck, // No optimization yet when checking against an interface. kArrayObjectCheck, // Can just check if the array is not primitive. - kArrayCheck // No optimization yet when checking against a generic array. + kArrayCheck, // No optimization yet when checking against a generic array. + kLast = kArrayCheck }; std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); @@ -5718,9 +5876,9 @@ class HInstanceOf : public HExpression<2> { uint32_t dex_pc) : HExpression(Primitive::kPrimBoolean, SideEffectsForArchRuntimeCalls(check_kind), - dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + dex_pc) { + SetPackedField(check_kind); + SetPackedFlag(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5732,16 +5890,14 @@ class HInstanceOf : public HExpression<2> { } bool NeedsEnvironment() const OVERRIDE { - return CanCallRuntime(check_kind_); + return CanCallRuntime(GetTypeCheckKind()); } - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } - - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - // Used only in code generation. - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } + bool MustDoNullCheck() const { return GetPackedFlag(); } + void ClearMustDoNullCheck() { SetPackedFlag(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } static bool CanCallRuntime(TypeCheckKind check_kind) { // Mips currently does runtime calls for any other checks. @@ -5755,8 +5911,13 @@ class HInstanceOf : public HExpression<2> { DECLARE_INSTRUCTION(InstanceOf); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; @@ -5765,28 +5926,35 @@ class HBoundType : public HExpression<1> { public: HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc) : HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc), - upper_bound_(ReferenceTypeInfo::CreateInvalid()), - upper_can_be_null_(true), - can_be_null_(true) { + upper_bound_(ReferenceTypeInfo::CreateInvalid()) { + SetPackedFlag(true); + SetPackedFlag(true); DCHECK_EQ(input->GetType(), Primitive::kPrimNot); SetRawInputAt(0, input); } // {Get,Set}Upper* should only be used in reference type propagation. const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; } - bool GetUpperCanBeNull() const { return upper_can_be_null_; } + bool GetUpperCanBeNull() const { return GetPackedFlag(); } void SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null); void SetCanBeNull(bool can_be_null) { - DCHECK(upper_can_be_null_ || !can_be_null); - can_be_null_ = can_be_null; + DCHECK(GetUpperCanBeNull() || !can_be_null); + SetPackedFlag(can_be_null); } - bool CanBeNull() const OVERRIDE { return can_be_null_; } + bool CanBeNull() const OVERRIDE { return GetPackedFlag(); } DECLARE_INSTRUCTION(BoundType); private: + // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this + // is false then CanBeNull() cannot be true). + static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits; + static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1; + static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1; + static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + // Encodes the most upper class that this instruction can have. In other words // it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()). // It is used to bound the type in cases like: @@ -5794,10 +5962,6 @@ class HBoundType : public HExpression<1> { // // uper_bound_ will be ClassX // } ReferenceTypeInfo upper_bound_; - // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this - // is false then can_be_null_ cannot be true). - bool upper_can_be_null_; - bool can_be_null_; DISALLOW_COPY_AND_ASSIGN(HBoundType); }; @@ -5808,9 +5972,9 @@ class HCheckCast : public HTemplateInstruction<2> { HLoadClass* constant, TypeCheckKind check_kind, uint32_t dex_pc) - : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), - check_kind_(check_kind), - must_do_null_check_(true) { + : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { + SetPackedField(check_kind); + SetPackedFlag(true); SetRawInputAt(0, object); SetRawInputAt(1, constant); } @@ -5828,17 +5992,21 @@ class HCheckCast : public HTemplateInstruction<2> { bool CanThrow() const OVERRIDE { return true; } - bool MustDoNullCheck() const { return must_do_null_check_; } - void ClearMustDoNullCheck() { must_do_null_check_ = false; } - TypeCheckKind GetTypeCheckKind() const { return check_kind_; } - - bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; } + bool MustDoNullCheck() const { return GetPackedFlag(); } + void ClearMustDoNullCheck() { SetPackedFlag(false); } + TypeCheckKind GetTypeCheckKind() const { return GetPackedField(); } + bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; } DECLARE_INSTRUCTION(CheckCast); private: - const TypeCheckKind check_kind_; - bool must_do_null_check_; + static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; + static constexpr size_t kFieldTypeCheckKindSize = + MinimumBitsToStore(static_cast(TypeCheckKind::kLast)); + static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize; + static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; + static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); + using TypeCheckKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; @@ -5847,30 +6015,40 @@ class HMemoryBarrier : public HTemplateInstruction<0> { public: explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc) : HTemplateInstruction( - SideEffects::AllWritesAndReads(), dex_pc), // Assume write/read on all fields/arrays. - barrier_kind_(barrier_kind) {} + SideEffects::AllWritesAndReads(), dex_pc) { // Assume write/read on all fields/arrays. + SetPackedField(barrier_kind); + } - MemBarrierKind GetBarrierKind() { return barrier_kind_; } + MemBarrierKind GetBarrierKind() { return GetPackedField(); } DECLARE_INSTRUCTION(MemoryBarrier); private: - const MemBarrierKind barrier_kind_; + static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldBarrierKindSize = + MinimumBitsToStore(static_cast(kLastBarrierKind)); + static constexpr size_t kNumberOfMemoryBarrierPackedBits = + kFieldBarrierKind + kFieldBarrierKindSize; + static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits, + "Too many packed fields."); + using BarrierKindField = BitField; DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; class HMonitorOperation : public HTemplateInstruction<1> { public: - enum OperationKind { + enum class OperationKind { kEnter, kExit, + kLast = kExit }; HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc) : HTemplateInstruction( - SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays. - kind_(kind) { + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. + dex_pc) { + SetPackedField(kind); SetRawInputAt(0, object); } @@ -5884,13 +6062,20 @@ class HMonitorOperation : public HTemplateInstruction<1> { return IsEnter(); } - - bool IsEnter() const { return kind_ == kEnter; } + OperationKind GetOperationKind() const { return GetPackedField(); } + bool IsEnter() const { return GetOperationKind() == OperationKind::kEnter; } DECLARE_INSTRUCTION(MonitorOperation); private: - const OperationKind kind_; + static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits; + static constexpr size_t kFieldOperationKindSize = + MinimumBitsToStore(static_cast(OperationKind::kLast)); + static constexpr size_t kNumberOfMonitorOperationPackedBits = + kFieldOperationKind + kFieldOperationKindSize; + static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + using OperationKindField = BitField; private: DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); @@ -6067,6 +6252,9 @@ class HParallelMove : public HTemplateInstruction<0> { } // namespace art +#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64) +#include "nodes_shared.h" +#endif #ifdef ART_ENABLE_CODEGEN_arm #include "nodes_arm.h" #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 445cdab1915a58bed37df256d776d4b60ae37649..173852a55d6c43b13ad32e85560645a1d281c87f 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -118,40 +118,6 @@ class HArm64IntermediateAddress : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); }; -class HArm64MultiplyAccumulate : public HExpression<3> { - public: - HArm64MultiplyAccumulate(Primitive::Type type, - InstructionKind op, - HInstruction* accumulator, - HInstruction* mul_left, - HInstruction* mul_right, - uint32_t dex_pc = kNoDexPc) - : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { - SetRawInputAt(kInputAccumulatorIndex, accumulator); - SetRawInputAt(kInputMulLeftIndex, mul_left); - SetRawInputAt(kInputMulRightIndex, mul_right); - } - - static constexpr int kInputAccumulatorIndex = 0; - static constexpr int kInputMulLeftIndex = 1; - static constexpr int kInputMulRightIndex = 2; - - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return op_kind_ == other->AsArm64MultiplyAccumulate()->op_kind_; - } - - InstructionKind GetOpKind() const { return op_kind_; } - - DECLARE_INSTRUCTION(Arm64MultiplyAccumulate); - - private: - // Indicates if this is a MADD or MSUB. - InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HArm64MultiplyAccumulate); -}; - } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h new file mode 100644 index 0000000000000000000000000000000000000000..c10c718ff4c3f6f3b6fd4c7826560593aa870f61 --- /dev/null +++ b/compiler/optimizing/nodes_shared.h @@ -0,0 +1,118 @@ +/* + * 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_OPTIMIZING_NODES_SHARED_H_ +#define ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ + +namespace art { + +class HMultiplyAccumulate : public HExpression<3> { + public: + HMultiplyAccumulate(Primitive::Type type, + InstructionKind op, + HInstruction* accumulator, + HInstruction* mul_left, + HInstruction* mul_right, + uint32_t dex_pc = kNoDexPc) + : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) { + SetRawInputAt(kInputAccumulatorIndex, accumulator); + SetRawInputAt(kInputMulLeftIndex, mul_left); + SetRawInputAt(kInputMulRightIndex, mul_right); + } + + static constexpr int kInputAccumulatorIndex = 0; + static constexpr int kInputMulLeftIndex = 1; + static constexpr int kInputMulRightIndex = 2; + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(HInstruction* other) const OVERRIDE { + return op_kind_ == other->AsMultiplyAccumulate()->op_kind_; + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(MultiplyAccumulate); + + private: + // Indicates if this is a MADD or MSUB. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); +}; + +class HBitwiseNegatedRight : public HBinaryOperation { + public: + HBitwiseNegatedRight(Primitive::Type result_type, + InstructionKind op, + HInstruction* left, + HInstruction* right, + uint32_t dex_pc = kNoDexPc) + : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc), + op_kind_(op) { + DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op; + } + + template + auto Compute(T x, U y) const -> decltype(x & ~y) { + static_assert(std::is_same::value && + std::is_same::value, + "Inconsistent negated bitwise types"); + switch (op_kind_) { + case HInstruction::kAnd: + return x & ~y; + case HInstruction::kOr: + return x | ~y; + case HInstruction::kXor: + return x ^ ~y; + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + } + + HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(x->GetValue(), y->GetValue()), GetDexPc()); + } + HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, + HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for float values"; + UNREACHABLE(); + } + HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, + HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for double values"; + UNREACHABLE(); + } + + InstructionKind GetOpKind() const { return op_kind_; } + + DECLARE_INSTRUCTION(BitwiseNegatedRight); + + private: + // Specifies the bitwise operation, which will be then negated. + const InstructionKind op_kind_; + + DISALLOW_COPY_AND_ASSIGN(HBitwiseNegatedRight); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index b1891c979e21d0c029e4c8b63752a54bf87cc521..7a82063bba380f53ebfe9b2b9629677106a2fc22 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -20,7 +20,7 @@ #include #include -#ifdef ART_ENABLE_CODEGEN_arm64 +#ifdef ART_ENABLE_CODEGEN_arm #include "dex_cache_array_fixups_arm.h" #endif @@ -60,6 +60,7 @@ #include "induction_var_analysis.h" #include "inliner.h" #include "instruction_simplifier.h" +#include "instruction_simplifier_arm.h" #include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit_code_cache.h" @@ -430,6 +431,7 @@ static void MaybeRunInliner(HGraph* graph, static void RunArchOptimizations(InstructionSet instruction_set, HGraph* graph, + CodeGenerator* codegen, OptimizingCompilerStats* stats, PassObserver* pass_observer) { ArenaAllocator* arena = graph->GetArena(); @@ -438,7 +440,10 @@ static void RunArchOptimizations(InstructionSet instruction_set, case kThumb2: case kArm: { arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats); + arm::InstructionSimplifierArm* simplifier = + new (arena) arm::InstructionSimplifierArm(graph, stats); HOptimization* arm_optimizations[] = { + simplifier, fixups }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); @@ -462,7 +467,8 @@ static void RunArchOptimizations(InstructionSet instruction_set, #endif #ifdef ART_ENABLE_CODEGEN_x86 case kX86: { - x86::PcRelativeFixups* pc_relative_fixups = new (arena) x86::PcRelativeFixups(graph, stats); + x86::PcRelativeFixups* pc_relative_fixups = + new (arena) x86::PcRelativeFixups(graph, codegen, stats); HOptimization* x86_optimizations[] = { pc_relative_fixups }; @@ -479,7 +485,11 @@ NO_INLINE // Avoid increasing caller's frame size by large stack-allocated obje static void AllocateRegisters(HGraph* graph, CodeGenerator* codegen, PassObserver* pass_observer) { - PrepareForRegisterAllocation(graph).Run(); + { + PassScope scope(PrepareForRegisterAllocation::kPrepareForRegisterAllocationPassName, + pass_observer); + PrepareForRegisterAllocation(graph).Run(); + } SsaLivenessAnalysis liveness(graph, codegen); { PassScope scope(SsaLivenessAnalysis::kLivenessPassName, pass_observer); @@ -553,7 +563,7 @@ static void RunOptimizations(HGraph* graph, }; RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); - RunArchOptimizations(driver->GetInstructionSet(), graph, stats, pass_observer); + RunArchOptimizations(driver->GetInstructionSet(), graph, codegen, stats, pass_observer); AllocateRegisters(graph, codegen, pass_observer); } @@ -857,7 +867,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, const uint32_t access_flags = method->GetAccessFlags(); const InvokeType invoke_type = method->GetInvokeType(); - ArenaAllocator arena(Runtime::Current()->GetArenaPool()); + ArenaAllocator arena(Runtime::Current()->GetJitArenaPool()); CodeVectorAllocator code_allocator(&arena); std::unique_ptr codegen; { @@ -905,34 +915,31 @@ bool OptimizingCompiler::JitCompile(Thread* self, return false; } - if (GetCompilerDriver()->GetCompilerOptions().GetGenerateDebugInfo()) { + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GetGenerateDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); - CompiledMethod compiled_method( - GetCompilerDriver(), - codegen->GetInstructionSet(), - ArrayRef(code_allocator.GetMemory()), - codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(), - codegen->GetCoreSpillMask(), - codegen->GetFpuSpillMask(), - ArrayRef(), - ArrayRef(), // mapping_table. - ArrayRef(stack_map_data, stack_map_size), - ArrayRef(), // native_gc_map. - ArrayRef(*codegen->GetAssembler()->cfi().data()), - ArrayRef()); - debug::MethodDebugInfo method_debug_info { - dex_file, - class_def_idx, - method_idx, - access_flags, - code_item, - false, // deduped. - code_address, - code_address + code_allocator.GetSize(), - &compiled_method - }; - ArrayRef elf_file = debug::WriteDebugElfFileForMethod(method_debug_info); + debug::MethodDebugInfo info = debug::MethodDebugInfo(); + info.trampoline_name = nullptr; + info.dex_file = dex_file; + info.class_def_index = class_def_idx; + info.dex_method_index = method_idx; + info.access_flags = access_flags; + info.code_item = code_item; + info.isa = codegen->GetInstructionSet(); + info.deduped = false; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = true; + info.is_code_address_text_relative = false; + info.code_address = code_address; + info.code_size = code_allocator.GetSize(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; + info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); + ArrayRef elf_file = debug::WriteDebugElfFileForMethods( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + ArrayRef(&info, 1)); CreateJITCodeEntryForAddress(code_address, std::unique_ptr(elf_file.data()), elf_file.size()); diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index a6f14616bf98529e6ed2b21fa0a00c6e0d5acb66..d281a9fc6c68f8ded01fed3b83c98e0d4aeb70a8 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -16,6 +16,7 @@ #include "pc_relative_fixups_x86.h" #include "code_generator_x86.h" +#include "intrinsics_x86.h" namespace art { namespace x86 { @@ -25,7 +26,10 @@ namespace x86 { */ class PCRelativeHandlerVisitor : public HGraphVisitor { public: - explicit PCRelativeHandlerVisitor(HGraph* graph) : HGraphVisitor(graph), base_(nullptr) {} + PCRelativeHandlerVisitor(HGraph* graph, CodeGenerator* codegen) + : HGraphVisitor(graph), + codegen_(down_cast(codegen)), + base_(nullptr) {} void MoveBaseIfNeeded() { if (base_ != nullptr) { @@ -146,7 +150,6 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { if (base_ != nullptr) { return; } - // Insert the base at the start of the entry block, move it to a better // position later in MoveBaseIfNeeded(). base_ = new (GetGraph()->GetArena()) HX86ComputeBaseMethodAddress(); @@ -180,7 +183,9 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } bool base_added = false; - if (invoke_static_or_direct != nullptr && invoke_static_or_direct->HasPcRelativeDexCache()) { + if (invoke_static_or_direct != nullptr && + invoke_static_or_direct->HasPcRelativeDexCache() && + !WillHaveCallFreeIntrinsicsCodeGen(invoke)) { InitializePCRelativeBasePointer(); // Add the extra parameter base_. invoke_static_or_direct->AddSpecialInput(base_); @@ -215,6 +220,24 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { } } + bool WillHaveCallFreeIntrinsicsCodeGen(HInvoke* invoke) { + if (invoke->GetIntrinsic() != Intrinsics::kNone) { + // This invoke may have intrinsic code generation defined. However, we must + // now also determine if this code generation is truly there and call-free + // (not unimplemented, no bail on instruction features, or call on slow path). + // This is done by actually calling the locations builder on the instruction + // and clearing out the locations once result is known. We assume this + // call only has creating locations as side effects! + IntrinsicLocationsBuilderX86 builder(codegen_); + bool success = builder.TryDispatch(invoke) && !invoke->GetLocations()->CanCall(); + invoke->SetLocations(nullptr); + return success; + } + return false; + } + + CodeGeneratorX86* codegen_; + // The generated HX86ComputeBaseMethodAddress in the entry block needed as an // input to the HX86LoadFromConstantTable instructions. HX86ComputeBaseMethodAddress* base_; @@ -226,7 +249,7 @@ void PcRelativeFixups::Run() { // that can be live-in at the irreducible loop header. return; } - PCRelativeHandlerVisitor visitor(graph_); + PCRelativeHandlerVisitor visitor(graph_, codegen_); visitor.VisitInsertionOrder(); visitor.MoveBaseIfNeeded(); } diff --git a/compiler/optimizing/pc_relative_fixups_x86.h b/compiler/optimizing/pc_relative_fixups_x86.h index af708acb031bf921b7f0842ba83fa77acacc22b2..03de2fcece4a50b88bb29b1cb5feff16f7d18f25 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.h +++ b/compiler/optimizing/pc_relative_fixups_x86.h @@ -21,14 +21,21 @@ #include "optimization.h" namespace art { + +class CodeGenerator; + namespace x86 { class PcRelativeFixups : public HOptimization { public: - PcRelativeFixups(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, "pc_relative_fixups_x86", stats) {} + PcRelativeFixups(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) + : HOptimization(graph, "pc_relative_fixups_x86", stats), + codegen_(codegen) {} void Run() OVERRIDE; + + private: + CodeGenerator* codegen_; }; } // namespace x86 diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index c8b8b0dcfad0efd3d1593c34c93c9c04b03664a9..c90724c251a739c9dd219a340b9d3bd6de4bdd7d 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -32,6 +32,9 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { void Run(); + static constexpr const char* kPrepareForRegisterAllocationPassName = + "prepare_for_register_allocation"; + private: void VisitNullCheck(HNullCheck* check) OVERRIDE; void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index deaa415ed49e8f8ac773923fe6ed83addbc64c2d..75356c848b65758ee1b4102833fd9a6db4d7a8d7 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -432,11 +432,10 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst } else if (klass != nullptr) { ScopedObjectAccess soa(Thread::Current()); ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass); - is_exact = is_exact || klass->CannotBeAssignedFromOtherTypes(); + is_exact = is_exact || handle->CannotBeAssignedFromOtherTypes(); instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact)); } else { - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -518,8 +517,7 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedInstanceFieldGet( HUnresolvedInstanceFieldGet* instr) { // TODO: Use descriptor to get the actual type. if (instr->GetFieldType() == Primitive::kPrimNot) { - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -527,8 +525,7 @@ void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( HUnresolvedStaticFieldGet* instr) { // TODO: Use descriptor to get the actual type. if (instr->GetFieldType() == Primitive::kPrimNot) { - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -724,12 +721,11 @@ void ReferenceTypePropagation::UpdateArrayGet(HArrayGet* instr, HandleCache* han if (handle->IsObjectArrayClass()) { ReferenceTypeInfo::TypeHandle component_handle = handle_cache->NewHandle(handle->GetComponentType()); - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(component_handle, /* is_exact */ false)); + bool is_exact = component_handle->CannotBeAssignedFromOtherTypes(); + instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(component_handle, is_exact)); } else { // We don't know what the parent actually is, so we fallback to object. - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache->GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); } } @@ -811,8 +807,7 @@ void ReferenceTypePropagation::UpdatePhi(HPhi* instr) { if (first_input_index_not_null == input_count) { // All inputs are NullConstants, set the type to object. // This may happen in the presence of inlining. - instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(handle_cache_.GetObjectClassHandle(), /* is_exact */ false)); + instr->SetReferenceTypeInfo(instr->GetBlock()->GetGraph()->GetInexactObjectRti()); return; } diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 4784de1380d983b732c546925d24a53b0c5ea12c..54cbdf8b668b71773de43e9ac7b33d1239f2cc10 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -63,8 +63,7 @@ void StackMapStream::EndStackMapEntry() { void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) { if (kind != DexRegisterLocation::Kind::kNone) { // Ensure we only use non-compressed location kind at this stage. - DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) - << DexRegisterLocation::PrettyDescriptor(kind); + DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind; DexRegisterLocation location(kind, value); // Look for Dex register `location` in the location catalog (using the @@ -257,6 +256,7 @@ void StackMapStream::FillIn(MemoryRegion region) { // Ensure we reached the end of the Dex registers location_catalog. DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); + ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { @@ -268,6 +268,9 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask); if (entry.sp_mask != nullptr) { stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask); + } else { + // The MemoryRegion does not have to be zeroed, so make sure we clear the bits. + stack_map.SetStackMask(stack_map_encoding_, empty_bitmask); } if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) { @@ -344,6 +347,11 @@ void StackMapStream::FillIn(MemoryRegion region) { } } } + + // Verify all written data in debug build. + if (kIsDebugBuild) { + CheckCodeInfo(region); + } } void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, @@ -423,4 +431,90 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn return true; } +// Helper for CheckCodeInfo - check that register map has the expected content. +void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const { + StackMapEncoding encoding = code_info.ExtractEncoding(); + for (size_t reg = 0; reg < num_dex_registers; reg++) { + // Find the location we tried to encode. + DexRegisterLocation expected = DexRegisterLocation::None(); + if (live_dex_registers_mask->IsBitSet(reg)) { + size_t catalog_index = dex_register_locations_[dex_register_locations_index++]; + expected = location_catalog_entries_[catalog_index]; + } + // Compare to the seen location. + if (expected.GetKind() == DexRegisterLocation::Kind::kNone) { + DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg)); + } else { + DCHECK(dex_register_map.IsDexRegisterLive(reg)); + DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation( + reg, num_dex_registers, code_info, encoding); + DCHECK_EQ(expected.GetKind(), seen.GetKind()); + DCHECK_EQ(expected.GetValue(), seen.GetValue()); + } + } + if (num_dex_registers == 0) { + DCHECK(!dex_register_map.IsValid()); + } +} + +// Check that all StackMapStream inputs are correctly encoded by trying to read them back. +void StackMapStream::CheckCodeInfo(MemoryRegion region) const { + CodeInfo code_info(region); + StackMapEncoding encoding = code_info.ExtractEncoding(); + DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size()); + for (size_t s = 0; s < stack_maps_.size(); ++s) { + const StackMap stack_map = code_info.GetStackMapAt(s, encoding); + StackMapEntry entry = stack_maps_[s]; + + // Check main stack map fields. + DCHECK_EQ(stack_map.GetNativePcOffset(encoding), entry.native_pc_offset); + DCHECK_EQ(stack_map.GetDexPc(encoding), entry.dex_pc); + DCHECK_EQ(stack_map.GetRegisterMask(encoding), entry.register_mask); + MemoryRegion stack_mask = stack_map.GetStackMask(encoding); + if (entry.sp_mask != nullptr) { + DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits()); + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b)); + } + } else { + for (size_t b = 0; b < stack_mask.size_in_bits(); b++) { + DCHECK_EQ(stack_mask.LoadBit(b), 0u); + } + } + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapOf( + stack_map, encoding, entry.num_dex_registers), + entry.num_dex_registers, + entry.live_dex_registers_mask, + entry.dex_register_locations_start_index); + + // Check inline info. + DCHECK_EQ(stack_map.HasInlineInfo(encoding), (entry.inlining_depth != 0)); + if (entry.inlining_depth != 0) { + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth); + for (size_t d = 0; d < entry.inlining_depth; ++d) { + size_t inline_info_index = entry.inline_infos_start_index + d; + DCHECK_LT(inline_info_index, inline_infos_.size()); + InlineInfoEntry inline_entry = inline_infos_[inline_info_index]; + DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc); + DCHECK_EQ(inline_info.GetMethodIndexAtDepth(d), inline_entry.method_index); + DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(d), inline_entry.invoke_type); + + CheckDexRegisterMap(code_info, + code_info.GetDexRegisterMapAtDepth( + d, inline_info, encoding, inline_entry.num_dex_registers), + inline_entry.num_dex_registers, + inline_entry.live_dex_registers_mask, + inline_entry.dex_register_locations_start_index); + } + } + } +} + } // namespace art diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index fc27a2b446501ac9c6ed8a924a8238b40a0b5c8b..016a911424fa69a54f3c7541b0ab4fac7a2c3b39 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -167,6 +167,13 @@ class StackMapStream : public ValueObject { const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; + void CheckDexRegisterMap(const CodeInfo& code_info, + const DexRegisterMap& dex_register_map, + size_t num_dex_registers, + BitVector* live_dex_registers_mask, + size_t dex_register_locations_index) const; + void CheckCodeInfo(MemoryRegion region) const; + ArenaAllocator* allocator_; ArenaVector stack_maps_; diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index f96376d9fea0e852145f2ec75feb3accf3c1a8ae..a8945654255bc2a7d3bd45d010f8ef45302d2cb3 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 ebca25bbf9ba04e5e749f9d301ead45839023cb6..0a227b21cd3107351d7afb55f8587b1446f3ebf2 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(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(rd) << kRdShift) | + (0b1111 << 8) | + static_cast(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 bf332feb62af2ae2db26b46a9182522ac57e2bd5..e3e05caf9279d21322b6eb8042fd9b4674f6809e 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 43805966a92f624ecbec849678a95b569790595a..e570e22fca6fb10eaedbd9cbca4b99601ce9f514 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 52023a67eecec715b2414153452f2745c87237f4..15298b390b18338341c2a783c0f1ae9224fe808a 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(rm) << 16 | - 0xf << 12 | static_cast(rd) << 8 | - B7 | static_cast(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(rm) << 16 | - 0xf << 12 | static_cast(rd) << 8 | - B7 | B5 | static_cast(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(rm) << 3 | + static_cast(rd); + Emit16Miscellaneous(encoding); + } else { + int32_t encoding = + static_cast(rm) << 16 | + static_cast(rd) << 8 | + static_cast(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 bf07b2dbf8a49f1b1144f4615200b42220d3a401..6b61acafacf97452cb277b07f56e26c5ccac7827 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 7b32b0fd26e9ad23d106f1b37e46d18581c6b025..650b08900bedb98fa3943c9c649007910a342539 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 6fd65ee9a43efbec48739c59147c1fc7b19c42ad..7c41813457cd233582bf29e6eb0b015619a7961f 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(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(0x8), static_cast(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(0), fs, fd, 0x4); +} + +void MipsAssembler::SqrtD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x4); +} + +void MipsAssembler::AbsS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x5); +} + +void MipsAssembler::AbsD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x5); +} + void MipsAssembler::MovS(FRegister fd, FRegister fs) { EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x6); } @@ -859,84 +883,140 @@ void MipsAssembler::NegD(FRegister fd, FRegister fs) { EmitFR(0x11, 0x11, static_cast(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(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(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(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(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(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(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(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(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(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(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(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(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(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((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(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(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((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((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(0), fs, fd, 0x1b); +} + +void MipsAssembler::ClassD(FRegister fd, FRegister fs) { + CHECK(IsR6()); + EmitFR(0x11, 0x11, static_cast(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(0), fs, fd, 0x09); } @@ -1095,6 +1239,14 @@ void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) { EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x21); } +void MipsAssembler::FloorWS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0xf); +} + +void MipsAssembler::FloorWD(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0xf); +} + void MipsAssembler::Mfc1(Register rt, FRegister fs) { EmitFR(0x11, 0x00, static_cast(rt), fs, static_cast(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(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(cc), ZERO); diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 2262af49b33273d8c8a4f151599e1bcc3af224a9..a7179fd1dccb1924391f2e62fcc53414dd450439 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/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc index 244a5fedbea4358db52f71062ca817eca9a309ae..1a8f567aa10fe910098eea996bc5e83447ea1507 100644 --- a/compiler/utils/swap_space.cc +++ b/compiler/utils/swap_space.cc @@ -152,7 +152,6 @@ SwapSpace::SpaceChunk SwapSpace::NewFileChunk(size_t min_size) { } size_ += next_part; SpaceChunk new_chunk = {ptr, next_part}; - maps_.push_back(new_chunk); return new_chunk; #else UNUSED(min_size, kMininumMapSize); diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h index b659f1d3c7516d835b1cac89db85220a5c2cccde..bf06675d72c028c34ed727b71fa0c9f1782e68e3 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -85,7 +85,6 @@ class SwapSpace { int fd_; size_t size_; - std::list maps_; // NOTE: Boost.Bimap would be useful for the two following members. diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 623ac3280e487f794e960da0b26c5b998af25279..ce4f38a9e381b1e2a487e178102974581cde304a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -46,6 +46,7 @@ #include "class_linker.h" #include "compiler.h" #include "compiler_callbacks.h" +#include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex/pass_manager.h" #include "dex/quick/dex_file_to_method_inliner_map.h" @@ -63,6 +64,7 @@ #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" @@ -696,6 +698,7 @@ class Dex2Oat FINAL { if (IsBootImage()) { // We need the boot image to always be debuggable. + // TODO: Remove this once we better deal with full frame deoptimization. compiler_options_->debuggable_ = true; } @@ -1032,6 +1035,9 @@ class Dex2Oat FINAL { key_value_store_->Put( OatHeader::kDebuggableKey, compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); + key_value_store_->Put( + OatHeader::kNativeDebuggableKey, + compiler_options_->native_debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); if (compiler_options_->IsExtractOnly()) { key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue); } else if (UseProfileGuidedCompilation()) { @@ -1384,7 +1390,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& 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 { @@ -1533,13 +1539,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_); @@ -1646,7 +1651,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_); @@ -1656,21 +1661,41 @@ 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& oat_file = oat_files_[i]; std::unique_ptr& elf_writer = elf_writers_[i]; std::unique_ptr& oat_writer = oat_writers_[i]; std::vector& 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& oat_file = oat_files_[i]; + std::unique_ptr& elf_writer = elf_writers_[i]; + std::unique_ptr& oat_writer = oat_writers_[i]; + + oat_writer->AddMethodDebugInfos(debug::MakeTrampolineInfos(oat_writer->GetOatHeader())); + + // 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); @@ -1696,7 +1721,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()); @@ -1710,19 +1741,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(); @@ -2084,8 +2106,10 @@ class Dex2Oat FINAL { elf_writers_.reserve(oat_files_.size()); oat_writers_.reserve(oat_files_.size()); for (const std::unique_ptr& oat_file : oat_files_) { - elf_writers_.emplace_back( - CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get())); + elf_writers_.emplace_back(CreateElfWriterQuick(instruction_set_, + instruction_set_features_.get(), + compiler_options_.get(), + oat_file.get())); elf_writers_.back()->Start(); oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_)); } @@ -2209,22 +2233,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 oat_data_begins; - for (const char* oat_filename : oat_filenames_) { - oat_data_begins.emplace(oat_filename, image_writer_->GetOatDataBegin(oat_filename)); + dchecked_vector 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 oat_file(OS::OpenFileReadWrite(oat_filename)); @@ -2233,9 +2256,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; @@ -2449,7 +2470,7 @@ class Dex2Oat FINAL { TimingLogger* timings_; std::unique_ptr compiler_phases_timings_; std::vector> dex_files_per_oat_file_; - std::unordered_map dex_file_oat_filename_map_; + std::unordered_map dex_file_oat_index_map_; // Backing storage. std::vector char_backing_storage_; @@ -2566,22 +2587,26 @@ 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 = MakeUnique(&timings); // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. - dex2oat.ParseArgs(argc, argv); + dex2oat->ParseArgs(argc, argv); // If needed, process profile information for profile guided compilation. // This operation involves I/O. - if (dex2oat.UseProfileGuidedCompilation()) { - if (!dex2oat.LoadProfile()) { + if (dex2oat->UseProfileGuidedCompilation()) { + if (!dex2oat->LoadProfile()) { LOG(ERROR) << "Failed to process profile file"; return EXIT_FAILURE; } } // Check early that the result of compilation can be written - if (!dex2oat.OpenFile()) { + if (!dex2oat->OpenFile()) { return EXIT_FAILURE; } @@ -2591,25 +2616,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/disassembler/Android.mk b/disassembler/Android.mk index 039986ce2bd4e752d2a911e640f9c487683ba0a7..bf563c766062ad306cdbb275dde7123584f9e686 100644 --- a/disassembler/Android.mk +++ b/disassembler/Android.mk @@ -89,7 +89,7 @@ define build-libart-disassembler LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE) # For disassembler_arm64. ifeq ($$(art_ndebug_or_debug),debug) - LOCAL_SHARED_LIBRARIES += libvixld + LOCAL_SHARED_LIBRARIES += libvixl else LOCAL_SHARED_LIBRARIES += libvixl endif diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index af08fc4e936a8188dbce81f1f8d70710a2a47f5c..3ed57664bce7a3ff6b788f593e655559ab97b6a6 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -32,6 +32,8 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "class_linker-inl.h" +#include "debug/elf_debug_writer.h" +#include "debug/method_debug_info.h" #include "dex_file-inl.h" #include "dex_instruction.h" #include "disassembler.h" @@ -98,6 +100,7 @@ const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* return ret; } +template class OatSymbolizer FINAL { public: OatSymbolizer(const OatFile* oat_file, const std::string& output_name) : @@ -105,29 +108,21 @@ class OatSymbolizer FINAL { output_name_(output_name.empty() ? "symbolized.oat" : output_name) { } - typedef void (OatSymbolizer::*Callback)(const DexFile::ClassDef&, - uint32_t, - const OatFile::OatMethod&, - const DexFile&, - uint32_t, - const DexFile::CodeItem*, - uint32_t); - bool Symbolize() { const InstructionSet isa = oat_file_->GetOatHeader().GetInstructionSet(); + const InstructionSetFeatures* features = InstructionSetFeatures::FromBitmap( + isa, oat_file_->GetOatHeader().GetInstructionSetFeaturesBitmap()); File* elf_file = OS::CreateEmptyFile(output_name_.c_str()); std::unique_ptr output_stream( MakeUnique(MakeUnique(elf_file))); - builder_.reset(new ElfBuilder(isa, output_stream.get())); + builder_.reset(new ElfBuilder(isa, features, output_stream.get())); builder_->Start(); auto* rodata = builder_->GetRoData(); auto* text = builder_->GetText(); auto* bss = builder_->GetBss(); - auto* strtab = builder_->GetStrTab(); - auto* symtab = builder_->GetSymTab(); rodata->Start(); const uint8_t* rodata_begin = oat_file_->Begin(); @@ -145,66 +140,38 @@ class OatSymbolizer FINAL { bss->WriteNoBitsSection(oat_file_->BssSize()); } - builder_->WriteDynamicSection(elf_file->GetPath()); - - Walk(&art::OatSymbolizer::RegisterForDedup); - - NormalizeState(); + if (isa == kMips || isa == kMips64) { + builder_->WriteMIPSabiflagsSection(); + } + builder_->PrepareDynamicSection( + elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize()); + builder_->WriteDynamicSection(); - strtab->Start(); - strtab->Write(""); // strtab should start with empty string. - AddTrampolineSymbols(); - Walk(&art::OatSymbolizer::AddSymbol); - strtab->End(); + Walk(); + for (const auto& trampoline : debug::MakeTrampolineInfos(oat_file_->GetOatHeader())) { + method_debug_infos_.push_back(trampoline); + } - symtab->Start(); - symtab->Write(); - symtab->End(); + debug::WriteDebugInfo(builder_.get(), + ArrayRef(method_debug_infos_), + dwarf::DW_DEBUG_FRAME_FORMAT, + true /* write_oat_patches */); builder_->End(); return builder_->Good(); } - void AddTrampolineSymbol(const char* name, uint32_t code_offset) { - if (code_offset != 0) { - uint32_t name_offset = builder_->GetStrTab()->Write(name); - uint64_t symbol_value = code_offset - oat_file_->GetOatHeader().GetExecutableOffset(); - // Specifying 0 as the symbol size means that the symbol lasts until the next symbol or until - // the end of the section in case of the last symbol. - builder_->GetSymTab()->Add(name_offset, builder_->GetText(), symbol_value, - /* is_relative */ true, /* size */ 0, STB_GLOBAL, STT_FUNC); - } - } - - void AddTrampolineSymbols() { - const OatHeader& oat_header = oat_file_->GetOatHeader(); - AddTrampolineSymbol("interpreterToInterpreterBridge", - oat_header.GetInterpreterToInterpreterBridgeOffset()); - AddTrampolineSymbol("interpreterToCompiledCodeBridge", - oat_header.GetInterpreterToCompiledCodeBridgeOffset()); - AddTrampolineSymbol("jniDlsymLookup", - oat_header.GetJniDlsymLookupOffset()); - AddTrampolineSymbol("quickGenericJniTrampoline", - oat_header.GetQuickGenericJniTrampolineOffset()); - AddTrampolineSymbol("quickImtConflictTrampoline", - oat_header.GetQuickImtConflictTrampolineOffset()); - AddTrampolineSymbol("quickResolutionTrampoline", - oat_header.GetQuickResolutionTrampolineOffset()); - AddTrampolineSymbol("quickToInterpreterBridge", - oat_header.GetQuickToInterpreterBridgeOffset()); - } - - void Walk(Callback callback) { + void Walk() { std::vector oat_dex_files = oat_file_->GetOatDexFiles(); for (size_t i = 0; i < oat_dex_files.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i]; CHECK(oat_dex_file != nullptr); - WalkOatDexFile(oat_dex_file, callback); + WalkOatDexFile(oat_dex_file); } } - void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file, Callback callback) { + void WalkOatDexFile(const OatFile::OatDexFile* oat_dex_file) { std::string error_msg; const DexFile* const dex_file = OpenDexFile(oat_dex_file, &error_msg); if (dex_file == nullptr) { @@ -213,13 +180,12 @@ class OatSymbolizer FINAL { for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); const OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); OatClassType type = oat_class.GetType(); switch (type) { case kOatClassAllCompiled: case kOatClassSomeCompiled: - WalkOatClass(oat_class, *dex_file, class_def, callback); + WalkOatClass(oat_class, *dex_file, class_def_index); break; case kOatClassNoneCompiled: @@ -230,8 +196,10 @@ class OatSymbolizer FINAL { } } - void WalkOatClass(const OatFile::OatClass& oat_class, const DexFile& dex_file, - const DexFile::ClassDef& class_def, Callback callback) { + void WalkOatClass(const OatFile::OatClass& oat_class, + const DexFile& dex_file, + uint32_t class_def_index) { + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); const uint8_t* class_data = dex_file.GetClassData(class_def); if (class_data == nullptr) { // empty class such as a marker interface? return; @@ -239,111 +207,62 @@ class OatSymbolizer FINAL { // Note: even if this is an interface or a native class, we still have to walk it, as there // might be a static initializer. ClassDataItemIterator it(dex_file, class_data); - SkipAllFields(&it); uint32_t class_method_idx = 0; - while (it.HasNextDirectMethod()) { - const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), - it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback); - class_method_idx++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { - const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - WalkOatMethod(class_def, class_method_idx, oat_method, dex_file, it.GetMemberIndex(), - it.GetMethodCodeItem(), it.GetMethodAccessFlags(), callback); - class_method_idx++; - it.Next(); + for (; it.HasNextStaticField(); it.Next()) { /* skip */ } + for (; it.HasNextInstanceField(); it.Next()) { /* skip */ } + for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + WalkOatMethod(oat_class.GetOatMethod(class_method_idx++), + dex_file, + class_def_index, + it.GetMemberIndex(), + it.GetMethodCodeItem(), + it.GetMethodAccessFlags()); } DCHECK(!it.HasNext()); } - void WalkOatMethod(const DexFile::ClassDef& class_def, uint32_t class_method_index, - const OatFile::OatMethod& oat_method, const DexFile& dex_file, - uint32_t dex_method_idx, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, Callback callback) { + void WalkOatMethod(const OatFile::OatMethod& oat_method, + const DexFile& dex_file, + uint32_t class_def_index, + uint32_t dex_method_index, + const DexFile::CodeItem* code_item, + uint32_t method_access_flags) { if ((method_access_flags & kAccAbstract) != 0) { // Abstract method, no code. return; } - if (oat_method.GetCodeOffset() == 0) { + const OatHeader& oat_header = oat_file_->GetOatHeader(); + const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader(); + if (method_header == nullptr || method_header->GetCodeSize() == 0) { // No code. return; } - (this->*callback)(class_def, class_method_index, oat_method, dex_file, dex_method_idx, code_item, - method_access_flags); - } - - void RegisterForDedup(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED, - uint32_t class_method_index ATTRIBUTE_UNUSED, - const OatFile::OatMethod& oat_method, - const DexFile& dex_file ATTRIBUTE_UNUSED, - uint32_t dex_method_idx ATTRIBUTE_UNUSED, - const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED, - uint32_t method_access_flags ATTRIBUTE_UNUSED) { - state_[oat_method.GetCodeOffset()]++; - } - - void NormalizeState() { - for (auto& x : state_) { - if (x.second == 1) { - state_[x.first] = 0; - } - } - } - - enum class DedupState { // private - kNotDeduplicated, - kDeduplicatedFirst, - kDeduplicatedOther - }; - DedupState IsDuplicated(uint32_t offset) { - if (state_[offset] == 0) { - return DedupState::kNotDeduplicated; - } - if (state_[offset] == 1) { - return DedupState::kDeduplicatedOther; - } - state_[offset] = 1; - return DedupState::kDeduplicatedFirst; - } - - void AddSymbol(const DexFile::ClassDef& class_def ATTRIBUTE_UNUSED, - uint32_t class_method_index ATTRIBUTE_UNUSED, - const OatFile::OatMethod& oat_method, - const DexFile& dex_file, - uint32_t dex_method_idx, - const DexFile::CodeItem* code_item ATTRIBUTE_UNUSED, - uint32_t method_access_flags ATTRIBUTE_UNUSED) { - DedupState dedup = IsDuplicated(oat_method.GetCodeOffset()); - if (dedup != DedupState::kDeduplicatedOther) { - std::string pretty_name = PrettyMethod(dex_method_idx, dex_file, true); - - if (dedup == DedupState::kDeduplicatedFirst) { - pretty_name = "[Dedup]" + pretty_name; - } - - int name_offset = builder_->GetStrTab()->Write(pretty_name); - builder_->GetSymTab()->Add(name_offset, builder_->GetText(), - oat_method.GetCodeOffset() - oat_file_->GetOatHeader().GetExecutableOffset(), - true, oat_method.GetQuickCodeSize(), STB_GLOBAL, STT_FUNC); - } + debug::MethodDebugInfo info = debug::MethodDebugInfo(); + info.trampoline_name = nullptr; + info.dex_file = &dex_file; + info.class_def_index = class_def_index; + info.dex_method_index = dex_method_index; + info.access_flags = method_access_flags; + info.code_item = code_item; + info.isa = oat_header.GetInstructionSet(); + info.deduped = !seen_offsets_.insert(oat_method.GetCodeOffset()).second; + info.is_native_debuggable = oat_header.IsNativeDebuggable(); + info.is_optimized = method_header->IsOptimized(); + info.is_code_address_text_relative = true; + info.code_address = oat_method.GetCodeOffset() - oat_header.GetExecutableOffset(); + info.code_size = method_header->GetCodeSize(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = info.is_optimized ? method_header->GetOptimizedCodeInfoPtr() : nullptr; + info.cfi = ArrayRef(); + method_debug_infos_.push_back(info); } private: - static void SkipAllFields(ClassDataItemIterator* it) { - while (it->HasNextStaticField()) { - it->Next(); - } - while (it->HasNextInstanceField()) { - it->Next(); - } - } - const OatFile* oat_file_; - std::unique_ptr > builder_; - std::unordered_map state_; + std::unique_ptr > builder_; + std::vector method_debug_infos_; + std::unordered_set seen_offsets_; const std::string output_name_; }; @@ -359,6 +278,7 @@ class OatDumperOptions { const char* method_filter, bool list_classes, bool list_methods, + bool dump_header_only, const char* export_dex_location, uint32_t addr2instr) : dump_raw_mapping_table_(dump_raw_mapping_table), @@ -371,6 +291,7 @@ class OatDumperOptions { method_filter_(method_filter), list_classes_(list_classes), list_methods_(list_methods), + dump_header_only_(dump_header_only), export_dex_location_(export_dex_location), addr2instr_(addr2instr), class_loader_(nullptr) {} @@ -385,6 +306,7 @@ class OatDumperOptions { const char* const method_filter_; const bool list_classes_; const bool list_methods_; + const bool dump_header_only_; const char* const export_dex_location_; uint32_t addr2instr_; Handle* class_loader_; @@ -512,21 +434,24 @@ class OatDumper { os << StringPrintf("0x%08x\n\n", resolved_addr2instr_); } - for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; - CHECK(oat_dex_file != nullptr); + if (!options_.dump_header_only_) { + for (size_t i = 0; i < oat_dex_files_.size(); i++) { + const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + CHECK(oat_dex_file != nullptr); - // If file export selected skip file analysis - if (options_.export_dex_location_) { - if (!ExportDexFile(os, *oat_dex_file)) { - success = false; - } - } else { - if (!DumpOatDexFile(os, *oat_dex_file)) { - success = false; + // If file export selected skip file analysis + if (options_.export_dex_location_) { + if (!ExportDexFile(os, *oat_dex_file)) { + success = false; + } + } else { + if (!DumpOatDexFile(os, *oat_dex_file)) { + success = false; + } } } } + os << std::flush; return success; } @@ -2534,8 +2459,17 @@ static int SymbolizeOat(const char* oat_filename, std::string& output_name) { return EXIT_FAILURE; } - OatSymbolizer oat_symbolizer(oat_file, output_name); - if (!oat_symbolizer.Symbolize()) { + bool result; + // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF + // files for 64-bit code in the past. + if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) { + OatSymbolizer oat_symbolizer(oat_file, output_name); + result = oat_symbolizer.Symbolize(); + } else { + OatSymbolizer oat_symbolizer(oat_file, output_name); + result = oat_symbolizer.Symbolize(); + } + if (!result) { fprintf(stderr, "Failed to symbolize\n"); return EXIT_FAILURE; } @@ -2570,6 +2504,8 @@ struct OatdumpArgs : public CmdlineArgs { dump_code_info_stack_maps_ = true; } else if (option == "--no-disassemble") { disassemble_code_ = false; + } else if (option =="--header-only") { + dump_header_only_ = true; } else if (option.starts_with("--symbolize=")) { oat_filename_ = option.substr(strlen("--symbolize=")).data(); symbolize_ = true; @@ -2653,6 +2589,9 @@ struct OatdumpArgs : public CmdlineArgs { " --no-disassemble may be used to disable disassembly.\n" " Example: --no-disassemble\n" "\n" + " --header-only may be used to print only the oat header.\n" + " Example: --header-only\n" + "\n" " --list-classes may be used to list target file classes (can be used with filters).\n" " Example: --list-classes\n" " Example: --list-classes --class-filter=com.example.foo\n" @@ -2695,6 +2634,7 @@ struct OatdumpArgs : public CmdlineArgs { bool symbolize_ = false; bool list_classes_ = false; bool list_methods_ = false; + bool dump_header_only_ = false; uint32_t addr2instr_ = 0; const char* export_dex_location_ = nullptr; }; @@ -2717,6 +2657,7 @@ struct OatdumpMain : public CmdlineMain { args_->method_filter_, args_->list_classes_, args_->list_methods_, + args_->dump_header_only_, args_->export_dex_location_, args_->addr2instr_)); diff --git a/runtime/Android.mk b/runtime/Android.mk index e76fb8e8b5a5c333bc6a0ed0eed5aee5c069aed3..84660a319501ee46fef0e83606431624dec651f6 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -292,7 +292,8 @@ LIBART_TARGET_SRC_FILES_x86 := \ # Note that the fault_handler_x86.cc is not a mistake. This file is # shared between the x86 and x86_64 architectures. LIBART_SRC_FILES_x86_64 := \ - interpreter/mterp/mterp_stub.cc \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_x86_64.S \ arch/x86_64/context_x86_64.cc \ arch/x86_64/entrypoints_init_x86_64.cc \ arch/x86_64/jni_entrypoints_x86_64.S \ @@ -306,7 +307,8 @@ LIBART_TARGET_SRC_FILES_x86_64 := \ $(LIBART_SRC_FILES_x86_64) \ LIBART_TARGET_SRC_FILES_mips := \ - interpreter/mterp/mterp_stub.cc \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_mips.S \ arch/mips/context_mips.cc \ arch/mips/entrypoints_init_mips.cc \ arch/mips/jni_entrypoints_mips.S \ @@ -316,7 +318,8 @@ LIBART_TARGET_SRC_FILES_mips := \ arch/mips/fault_handler_mips.cc LIBART_TARGET_SRC_FILES_mips64 := \ - interpreter/mterp/mterp_stub.cc \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_mips64.S \ arch/mips64/context_mips64.cc \ arch/mips64/entrypoints_init_mips64.cc \ arch/mips64/jni_entrypoints_mips64.S \ diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index c4e314b6c8e815e69cf02345c86830b723250003..64135d8f77bf95f6296c4075056b21a4a54f199b 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -276,7 +276,7 @@ ENTRY \name bl \entrypoint @ (field_idx, Object*, new_val, referrer, Thread*) add sp, #16 @ release out args .cfi_adjust_cfa_offset -16 - RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @ TODO: we can clearly save an add here \return END \name .endm @@ -812,14 +812,23 @@ END \name .macro FOUR_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name + sub sp, #12 @ alignment padding + .cfi_adjust_cfa_offset 12 + push {r3} @ Save r3 as is it used as a temp register in the + .cfi_adjust_cfa_offset 4 @ expansion of the SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + .cfi_rel_offset r3, 0 @ macro below, which clobbers its arguments. SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC + ldr r3, [sp, 32] @ restore r3 + .cfi_restore r3 + str r9, [sp, #-16]! @ expand the frame and pass Thread::Current - .pad #16 .cfi_adjust_cfa_offset 16 bl \entrypoint add sp, #16 @ strip the extra frame .cfi_adjust_cfa_offset -16 RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME + add sp, #16 @ pop r3 + padding + .cfi_adjust_cfa_offset -16 \return END \name .endm @@ -942,7 +951,7 @@ ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_R // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. @@ -1046,6 +1055,127 @@ ENTRY art_quick_alloc_object_rosalloc RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END art_quick_alloc_object_rosalloc +// The common fast path code for art_quick_alloc_object_tlab and art_quick_alloc_object_region_tlab. +// +// r0: type_idx/return value, r1: ArtMethod*, r2: class, r9: Thread::Current, r3, r12: free. +// Need to preserve r0 and r1 to the slow path. +.macro ALLOC_OBJECT_TLAB_FAST_PATH slowPathLabel + cbz r2, \slowPathLabel // Check null class + // Check class status. + ldr r3, [r2, #MIRROR_CLASS_STATUS_OFFSET] + cmp r3, #MIRROR_CLASS_STATUS_INITIALIZED + bne \slowPathLabel + // Add a fake dependence from the + // following access flag and size + // loads to the status load. + // This is to prevent those loads + // from being reordered above the + // status load and reading wrong + // values (an alternative is to use + // a load-acquire for the status). + eor r3, r3, r3 + add r2, r2, r3 + // Check access flags has + // kAccClassIsFinalizable. + ldr r3, [r2, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET] + tst r3, #ACCESS_FLAGS_CLASS_IS_FINALIZABLE + bne \slowPathLabel + // Load thread_local_pos (r12) and + // thread_local_end (r3) with ldrd. + // Check constraints for ldrd. +#if !((THREAD_LOCAL_POS_OFFSET + 4 == THREAD_LOCAL_END_OFFSET) && (THREAD_LOCAL_POS_OFFSET % 8 == 0)) +#error "Thread::thread_local_pos/end must be consecutive and are 8 byte aligned for performance" +#endif + ldrd r12, r3, [r9, #THREAD_LOCAL_POS_OFFSET] + sub r12, r3, r12 // Compute the remaining buf size. + ldr r3, [r2, #MIRROR_CLASS_OBJECT_SIZE_OFFSET] // Load the object size (r3). + cmp r3, r12 // Check if it fits. OK to do this + // before rounding up the object size + // assuming the buf size alignment. + bhi \slowPathLabel + // "Point of no slow path". Won't go to the slow path from here on. OK to clobber r0 and r1. + // Round up the object size by the + // object alignment. (addr + 7) & ~7. + add r3, r3, #OBJECT_ALIGNMENT_MASK + and r3, r3, #OBJECT_ALIGNMENT_MASK_TOGGLED + // Reload old thread_local_pos (r0) + // for the return value. + ldr r0, [r9, #THREAD_LOCAL_POS_OFFSET] + add r1, r0, r3 + str r1, [r9, #THREAD_LOCAL_POS_OFFSET] // Store new thread_local_pos. + ldr r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] // Increment thread_local_objects. + add r1, r1, #1 + str r1, [r9, #THREAD_LOCAL_OBJECTS_OFFSET] + POISON_HEAP_REF r2 + str r2, [r0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer. + // Fence. This is "ish" not "ishst" so + // that the code after this allocation + // site will see the right values in + // the fields of the class. + // Alternatively we could use "ishst" + // if we use load-acquire for the + // class status load.) + dmb ish + bx lr +.endm + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB). +ENTRY art_quick_alloc_object_tlab + // Fast path tlab allocation. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current + // r2, r3, r12: free. +#if defined(USE_READ_BARRIER) + eor r0, r0, r0 // Read barrier not supported here. + sub r0, r0, #1 // Return -1. + bx lr +#endif + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path +.Lart_quick_alloc_object_tlab_slow_path: + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. + mov r2, r9 // Pass Thread::Current. + bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +END art_quick_alloc_object_tlab + +// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) +ENTRY art_quick_alloc_object_region_tlab + // Fast path tlab allocation. + // r0: type_idx/return value, r1: ArtMethod*, r9: Thread::Current, r2, r3, r12: free. +#if !defined(USE_READ_BARRIER) + eor r0, r0, r0 // Read barrier must be enabled here. + sub r0, r0, #1 // Return -1. + bx lr +#endif + ldr r2, [r1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_32] // Load dex cache resolved types array + // Load the class (r2) + ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] + // Read barrier for class load. + ldr r3, [r9, #THREAD_IS_GC_MARKING_OFFSET] + cbnz r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit: + ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path +.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path: + // The read barrier slow path. Mark + // the class. + push {r0, r1, r3, lr} // Save registers. r3 is pushed only + // to align sp by 16 bytes. + mov r0, r2 // Pass the class as the first param. + bl artReadBarrierMark + mov r2, r0 // Get the (marked) class back. + pop {r0, r1, r3, lr} + b .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit +.Lart_quick_alloc_object_region_tlab_slow_path: + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. + mov r2, r9 // Pass Thread::Current. + bl artAllocObjectFromCodeRegionTLAB // (uint32_t type_idx, Method* method, Thread*) + RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +END art_quick_alloc_object_region_tlab + /* * Called by managed code when the value in rSUSPEND has been decremented to 0. */ diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index e8480087a77e339829e418893fa57f4585a259ef..e4c255809b8145af085d0adee37ee4f10f77b8c5 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1537,7 +1537,7 @@ ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_R // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. @@ -1638,6 +1638,9 @@ ENTRY art_quick_alloc_object_rosalloc RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END art_quick_alloc_object_rosalloc +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + /* * Called by managed code when the thread has been asked to suspend. */ diff --git a/runtime/arch/mips/asm_support_mips.S b/runtime/arch/mips/asm_support_mips.S index 51e224cbf3be042da0e2adc4cc567f51560a0656..801f708ad390aba4f3e06bddcbb70777cf5202b6 100644 --- a/runtime/arch/mips/asm_support_mips.S +++ b/runtime/arch/mips/asm_support_mips.S @@ -129,4 +129,43 @@ #endif // USE_HEAP_POISONING .endm +// Based on contents of creg select the minimum integer +// At the end of the macro the original value of creg is lost +.macro MINint dreg,rreg,sreg,creg + .set push + .set noat +#if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6) + .ifc \dreg, \rreg + selnez \dreg, \rreg, \creg + seleqz \creg, \sreg, \creg + .else + seleqz \dreg, \sreg, \creg + selnez \creg, \rreg, \creg + .endif + or \dreg, \dreg, \creg +#else + movn \dreg, \rreg, \creg + movz \dreg, \sreg, \creg +#endif + .set pop +.endm + +// Find minimum of two signed registers +.macro MINs dreg,rreg,sreg + .set push + .set noat + slt $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + +// Find minimum of two unsigned registers +.macro MINu dreg,rreg,sreg + .set push + .set noat + sltu $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + #endif // ART_RUNTIME_ARCH_MIPS_ASM_SUPPORT_MIPS_S_ diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 699ab3e65aefd211d974501c5d4ea0cfbdc41a45..dbf0abb558336395c1631adcb3bbc6e11a186c15 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1313,7 +1313,7 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc @@ -1416,11 +1416,14 @@ ENTRY art_quick_alloc_object_rosalloc SETUP_REFS_ONLY_CALLEE_SAVE_FRAME jal artAllocObjectFromCodeRosAlloc - move $a2 ,$s1 # Pass self as argument. + move $a2, $s1 # Pass self as argument. RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER END art_quick_alloc_object_rosalloc +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast @@ -1744,5 +1747,74 @@ ENTRY_NO_GP art_quick_ushr_long nop END art_quick_ushr_long -UNIMPLEMENTED art_quick_indexof -UNIMPLEMENTED art_quick_string_compareto +/* java.lang.String.indexOf(int ch, int fromIndex=0) */ +ENTRY_NO_GP art_quick_indexof +/* $a0 holds address of "this" */ +/* $a1 holds "ch" */ +/* $a2 holds "fromIndex" */ + lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() + slt $at, $a2, $zero # if fromIndex < 0 +#if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6) + seleqz $a2, $a2, $at # fromIndex = 0; +#else + movn $a2, $zero, $at # fromIndex = 0; +#endif + subu $t0, $t0, $a2 # this.length() - fromIndex + blez $t0, 6f # if this.length()-fromIndex <= 0 + li $v0, -1 # return -1; + + sll $v0, $a2, 1 # $a0 += $a2 * 2 + addu $a0, $a0, $v0 # " " " " " + move $v0, $a2 # Set i to fromIndex. + +1: + lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch + beq $t3, $a1, 6f # return i; + addu $a0, $a0, 2 # i++ + subu $t0, $t0, 1 # this.length() - i + bnez $t0, 1b # while this.length() - i > 0 + addu $v0, $v0, 1 # i++ + + li $v0, -1 # if this.length() - i <= 0 + # return -1; + +6: + j $ra + nop +END art_quick_indexof + + .set push + .set noat +/* java.lang.String.compareTo(String anotherString) */ +ENTRY_NO_GP art_quick_string_compareto +/* $a0 holds address of "this" */ +/* $a1 holds address of "anotherString" */ + beq $a0, $a1, 9f # this and anotherString are the same object + move $v0, $zero + + lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() + lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() + MINu $t2, $a2, $a3 +# $t2 now holds min(this.length(),anotherString.length()) + + beqz $t2, 9f # while min(this.length(),anotherString.length())-i != 0 + subu $v0, $a2, $a3 # if $t2==0 return + # (this.length() - anotherString.length()) +1: + lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i) + lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bne $t0, $t1, 9f # if this.charAt(i) != anotherString.charAt(i) + subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i)) + addiu $a0, $a0, 2 # point at this.charAt(i++) + subu $t2, $t2, 1 # new value of + # min(this.length(),anotherString.length())-i + bnez $t2, 1b + addiu $a1, $a1, 2 # point at anotherString.charAt(i++) + subu $v0, $a2, $a3 + +9: + j $ra + nop +END art_quick_string_compareto + + .set pop diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index 1096af0131310c2a769cdd0a5bec32ade3a34d41..ae01bd5d18c7bac0580653017acc242bd1eafe67 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/asm_support_mips64.S b/runtime/arch/mips64/asm_support_mips64.S index b859c708baf05e6e700f09487804c4776cf5669d..786e86043e44abeab98b1c8cf6635119bf32ec5a 100644 --- a/runtime/arch/mips64/asm_support_mips64.S +++ b/runtime/arch/mips64/asm_support_mips64.S @@ -83,4 +83,38 @@ #endif // USE_HEAP_POISONING .endm +// Based on contents of creg select the minimum integer +// At the end of the macro the original value of creg is lost +.macro MINint dreg,rreg,sreg,creg + .set push + .set noat + .ifc \dreg, \rreg + selnez \dreg, \rreg, \creg + seleqz \creg, \sreg, \creg + .else + seleqz \dreg, \sreg, \creg + selnez \creg, \rreg, \creg + .endif + or \dreg, \dreg, \creg + .set pop +.endm + +// Find minimum of two signed registers +.macro MINs dreg,rreg,sreg + .set push + .set noat + slt $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + +// Find minimum of two unsigned registers +.macro MINu dreg,rreg,sreg + .set push + .set noat + sltu $at, \rreg, \sreg + MINint \dreg, \rreg, \sreg, $at + .set pop +.endm + #endif // ART_RUNTIME_ARCH_MIPS64_ASM_SUPPORT_MIPS64_S_ diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index d264c9baafd905aa0d6147ed400bdf9fd5043c34..f1e605ac4a0655ecb2891a093542d04b716eaa94 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1367,7 +1367,7 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). ENTRY art_quick_alloc_object_rosalloc @@ -1467,6 +1467,9 @@ ENTRY art_quick_alloc_object_rosalloc END art_quick_alloc_object_rosalloc +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + /* * Entry from managed code to resolve a string, this stub will allocate a String and deliver an * exception on error. On success the String is returned. A0 holds the string index. The fast @@ -1725,10 +1728,8 @@ ENTRY_NO_GP art_quick_string_compareto lw $a2,MIRROR_STRING_COUNT_OFFSET($a0) # this.length() lw $a3,MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() - sltu $at,$a2,$a3 - seleqz $t2,$a3,$at - selnez $at,$a2,$at - or $t2,$t2,$at # $t2 now holds min(this.length(),anotherString.length()) + MINu $t2, $a2, $a3 +# $t2 now holds min(this.length(),anotherString.length()) beqz $t2,9f # while min(this.length(),anotherString.length())-i != 0 subu $v0,$a2,$a3 # if $t2==0 return @@ -1753,16 +1754,18 @@ END art_quick_string_compareto /* java.lang.String.indexOf(int ch, int fromIndex=0) */ ENTRY_NO_GP art_quick_indexof /* $a0 holds address of "this" */ -/* $a1 holds address of "ch" */ -/* $a2 holds address of "fromIndex" */ +/* $a1 holds "ch" */ +/* $a2 holds "fromIndex" */ lw $t0,MIRROR_STRING_COUNT_OFFSET($a0) # this.length() - subu $t0,$t0,$a2 # this.length() - offset - blez $t0,6f # if this.length()-offset <= 0 + slt $at, $a2, $zero # if fromIndex < 0 + seleqz $a2, $a2, $at # fromIndex = 0; + subu $t0,$t0,$a2 # this.length() - fromIndex + blez $t0,6f # if this.length()-fromIndex <= 0 li $v0,-1 # return -1; sll $v0,$a2,1 # $a0 += $a2 * 2 daddu $a0,$a0,$v0 # " " " " " - move $v0,$a2 # Set i to offset. + move $v0,$a2 # Set i to fromIndex. 1: lhu $t3,MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index b027c955bf6d3c10260cb1f819dfe6dc70217a13..81fae72b44a2aaa11f9124c33e81613e7b49a1e1 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/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S index fbacdbc930a894243a86adeca7471798cffb9200..290769b365fb8fab5b652d78495275018fe87814 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -219,7 +219,8 @@ GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionI GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) +// This is to be separately defined for each architecture to allow a hand-written assembly fast path. +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB) diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index d4b873e441b285dcd6e8027003e4178a95797c18..4236c287de3fe2c96e7841406a8b5a35a70bd299 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. @@ -1196,7 +1205,7 @@ TEST_F(StubTest, AllocObjectArray) { TEST_F(StubTest, StringCompareTo) { #if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || \ - (defined(__mips__) && defined(__LP64__)) || (defined(__x86_64__) && !defined(__APPLE__)) + defined(__mips__) || (defined(__x86_64__) && !defined(__APPLE__)) // TODO: Check the "Unresolved" allocation stubs Thread* self = Thread::Current(); @@ -2045,7 +2054,7 @@ TEST_F(StubTest, IMT) { } TEST_F(StubTest, StringIndexOf) { -#if defined(__arm__) || defined(__aarch64__) || (defined(__mips__) && defined(__LP64__)) +#if defined(__arm__) || defined(__aarch64__) || defined(__mips__) Thread* self = Thread::Current(); ScopedObjectAccess soa(self); // garbage is created during ClassLinker::Init diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S index 77b8e87c99f7b96b2625fe527210058b483e905d..3e47209afbc6ab5a0dc4281f4e65d0a75eb5c1ce 100644 --- a/runtime/arch/x86/asm_support_x86.S +++ b/runtime/arch/x86/asm_support_x86.S @@ -142,6 +142,10 @@ MACRO1(POP, reg) CFI_RESTORE(REG_VAR(reg)) END_MACRO +MACRO1(CFI_RESTORE_REG, reg) + CFI_RESTORE(REG_VAR(reg)) +END_MACRO + #define UNREACHABLE int3 MACRO1(UNIMPLEMENTED,name) diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index fbee5d77247a38317185d3135e4b1b065da8d70d..125570d5bbb9f0e52f85b7ce1cca6c205813c957 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -686,7 +686,15 @@ END_MACRO MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro) DEFINE_FUNCTION VAR(c_name) + subl MACRO_LITERAL(12), %esp // alignment padding + CFI_ADJUST_CFA_OFFSET(12) + PUSH ebx // Save ebx as the expansion of the + // SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + // macro below clobbers it. SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC + movl 28(%esp), %ebx // restore ebx + CFI_RESTORE_REG ebx + // Outgoing argument set up subl MACRO_LITERAL(12), %esp // alignment padding CFI_ADJUST_CFA_OFFSET(12) @@ -700,6 +708,8 @@ MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro) addl MACRO_LITERAL(32), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-32) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address + addl MACRO_LITERAL(16), %esp // pop ebx + padding + CFI_ADJUST_CFA_OFFSET(-16) CALL_MACRO(return_macro) // return or deliver exception END_FUNCTION VAR(c_name) END_MACRO @@ -887,8 +897,8 @@ DEFINE_FUNCTION art_quick_alloc_object_rosalloc RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_FUNCTION art_quick_alloc_object_rosalloc - GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB) +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 69caec88f0c90a2b6645280a32a0370433235a46..dee8d3c6f3b9f6aed6739068781de1014f09ac20 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -809,6 +809,7 @@ END_MACRO // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR + // A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc). DEFINE_FUNCTION art_quick_alloc_object_rosalloc // Fast path rosalloc allocation. @@ -943,6 +944,8 @@ DEFINE_FUNCTION art_quick_alloc_object_tlab RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception END_FUNCTION art_quick_alloc_object_tlab +GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_region_tlab, RegionTLAB) + ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 243fdf851e5fccd40de408b670ccf5b01e995009..f97ad5156844423a0bd4fc2c9d9c61e93eb793f4 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -411,7 +411,12 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { DCHECK(method_header->Contains(pc)); return method_header; } else { - DCHECK(!code_cache->ContainsPc(reinterpret_cast(pc))) << std::hex << pc; + DCHECK(!code_cache->ContainsPc(reinterpret_cast(pc))) + << PrettyMethod(this) + << ", pc=" << std::hex << pc + << ", entry_point=" << std::hex << reinterpret_cast(existing_entry_point) + << ", copy=" << std::boolalpha << IsCopied() + << ", proxy=" << std::boolalpha << IsProxyMethod(); } } diff --git a/runtime/asm_support.h b/runtime/asm_support.h index eb3b7f353837ae7350a05c012a8d84636eeac2d0..d5f0dffc387a88cc0f3acd0a2ab5ec6579cd86e6 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -101,6 +101,11 @@ ADD_TEST_EQ(THREAD_FLAGS_OFFSET, ADD_TEST_EQ(THREAD_ID_OFFSET, art::Thread::ThinLockIdOffset<__SIZEOF_POINTER__>().Int32Value()) +// Offset of field Thread::tls32_.is_gc_marking. +#define THREAD_IS_GC_MARKING_OFFSET 52 +ADD_TEST_EQ(THREAD_IS_GC_MARKING_OFFSET, + art::Thread::IsGcMarkingOffset<__SIZEOF_POINTER__>().Int32Value()) + // Offset of field Thread::tlsPtr_.card_table. #define THREAD_CARD_TABLE_OFFSET 128 ADD_TEST_EQ(THREAD_CARD_TABLE_OFFSET, @@ -121,32 +126,32 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET, ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) +// Offset of field Thread::tlsPtr_.thread_local_objects. +#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__) +ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, + art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_pos. -#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 169 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + 2 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_end. #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, art::Thread::ThreadLocalEndOffset<__SIZEOF_POINTER__>().Int32Value()) -// Offset of field Thread::tlsPtr_.thread_local_objects. -#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__) -ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, - art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_current_ibase. -#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__) +#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. -#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__) +#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_DEFAULT_IBASE_OFFSET, art::Thread::MterpDefaultIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_alt_ibase. -#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__) +#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ALT_IBASE_OFFSET, art::Thread::MterpAltIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.rosalloc_runs. -#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 6 * __SIZEOF_POINTER__) +#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET, art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top. diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index fba26fe20aaad62f6d191ae0c691c82ffaa633ed..f871543862c7b16f9bff35a32fcc26f0655c9f89 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -184,10 +184,10 @@ MallocArena::~MallocArena() { free(reinterpret_cast(memory_)); } -MemMapArena::MemMapArena(size_t size, bool low_4gb) { +MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) { std::string error_msg; map_.reset(MemMap::MapAnonymous( - "LinearAlloc", nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); + name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg)); CHECK(map_.get() != nullptr) << error_msg; memory_ = map_->Begin(); size_ = map_->Size(); @@ -211,9 +211,12 @@ void Arena::Reset() { } } -ArenaPool::ArenaPool(bool use_malloc, bool low_4gb) - : use_malloc_(use_malloc), lock_("Arena pool lock", kArenaPoolLock), free_arenas_(nullptr), - low_4gb_(low_4gb) { +ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name) + : use_malloc_(use_malloc), + lock_("Arena pool lock", kArenaPoolLock), + free_arenas_(nullptr), + low_4gb_(low_4gb), + name_(name) { if (low_4gb) { CHECK(!use_malloc) << "low4gb must use map implementation"; } @@ -251,7 +254,7 @@ Arena* ArenaPool::AllocArena(size_t size) { } if (ret == nullptr) { ret = use_malloc_ ? static_cast(new MallocArena(size)) : - new MemMapArena(size, low_4gb_); + new MemMapArena(size, low_4gb_, name_); } ret->Reset(); return ret; diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index 8a96571e9914b7a4f210536bb70d6436438b7acb..728f897229e576e8e048571dcc3483b24d16eaac 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -261,7 +261,7 @@ class MallocArena FINAL : public Arena { class MemMapArena FINAL : public Arena { public: - MemMapArena(size_t size, bool low_4gb); + MemMapArena(size_t size, bool low_4gb, const char* name); virtual ~MemMapArena(); void Release() OVERRIDE; @@ -271,7 +271,9 @@ class MemMapArena FINAL : public Arena { class ArenaPool { public: - explicit ArenaPool(bool use_malloc = true, bool low_4gb = false); + ArenaPool(bool use_malloc = true, + bool low_4gb = false, + const char* name = "LinearAlloc"); ~ArenaPool(); Arena* AllocArena(size_t size) REQUIRES(!lock_); void FreeArenaChain(Arena* first) REQUIRES(!lock_); @@ -287,6 +289,7 @@ class ArenaPool { mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; Arena* free_arenas_ GUARDED_BY(lock_); const bool low_4gb_; + const char* name_; DISALLOW_COPY_AND_ASSIGN(ArenaPool); }; diff --git a/runtime/base/bit_field.h b/runtime/base/bit_field.h index fd65d500aa9f548d3eb5bd9ca0f784674e86d6df..a80ca28d2ec266ebe72f0820aa731357ee03d238 100644 --- a/runtime/base/bit_field.h +++ b/runtime/base/bit_field.h @@ -26,9 +26,18 @@ static constexpr uintptr_t kUintPtrTOne = 1U; // BitField is a template for encoding and decoding a bit field inside // an unsigned machine word. -template +template class BitField { public: + typedef T value_type; + static constexpr size_t position = kPosition; + static constexpr size_t size = kSize; + + static_assert(position < sizeof(uintptr_t) * kBitsPerByte, "Invalid position."); + static_assert(size != 0u, "Invalid size."); + static_assert(size <= sizeof(uintptr_t) * kBitsPerByte, "Invalid size."); + static_assert(size + position <= sizeof(uintptr_t) * kBitsPerByte, "Invalid position + size."); + // Tells whether the provided value fits into the bit field. static bool IsValid(T value) { return (static_cast(value) & ~((kUintPtrTOne << size) - 1)) == 0; diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h index 9b55e708c8e64e619538f695a015f4efade8839f..424ebb70f69208e9f5531641dc00d90c85f81c02 100644 --- a/runtime/base/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -229,6 +229,11 @@ class BitVector { */ int GetHighestBitSet() const; + // Minimum number of bits required to store this vector, 0 if none are set. + size_t GetNumberOfBits() const { + return GetHighestBitSet() + 1; + } + // Is bit set in storage. (No range check.) static bool IsBitSet(const uint32_t* storage, uint32_t idx) { return (storage[WordIndex(idx)] & BitMask(idx)) != 0; diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index b9ea4751498397731b03433634c0e6569ff4db8c..fcf3326c81e4cdfe2bbf96e42caa53b37fe45eeb 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -100,8 +100,7 @@ class CheckReferenceMapVisitor : public StackVisitor { CHECK_EQ(location.GetValue(), 0); break; default: - LOG(FATAL) << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); + LOG(FATAL) << "Unexpected location kind " << location.GetInternalKind(); } } } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 149f918dbb4c0483c6adb9701bfbcf2cb2294d8e..06895891a4e9c1fa434f68fa62dcc8c4c68fabd7 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1506,7 +1506,6 @@ bool ClassLinker::AddImageSpace( Runtime* const runtime = Runtime::Current(); gc::Heap* const heap = runtime->GetHeap(); Thread* const self = Thread::Current(); - ScopedAssertNoThreadSuspension nts(self, __FUNCTION__); StackHandleScope<2> hs(self); Handle> dex_caches( hs.NewHandle(dex_caches_object->AsObjectArray())); @@ -2676,18 +2675,6 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) { return nullptr; } -const void* ClassLinker::GetQuickOatCodeFor(const DexFile& dex_file, - uint16_t class_def_idx, - uint32_t method_idx) { - bool found; - OatFile::OatClass oat_class = FindOatClass(dex_file, class_def_idx, &found); - if (!found) { - return nullptr; - } - uint32_t oat_method_idx = GetOatMethodIndexFromMethodIndex(dex_file, class_def_idx, method_idx); - return oat_class.GetOatMethod(oat_method_idx).GetQuickCode(); -} - bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { if (UNLIKELY(method->IsNative() || method->IsProxyMethod())) { return false; @@ -2718,6 +2705,11 @@ bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* return true; } + if (Dbg::IsDebuggerActive()) { + // Boot image classes are AOT-compiled as non-debuggable. + return runtime->GetHeap()->IsInBootImageOatFile(quick_code); + } + return false; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 452edbbdfcbbaf6c81076942035e0a21174244c8..492a228522a7e6b0113107eee45e685e0fa22625 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -475,12 +475,6 @@ class ClassLinker { const void* GetQuickOatCodeFor(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_); - // Get the oat code for a method from a method index. - const void* GetQuickOatCodeFor(const DexFile& dex_file, - uint16_t class_def_idx, - uint32_t method_idx) - SHARED_REQUIRES(Locks::mutator_lock_); - // Get compiled code for a method, return null if no code // exists. This is unlike Get..OatCodeFor which will return a bridge // or interpreter entrypoint. diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 3df910161392982e913967e3a41723924fa1fff6..729957f3184a180e04e6a99a9f7cbde6868d0f0c 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -406,6 +406,7 @@ void CommonRuntimeTestImpl::TearDown() { int rmdir_cache_result = rmdir(dalvik_cache_.c_str()); ASSERT_EQ(0, rmdir_cache_result); TearDownAndroidData(android_data_, true); + dalvik_cache_.clear(); // icu4c has a fixed 10-element array "gCommonICUDataArray". // If we run > 10 tests, we fill that array and u_setCommonData fails. diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 904490aa8c11a3dd5aa3d59e0376232d247e50f2..bc6589380cfda16326009b824a3abf54e571e7b1 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -28,6 +28,7 @@ #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_instruction.h" +#include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocation_record.h" #include "gc/scoped_gc_critical_section.h" @@ -570,6 +571,29 @@ bool Dbg::RequiresDeoptimization() { return !Runtime::Current()->GetInstrumentation()->IsForcedInterpretOnly(); } +// Used to patch boot image method entry point to interpreter bridge. +class UpdateEntryPointsClassVisitor : public ClassVisitor { + public: + explicit UpdateEntryPointsClassVisitor(instrumentation::Instrumentation* instrumentation) + : instrumentation_(instrumentation) {} + + bool operator()(mirror::Class* klass) OVERRIDE REQUIRES(Locks::mutator_lock_) { + auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + for (auto& m : klass->GetMethods(pointer_size)) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && + !m.IsNative() && + !m.IsProxyMethod()) { + instrumentation_->UpdateMethodsCode(&m, GetQuickToInterpreterBridge()); + } + } + return true; + } + + private: + instrumentation::Instrumentation* const instrumentation_; +}; + void Dbg::GoActive() { // Enable all debugging features, including scans for breakpoints. // This is a no-op if we're already active. @@ -598,6 +622,14 @@ void Dbg::GoActive() { } Runtime* runtime = Runtime::Current(); + // Since boot image code is AOT compiled as not debuggable, we need to patch + // entry points of methods in boot image to interpreter bridge. + if (!runtime->GetInstrumentation()->IsForcedInterpretOnly()) { + ScopedObjectAccess soa(self); + UpdateEntryPointsClassVisitor visitor(runtime->GetInstrumentation()); + runtime->GetClassLinker()->VisitClasses(&visitor); + } + ScopedSuspendAll ssa(__FUNCTION__); if (RequiresDeoptimization()) { runtime->GetInstrumentation()->EnableDeoptimization(); diff --git a/runtime/elf.h b/runtime/elf.h index d1efc92c30010b55842cb5cec21a75ca8b63c2e3..63b18c5d34f952241a973cef2b59ef181013f0c0 100644 --- a/runtime/elf.h +++ b/runtime/elf.h @@ -1284,6 +1284,7 @@ enum : unsigned { SHT_MIPS_REGINFO = 0x70000006, // Register usage information SHT_MIPS_OPTIONS = 0x7000000d, // General options + SHT_MIPS_ABIFLAGS = 0x7000002a, // Abiflags options SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type. SHT_LOUSER = 0x80000000, // Lowest type reserved for applications. @@ -1606,7 +1607,8 @@ enum { // MIPS program header types. PT_MIPS_REGINFO = 0x70000000, // Register usage information. PT_MIPS_RTPROC = 0x70000001, // Runtime procedure table. - PT_MIPS_OPTIONS = 0x70000002 // Options segment. + PT_MIPS_OPTIONS = 0x70000002, // Options segment. + PT_MIPS_ABIFLAGS = 0x70000003 // Abiflags segment. }; // Segment flag bits. diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index fbf028d882073cdbeb662cdfc85daa11993e787f..4e01d8031283555e7840f3719602e36eb7e795e2 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -93,7 +93,7 @@ extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t); extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t); // Intrinsic entrypoints. -extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); +extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); extern "C" void* art_quick_memcpy(void*, const void*, size_t); diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index faa4747ec3e4e949cb51e7f502ef1563b6547074..79d1c1377b163f3db832e6b481687d286552c037 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -119,7 +119,7 @@ V(ShrLong, uint64_t, uint64_t, uint32_t) \ V(UshrLong, uint64_t, uint64_t, uint32_t) \ \ - V(IndexOf, int32_t, void*, uint32_t, uint32_t, uint32_t) \ + V(IndexOf, int32_t, void*, uint32_t, uint32_t) \ V(StringCompareTo, int32_t, void*, void*) \ V(Memcpy, void*, void*, const void*, size_t) \ \ diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index e72809b29707c8ed6db0390809c5df70fac4c8b3..c621672ae7a2b97cfc2c53fd4f30036528089932 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -119,10 +119,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest { // Skip across the entrypoints structures. + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, thread_local_start, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, mterp_current_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*)); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 7e242d5bb7600b7723bb09b8fb5c152fcb17ef1b..2e5b599940922a583b96c7203a2a45c67cb31706 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -59,6 +59,8 @@ #include "heap-inl.h" #include "image.h" #include "intern_table.h" +#include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" @@ -2668,6 +2670,12 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, // permanantly disabled. b/17942071 concurrent_start_bytes_ = std::numeric_limits::max(); } + + if ((gc_type == collector::kGcTypeFull) && runtime->UseJit()) { + // It's time to clear all inline caches, in case some classes can be unloaded. + runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self); + } + CHECK(collector != nullptr) << "Could not find garbage collector with collector_type=" << static_cast(collector_type_) << " and gc_type=" << gc_type; @@ -4068,6 +4076,15 @@ bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const { return false; } +bool Heap::IsInBootImageOatFile(const void* p) const { + for (gc::space::ImageSpace* space : boot_image_spaces_) { + if (space->GetOatFile()->Contains(p)) { + return true; + } + } + return false; +} + void Heap::GetBootImagesSize(uint32_t* boot_image_begin, uint32_t* boot_image_end, uint32_t* boot_oat_begin, diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 37aa82d68fe2feb75970ee3a563caff57f7b15a2..e0a53a0cc84a2948ca8b23685dd6b90e6ed03ca6 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -605,6 +605,9 @@ class Heap { bool ObjectIsInBootImageSpace(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_); + bool IsInBootImageOatFile(const void* p) const + SHARED_REQUIRES(Locks::mutator_lock_); + void GetBootImagesSize(uint32_t* boot_image_begin, uint32_t* boot_image_end, uint32_t* boot_oat_begin, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 748463529eab3264ce21dd8a7beba6c28a1fbcfb..b107b72b554c74d9e6db081fab010bdbf892586a 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -104,6 +104,14 @@ static void UpdateEntrypoints(ArtMethod* method, const void* quick_code) method->SetEntryPointFromQuickCompiledCode(quick_code); } +bool Instrumentation::NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const + SHARED_REQUIRES(Locks::mutator_lock_) { + return Dbg::IsDebuggerActive() && + Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) && + !method->IsNative() && + !method->IsProxyMethod(); +} + void Instrumentation::InstallStubsForMethod(ArtMethod* method) { if (!method->IsInvokable() || method->IsProxyMethod()) { // Do not change stubs for these methods. @@ -124,6 +132,9 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { new_quick_code = GetQuickToInterpreterBridge(); } else if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { new_quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, new_quick_code)) { + new_quick_code = GetQuickToInterpreterBridge(); + } } else { new_quick_code = GetQuickResolutionStub(); } @@ -136,10 +147,13 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { // class, all its static methods code will be set to the instrumentation entry point. // For more details, see ClassLinker::FixupStaticTrampolines. if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) { - if (entry_exit_stubs_installed_) { + new_quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, new_quick_code)) { + // Oat code should not be used. Don't install instrumentation stub and + // use interpreter for instrumentation. + new_quick_code = GetQuickToInterpreterBridge(); + } else if (entry_exit_stubs_installed_) { new_quick_code = GetQuickInstrumentationEntryPoint(); - } else { - new_quick_code = class_linker->GetQuickOatCodeFor(method); } } else { new_quick_code = GetQuickResolutionStub(); @@ -775,6 +789,9 @@ void Instrumentation::Undeoptimize(ArtMethod* method) { UpdateEntrypoints(method, GetQuickResolutionStub()); } else { const void* quick_code = class_linker->GetQuickOatCodeFor(method); + if (NeedDebugVersionForBootImageCode(method, quick_code)) { + quick_code = GetQuickToInterpreterBridge(); + } UpdateEntrypoints(method, quick_code); } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index fa2f0388dc6050f43e67a9dc6e97b392ed6f56dd..b3cdb410f386d77a499a2453510a7e58ca2c483c 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -247,6 +247,11 @@ class Instrumentation { return forced_interpret_only_; } + // Code is in boot image oat file which isn't compiled as debuggable. + // Need debug version (interpreter or jitted) if that's the case. + bool NeedDebugVersionForBootImageCode(ArtMethod* method, const void* code) const + SHARED_REQUIRES(Locks::mutator_lock_); + bool AreExitStubsInstalled() const { return instrumentation_stubs_installed_; } diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 9808e2292782b2afbad54f59fbb8a1bc37fd52ca..baf4afea184977ac8c46e844778bf7153a061046 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -240,20 +240,10 @@ static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs return os; } -#if !defined(__clang__) -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) -// TODO: remove when all targets implemented. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; -#else -static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKind; -#endif -#else + +#if defined(__clang__) // Clang 3.4 fails to build the goto interpreter implementation. -#if (defined(__arm__) || defined(__i386__) || defined(__aarch64__)) -static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; -#else -static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind; -#endif template JValue ExecuteGotoImpl(Thread*, const DexFile::CodeItem*, ShadowFrame&, JValue) { LOG(FATAL) << "UNREACHABLE"; @@ -325,12 +315,8 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, while (true) { // Mterp does not support all instrumentation/debugging. if (MterpShouldSwitchInterpreters()) { -#if !defined(__clang__) - return ExecuteGotoImpl(self, code_item, shadow_frame, result_register); -#else return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, false); -#endif } bool returned = ExecuteMterpImpl(self, code_item, &shadow_frame, &result_register); if (returned) { diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index ca8598e5e6196396de6c2f81c94c658e34b59b52..12d6fdc00d0fd6f76fad53d8ff5a1ded55dc61f1 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -792,6 +792,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -810,6 +811,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -828,6 +830,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -846,6 +849,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -864,6 +868,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -882,6 +887,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -899,6 +905,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -916,6 +923,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -933,6 +941,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -950,6 +959,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -967,6 +977,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } @@ -984,6 +995,7 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF } ADVANCE(offset); } else { + BRANCH_INSTRUMENTATION(2); ADVANCE(2); } } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 25dbab249434043c3151a0e85948df8a4da238de..0488dbf028192370b1308ed576436f0550ade782 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -712,6 +712,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -727,6 +728,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -742,6 +744,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -757,6 +760,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -772,6 +776,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -787,6 +792,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -801,6 +807,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -815,6 +822,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -829,6 +837,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -843,6 +852,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -857,6 +867,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; @@ -871,6 +882,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, } inst = inst->RelativeAt(offset); } else { + BRANCH_INSTRUMENTATION(2); inst = inst->Next_2xx(); } break; diff --git a/runtime/interpreter/mterp/arm/bincmp.S b/runtime/interpreter/mterp/arm/bincmp.S index 774e1676b7822d70b0490341186d73df962b0b3c..cfad7147e225f6107dfb07cbb59e9d01ec0d97a3 100644 --- a/runtime/interpreter/mterp/arm/bincmp.S +++ b/runtime/interpreter/mterp/arm/bincmp.S @@ -6,14 +6,15 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - b${revcmp} .L_${opcode}_not_taken + mov${revcmp} rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -21,28 +22,10 @@ bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST 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 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 - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm/binopLit8.S b/runtime/interpreter/mterp/arm/binopLit8.S index ec0b3c445d9a8f646b4a9ce11f577c1d6833dfee..b8f0d925cdb7bf225fa03bc107881f446a7ea8e8 100644 --- a/runtime/interpreter/mterp/arm/binopLit8.S +++ b/runtime/interpreter/mterp/arm/binopLit8.S @@ -13,7 +13,7 @@ * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB diff --git a/runtime/interpreter/mterp/arm/binopWide.S b/runtime/interpreter/mterp/arm/binopWide.S index 1d511ecfb011d6884ce80e1304d42e7829f3c614..4d880015c87127ff23b45d469c654250cab4cb03 100644 --- a/runtime/interpreter/mterp/arm/binopWide.S +++ b/runtime/interpreter/mterp/arm/binopWide.S @@ -19,9 +19,9 @@ mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if $chkzero diff --git a/runtime/interpreter/mterp/arm/binopWide2addr.S b/runtime/interpreter/mterp/arm/binopWide2addr.S index 81db48badeaad2b72ac08df7e881cc7eca67537c..bb16335c343ff9fcb3dab99f53ea3794b1a7cec4 100644 --- a/runtime/interpreter/mterp/arm/binopWide2addr.S +++ b/runtime/interpreter/mterp/arm/binopWide2addr.S @@ -16,8 +16,8 @@ /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if $chkzero diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S index 4c5ffc5b4ecfbd1a7d8bb941eb04061906cb879c..981c03659f5a7b5be00f763b91991c654716cc69 100644 --- a/runtime/interpreter/mterp/arm/entry.S +++ b/runtime/interpreter/mterp/arm/entry.S @@ -47,8 +47,8 @@ ExecuteMterpImpl: /* set up "named" registers */ mov rSELF, r0 ldr r0, [r2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to insns[] (i.e. - the dalivk byte code). - add rREFS, rFP, r0, lsl #2 @ point to reference array in shadow frame + add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to vregs. + VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, #CODEITEM_INSNS_OFFSET @ Point to base of insns[] add rPC, rPC, r0, lsl #1 @ Create direct pointer to 1st dex opcode diff --git a/runtime/interpreter/mterp/arm/fbinop2addr.S b/runtime/interpreter/mterp/arm/fbinop2addr.S index b052a29a8814530bc312b993d6c7802037f13ef6..53c87a08f399a463b0f427fe8ab7d7fd3bad1cc4 100644 --- a/runtime/interpreter/mterp/arm/fbinop2addr.S +++ b/runtime/interpreter/mterp/arm/fbinop2addr.S @@ -7,14 +7,12 @@ */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - $instr @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 diff --git a/runtime/interpreter/mterp/arm/fbinopWide2addr.S b/runtime/interpreter/mterp/arm/fbinopWide2addr.S index 4e7401dae3226b7b9fae5affb8cbe94627951ad4..9766e2c0c454aa1d7690d7f242433e2b4a31d408 100644 --- a/runtime/interpreter/mterp/arm/fbinopWide2addr.S +++ b/runtime/interpreter/mterp/arm/fbinopWide2addr.S @@ -8,11 +8,10 @@ */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA diff --git a/runtime/interpreter/mterp/arm/funop.S b/runtime/interpreter/mterp/arm/funop.S index d7a0859cdcce7329477829a3ed3c18fa8528af05..1b8bb8bac68c9c5f9a754ea26f211ee0169a0fcd 100644 --- a/runtime/interpreter/mterp/arm/funop.S +++ b/runtime/interpreter/mterp/arm/funop.S @@ -6,11 +6,10 @@ */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A $instr @ s1<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA diff --git a/runtime/interpreter/mterp/arm/funopNarrower.S b/runtime/interpreter/mterp/arm/funopNarrower.S index 9daec28556d2e083c6e2ef43e47d9461c746fd08..b9f758ba867e64c8374235a8013871cc7a456de8 100644 --- a/runtime/interpreter/mterp/arm/funopNarrower.S +++ b/runtime/interpreter/mterp/arm/funopNarrower.S @@ -6,11 +6,10 @@ */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB fldd d0, [r3] @ d0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A $instr @ s0<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA diff --git a/runtime/interpreter/mterp/arm/funopWider.S b/runtime/interpreter/mterp/arm/funopWider.S index 450ba3a15722460af09f8d48d7f7d15b7b0df2e5..854cdc9b66304c12086416d1f67dcb7970ab3ab7 100644 --- a/runtime/interpreter/mterp/arm/funopWider.S +++ b/runtime/interpreter/mterp/arm/funopWider.S @@ -6,11 +6,10 @@ */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A $instr @ d0<- op CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_aget_wide.S b/runtime/interpreter/mterp/arm/op_aget_wide.S index e1430b44f275a080602c55a72df8b93b1128f796..853a7a4e7987adfa35661c0a7d2b61cb1ae15ac0 100644 --- a/runtime/interpreter/mterp/arm/op_aget_wide.S +++ b/runtime/interpreter/mterp/arm/op_aget_wide.S @@ -19,7 +19,7 @@ bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrd r2, [r0, #MIRROR_WIDE_ARRAY_DATA_OFFSET] @ r2/r3<- vBB[vCC] - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_aput_wide.S b/runtime/interpreter/mterp/arm/op_aput_wide.S index 49839d1b24e9645ff5cdf703a5915fce488329ca..005750752f8dfbf3f62bc9cfa89fae58e1c166c6 100644 --- a/runtime/interpreter/mterp/arm/op_aput_wide.S +++ b/runtime/interpreter/mterp/arm/op_aput_wide.S @@ -15,7 +15,7 @@ ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width cmp r1, r3 @ compare unsigned index, length - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1 diff --git a/runtime/interpreter/mterp/arm/op_cmp_long.S b/runtime/interpreter/mterp/arm/op_cmp_long.S index 2b4c0ea5bbd75573d7cfff0f94ea08a5210a934f..e57b19c5ccef859f69e7cc58a850a4b9fa952f44 100644 --- a/runtime/interpreter/mterp/arm/op_cmp_long.S +++ b/runtime/interpreter/mterp/arm/op_cmp_long.S @@ -23,8 +23,8 @@ mov r9, rINST, lsr #8 @ r9<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 cmp r1, r3 @ compare (vBB+1, vCC+1) diff --git a/runtime/interpreter/mterp/arm/op_cmpg_double.S b/runtime/interpreter/mterp/arm/op_cmpg_double.S index 4b05c44beb1dca4b735842f7c5cdaedb9f82b32a..602a4b1bfd62931d9ad73c138f689f32f838d857 100644 --- a/runtime/interpreter/mterp/arm/op_cmpg_double.S +++ b/runtime/interpreter/mterp/arm/op_cmpg_double.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_cmpg_float.S b/runtime/interpreter/mterp/arm/op_cmpg_float.S index d5d2df2ef5a29ebf7c8155e9e1ded00affaca082..965091f82d0db1327d9c88da0b56d9d28314102d 100644 --- a/runtime/interpreter/mterp/arm/op_cmpg_float.S +++ b/runtime/interpreter/mterp/arm/op_cmpg_float.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_cmpl_double.S b/runtime/interpreter/mterp/arm/op_cmpl_double.S index 6ee53b301e6690a3330c91b810063a17193548dd..8a5e509ee8aa1bc5565772e5e50f5519ff477b5e 100644 --- a/runtime/interpreter/mterp/arm/op_cmpl_double.S +++ b/runtime/interpreter/mterp/arm/op_cmpl_double.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_cmpl_float.S b/runtime/interpreter/mterp/arm/op_cmpl_float.S index 64535b68ae899d61e92608c7e13e0b0e8d620aca..9df0c2c17199daa9c09e1cf2c0d5ce5b41b7b0bc 100644 --- a/runtime/interpreter/mterp/arm/op_cmpl_float.S +++ b/runtime/interpreter/mterp/arm/op_cmpl_float.S @@ -23,7 +23,7 @@ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_const.S b/runtime/interpreter/mterp/arm/op_const.S index de3e3c3c88aa66eda24c00a69638ff5bf9de388b..39890a085a8443c742e248404e10c3e65526b6d4 100644 --- a/runtime/interpreter/mterp/arm/op_const.S +++ b/runtime/interpreter/mterp/arm/op_const.S @@ -1,7 +1,7 @@ /* const vAA, #+BBBBbbbb */ mov r3, rINST, lsr #8 @ r3<- AA - FETCH r0, 1 @ r0<- bbbb (low - FETCH r1, 2 @ r1<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r1, 2 @ r1<- BBBB (high) FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_const_16.S b/runtime/interpreter/mterp/arm/op_const_16.S index 59c6dac10a228ba06096522298907432e23b8e1c..a30cf3a0dbb310f7b37d35d92f4759cac97bf9aa 100644 --- a/runtime/interpreter/mterp/arm/op_const_16.S +++ b/runtime/interpreter/mterp/arm/op_const_16.S @@ -1,5 +1,5 @@ /* const/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA FETCH_ADVANCE_INST 2 @ advance rPC, load rINST SET_VREG r0, r3 @ vAA<- r0 diff --git a/runtime/interpreter/mterp/arm/op_const_4.S b/runtime/interpreter/mterp/arm/op_const_4.S index c177bb9eb3ff2f62b522ee9ce1e41d46a5b0faca..c97b0e91f52f705810cc61087716b2f262dabb6f 100644 --- a/runtime/interpreter/mterp/arm/op_const_4.S +++ b/runtime/interpreter/mterp/arm/op_const_4.S @@ -1,8 +1,7 @@ /* const/4 vA, #+B */ - mov r1, rINST, lsl #16 @ r1<- Bxxx0000 + sbfx r1, rINST, #12, #4 @ r1<- sssssssB (sign-extended) ubfx r0, rINST, #8, #4 @ r0<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended) GET_INST_OPCODE ip @ ip<- opcode from rINST SET_VREG r1, r0 @ fp[A]<- r1 GOTO_OPCODE ip @ execute next instruction diff --git a/runtime/interpreter/mterp/arm/op_const_high16.S b/runtime/interpreter/mterp/arm/op_const_high16.S index 460d546f3bd6c81d8cb104caea3509f322065b7b..536276d52d76ce971d125bdee373543678c047be 100644 --- a/runtime/interpreter/mterp/arm/op_const_high16.S +++ b/runtime/interpreter/mterp/arm/op_const_high16.S @@ -1,5 +1,5 @@ /* const/high16 vAA, #+BBBB0000 */ - FETCH r0, 1 @ r0<- 0000BBBB (zero-extended + FETCH r0, 1 @ r0<- 0000BBBB (zero-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r0, r0, lsl #16 @ r0<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_const_string_jumbo.S b/runtime/interpreter/mterp/arm/op_const_string_jumbo.S index 1a3d0b25421d8d8d53638be30b46d23b8a9d02ae..1255c0768d1b9796f6ea23ada9601a43dffaf21d 100644 --- a/runtime/interpreter/mterp/arm/op_const_string_jumbo.S +++ b/runtime/interpreter/mterp/arm/op_const_string_jumbo.S @@ -1,7 +1,7 @@ /* const/string vAA, String@BBBBBBBB */ EXPORT_PC - FETCH r0, 1 @ r0<- bbbb (low - FETCH r2, 2 @ r2<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r2, 2 @ r2<- BBBB (high) mov r1, rINST, lsr #8 @ r1<- AA orr r0, r0, r2, lsl #16 @ r1<- BBBBbbbb add r2, rFP, #OFF_FP_SHADOWFRAME diff --git a/runtime/interpreter/mterp/arm/op_const_wide.S b/runtime/interpreter/mterp/arm/op_const_wide.S index 12394b6cbe6d76ff43e18abb84aa00523e5acf00..8310a4c129e5db089264e07489aabfc2ce31c286 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide.S +++ b/runtime/interpreter/mterp/arm/op_const_wide.S @@ -8,7 +8,7 @@ orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word) CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs FETCH_ADVANCE_INST 5 @ advance rPC, load rINST - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_const_wide_16.S b/runtime/interpreter/mterp/arm/op_const_wide_16.S index 3811d8641bb0e3b8e9473d161e549c445b56e75f..28abb512f02e256dbe93e3108dd725a128e6da22 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide_16.S +++ b/runtime/interpreter/mterp/arm/op_const_wide_16.S @@ -1,10 +1,10 @@ /* const-wide/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r1, r0, asr #31 @ r1<- ssssssss FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_const_wide_32.S b/runtime/interpreter/mterp/arm/op_const_wide_32.S index 0b6f1cc384bdfab8f0931dcc59348b3a7f86e69c..c10bb0461ac428a71e083f6e9d24f3f3ebeb0aa6 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide_32.S +++ b/runtime/interpreter/mterp/arm/op_const_wide_32.S @@ -5,7 +5,7 @@ FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] mov r1, r0, asr #31 @ r1<- ssssssss GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 diff --git a/runtime/interpreter/mterp/arm/op_const_wide_high16.S b/runtime/interpreter/mterp/arm/op_const_wide_high16.S index b9796eb561bde0b620fcd660364601e1c4400111..d7e38ecc20670f1cd0e82d3096a7509249927938 100644 --- a/runtime/interpreter/mterp/arm/op_const_wide_high16.S +++ b/runtime/interpreter/mterp/arm/op_const_wide_high16.S @@ -5,7 +5,7 @@ mov r1, r1, lsl #16 @ r1<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r0, r2 @ Zero shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction diff --git a/runtime/interpreter/mterp/arm/op_double_to_float.S b/runtime/interpreter/mterp/arm/op_double_to_float.S index e3270004094a8c699d9a19f23c85414810bf9057..98fdfbc64ed521d6aaa231d11a1638c9daaef7da 100644 --- a/runtime/interpreter/mterp/arm/op_double_to_float.S +++ b/runtime/interpreter/mterp/arm/op_double_to_float.S @@ -1 +1 @@ -%include "arm/funopNarrower.S" {"instr":"fcvtsd s0, d0"} +%include "arm/funopNarrower.S" {"instr":"vcvt.f32.f64 s0, d0"} diff --git a/runtime/interpreter/mterp/arm/op_float_to_double.S b/runtime/interpreter/mterp/arm/op_float_to_double.S index fb1892b6d0ad2c6ee3b21d4d4141276066b40a5d..b1e12bdc7a5db3943144acee403bf79bb882cf7a 100644 --- a/runtime/interpreter/mterp/arm/op_float_to_double.S +++ b/runtime/interpreter/mterp/arm/op_float_to_double.S @@ -1 +1 @@ -%include "arm/funopWider.S" {"instr":"fcvtds d0, s0"} +%include "arm/funopWider.S" {"instr":"vcvt.f64.f32 d0, s0"} diff --git a/runtime/interpreter/mterp/arm/op_float_to_long.S b/runtime/interpreter/mterp/arm/op_float_to_long.S index 24416d33d23edc3c00cc27486db9f1af8ab0d01a..5c8680f1338ab2599f3c02ea5d043f5759ed1d9c 100644 --- a/runtime/interpreter/mterp/arm/op_float_to_long.S +++ b/runtime/interpreter/mterp/arm/op_float_to_long.S @@ -17,7 +17,7 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes mvnne r0, #0 @ return maxlong (7fffffff) mvnne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, #0xdf000000 @ (float)minlong @@ -25,14 +25,14 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes movne r0, #0 @ return minlong (80000000) movne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, r4 bl __aeabi_fcmpeq @ is arg == self? cmp r0, #0 @ zero == no moveq r1, #0 @ return zero for NaN - ldmeqfd sp!, {r4, pc} + popeq {r4, pc} mov r0, r4 @ recover arg bl __aeabi_f2lz @ convert float to long diff --git a/runtime/interpreter/mterp/arm/op_iget_wide.S b/runtime/interpreter/mterp/arm/op_iget_wide.S index 859ffac0382877d003e89f4a726197c4b5808d8e..e287d519adbadf0de15cbef1dead42c6144768a5 100644 --- a/runtime/interpreter/mterp/arm/op_iget_wide.S +++ b/runtime/interpreter/mterp/arm/op_iget_wide.S @@ -16,7 +16,7 @@ cmp r3, #0 bne MterpException @ bail out CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] stmia r3, {r0-r1} @ fp[A]<- r0/r1 ADVANCE 2 GET_INST_OPCODE ip @ extract opcode from rINST diff --git a/runtime/interpreter/mterp/arm/op_iget_wide_quick.S b/runtime/interpreter/mterp/arm/op_iget_wide_quick.S index 07f854adf46ba323f265761fb953aa1579a32890..5a7177d8f5f86b385eb9e09f92c254f6ebbce5aa 100644 --- a/runtime/interpreter/mterp/arm/op_iget_wide_quick.S +++ b/runtime/interpreter/mterp/arm/op_iget_wide_quick.S @@ -7,7 +7,7 @@ beq common_errNullObject @ object was null ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ fp[A]<- r0/r1 diff --git a/runtime/interpreter/mterp/arm/op_instance_of.S b/runtime/interpreter/mterp/arm/op_instance_of.S index d76f0b09fee8febef05233e9705d035e5e6da83f..019929edf910da5e06d7e0159f46922466a80fd7 100644 --- a/runtime/interpreter/mterp/arm/op_instance_of.S +++ b/runtime/interpreter/mterp/arm/op_instance_of.S @@ -11,10 +11,9 @@ VREG_INDEX_TO_ADDR r1, r1 @ r1<- &object ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- method mov r3, rSELF @ r3<- self - mov r9, rINST, lsr #8 @ r9<- A+ - and r9, r9, #15 @ r9<- A bl MterpInstanceOf @ (index, &obj, method, self) ldr r1, [rSELF, #THREAD_EXCEPTION_OFFSET] + ubfx r9, rINST, #8, #4 @ r9<- A PREFETCH_INST 2 cmp r1, #0 @ exception pending? bne MterpException diff --git a/runtime/interpreter/mterp/arm/op_iput_wide.S b/runtime/interpreter/mterp/arm/op_iput_wide.S index 8bbd63efc997a0ba88997d0a74901cdfd136ea35..3dda1877b587f90d30c28b24c5b30ebb53b75190 100644 --- a/runtime/interpreter/mterp/arm/op_iput_wide.S +++ b/runtime/interpreter/mterp/arm/op_iput_wide.S @@ -5,7 +5,7 @@ mov r1, rINST, lsr #12 @ r1<- B GET_VREG r1, r1 @ r1<- fp[B], the object pointer ubfx r2, rINST, #8, #4 @ r2<- A - add r2, rFP, r2, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[A] ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp diff --git a/runtime/interpreter/mterp/arm/op_iput_wide_quick.S b/runtime/interpreter/mterp/arm/op_iput_wide_quick.S index a2fc9e11ed6c9e3ff90abf8da3070f268ad274cb..88e6ea102c36b4bee7be78bea8527c3bc2636205 100644 --- a/runtime/interpreter/mterp/arm/op_iput_wide_quick.S +++ b/runtime/interpreter/mterp/arm/op_iput_wide_quick.S @@ -5,7 +5,7 @@ ubfx r0, rINST, #8, #4 @ r0<- A cmp r2, #0 @ check object for null beq common_errNullObject @ object was null - add r0, rFP, r0, lsl #2 @ r0<- &fp[A] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[A] ldmia r0, {r0-r1} @ r0/r1<- fp[A]/fp[A+1] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST strd r0, [r2, r3] @ obj.field<- r0/r1 diff --git a/runtime/interpreter/mterp/arm/op_long_to_double.S b/runtime/interpreter/mterp/arm/op_long_to_double.S index 1d48a2acf23c18aa35f4725da46a9908146a869a..cac12d48d45386c0f2d44c4330562a8dc4823ff6 100644 --- a/runtime/interpreter/mterp/arm/op_long_to_double.S +++ b/runtime/interpreter/mterp/arm/op_long_to_double.S @@ -8,8 +8,8 @@ */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] vldr d0, [r3] @ d0<- vAA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_move_result_wide.S b/runtime/interpreter/mterp/arm/op_move_result_wide.S index 1845ccf69f4b6db5ed2246886167045968fa4802..87929eaeeb0bca5439d3e3fda5cb244efc99110c 100644 --- a/runtime/interpreter/mterp/arm/op_move_result_wide.S +++ b/runtime/interpreter/mterp/arm/op_move_result_wide.S @@ -1,7 +1,7 @@ /* move-result-wide vAA */ mov rINST, rINST, lsr #8 @ rINST<- AA ldr r3, [rFP, #OFF_FP_RESULT_REGISTER] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- retval.j CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_move_wide.S b/runtime/interpreter/mterp/arm/op_move_wide.S index f5d156d732d36af4d8553527c8d5f7a73376e366..ff353ea5d928e3f9e558182cad2e810e535cb054 100644 --- a/runtime/interpreter/mterp/arm/op_move_wide.S +++ b/runtime/interpreter/mterp/arm/op_move_wide.S @@ -2,8 +2,8 @@ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- fp[B] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_move_wide_16.S b/runtime/interpreter/mterp/arm/op_move_wide_16.S index 8a55c4b13b8cd396d4f94ba44bcd453cd336a103..9812b66e97065113ca753325474c7a7c7cbe3686 100644 --- a/runtime/interpreter/mterp/arm/op_move_wide_16.S +++ b/runtime/interpreter/mterp/arm/op_move_wide_16.S @@ -2,8 +2,8 @@ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 2 @ r3<- BBBB FETCH r2, 1 @ r2<- AAAA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add lr, rFP, r2, lsl #2 @ r2<- &fp[AAAA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR lr, r2 @ r2<- &fp[AAAA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] FETCH_ADVANCE_INST 3 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r2, r3, ip @ Zero out the shadow regs diff --git a/runtime/interpreter/mterp/arm/op_move_wide_from16.S b/runtime/interpreter/mterp/arm/op_move_wide_from16.S index b65259db50ff3321224934634e2c191647a9c878..d2cc60ca9da597d140b51d0d3235a00113b952b7 100644 --- a/runtime/interpreter/mterp/arm/op_move_wide_from16.S +++ b/runtime/interpreter/mterp/arm/op_move_wide_from16.S @@ -2,8 +2,8 @@ /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 1 @ r3<- BBBB mov rINST, rINST, lsr #8 @ rINST<- AA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 2 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_mul_long.S b/runtime/interpreter/mterp/arm/op_mul_long.S index 9e83778e2f68524d4db87b473130f0543b43f441..8f40f1976b58954145f368526c35c998a2b7661b 100644 --- a/runtime/interpreter/mterp/arm/op_mul_long.S +++ b/runtime/interpreter/mterp/arm/op_mul_long.S @@ -20,8 +20,8 @@ FETCH r0, 1 @ r0<- CCBB and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 mul ip, r2, r1 @ ip<- ZxW @@ -29,7 +29,7 @@ mla r2, r0, r3, ip @ r2<- YxX + (ZxW) mov r0, rINST, lsr #8 @ r0<- AA add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX)) - add r0, rFP, r0, lsl #2 @ r0<- &fp[AA] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[AA] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10 diff --git a/runtime/interpreter/mterp/arm/op_mul_long_2addr.S b/runtime/interpreter/mterp/arm/op_mul_long_2addr.S index 789dbd302565afa9b0b0ef4d6ed5a16b8b8563a6..7ef24c51424f065f4d66b19388c73fc424850e2a 100644 --- a/runtime/interpreter/mterp/arm/op_mul_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_mul_long_2addr.S @@ -9,8 +9,8 @@ /* mul-long/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR rINST, r9 @ rINST<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1 mul ip, r2, r1 @ ip<- ZxW diff --git a/runtime/interpreter/mterp/arm/op_return_wide.S b/runtime/interpreter/mterp/arm/op_return_wide.S index cfab5301e5434d933f04621129816096a5b3eb81..ceae878fa4d3d88b9891dc39fb9e060e392f0ab2 100644 --- a/runtime/interpreter/mterp/arm/op_return_wide.S +++ b/runtime/interpreter/mterp/arm/op_return_wide.S @@ -9,6 +9,6 @@ ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA - add r2, rFP, r2, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[AA] ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1 b MterpReturn diff --git a/runtime/interpreter/mterp/arm/op_sget_wide.S b/runtime/interpreter/mterp/arm/op_sget_wide.S index 3a5090866ae94cc32320447073bc5739ae49ca46..4f2f89d6c35807c8159344df1947c6504877aced 100644 --- a/runtime/interpreter/mterp/arm/op_sget_wide.S +++ b/runtime/interpreter/mterp/arm/op_sget_wide.S @@ -12,7 +12,7 @@ bl artGet64StaticFromCode ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET] mov r9, rINST, lsr #8 @ r9<- AA - add lr, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR lr, r9 @ r9<- &fp[AA] cmp r3, #0 @ Fail to resolve? bne MterpException @ bail out FETCH_ADVANCE_INST 2 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/op_shl_long.S b/runtime/interpreter/mterp/arm/op_shl_long.S index 12ea24883f3e2cc38444efa2c75b184355bd84e4..82ec6ed09f628ba6212578b35afc524a6ea654f5 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long.S +++ b/runtime/interpreter/mterp/arm/op_shl_long.S @@ -9,12 +9,12 @@ mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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)) diff --git a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S index 4799e772131b4699e8e351c29a2e731ff3f58041..f361a7d29c3e000163559db9fe43f1f1deeb0553 100644 --- a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S @@ -7,7 +7,7 @@ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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 diff --git a/runtime/interpreter/mterp/arm/op_shr_long.S b/runtime/interpreter/mterp/arm/op_shr_long.S index 88a13d60728fb9badf6f627c220812085505e928..a0afe5b04047a1fec0eff8cef0182e64dd76d35b 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long.S +++ b/runtime/interpreter/mterp/arm/op_shr_long.S @@ -9,12 +9,12 @@ mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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)) diff --git a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S index 78d8bb7dba2bf009cdea80b34b32294ee990752b..976110efd4140bba28ba64e9041deb2c0da3f060 100644 --- a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S @@ -7,7 +7,7 @@ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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 diff --git a/runtime/interpreter/mterp/arm/op_sput_wide.S b/runtime/interpreter/mterp/arm/op_sput_wide.S index adbcffa5a9575f3eaf1842aeb9cf5ca1fa935c57..8d8ed8c4a29310fcff7e1e536d33894629464bce 100644 --- a/runtime/interpreter/mterp/arm/op_sput_wide.S +++ b/runtime/interpreter/mterp/arm/op_sput_wide.S @@ -8,7 +8,7 @@ FETCH r0, 1 @ r0<- field ref BBBB ldr r1, [rFP, #OFF_FP_METHOD] mov r2, rINST, lsr #8 @ r3<- AA - add r2, rFP, r2, lsl #2 + VREG_INDEX_TO_ADDR r2, r2 mov r3, rSELF PREFETCH_INST 2 @ Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp diff --git a/runtime/interpreter/mterp/arm/op_ushr_long.S b/runtime/interpreter/mterp/arm/op_ushr_long.S index f98ec639faf9d12644350dc7801c02f79d30d9d2..c817bc9fb958f9e27f60656980e7d2b16a971773 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long.S @@ -9,12 +9,12 @@ mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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)) diff --git a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S index 840283dd58bdb8c94d3807e16ac0bca91435fe79..2735f8733aafba2f3b9aba192e28c655ffb4eade 100644 --- a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S +++ b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S @@ -7,7 +7,7 @@ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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 diff --git a/runtime/interpreter/mterp/arm/unopNarrower.S b/runtime/interpreter/mterp/arm/unopNarrower.S index a5fc02797de4e62ad572727e33037343ded2c5ca..2d0453aeb1a991d78773bb1b1b30303cb976a2fc 100644 --- a/runtime/interpreter/mterp/arm/unopNarrower.S +++ b/runtime/interpreter/mterp/arm/unopNarrower.S @@ -12,7 +12,7 @@ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST $preinstr @ optional op; may set condition codes diff --git a/runtime/interpreter/mterp/arm/unopWide.S b/runtime/interpreter/mterp/arm/unopWide.S index a07423468df28ed85f7a54b9ee0bc1b68475a682..cd5defd616dbc05d333edddb3289d8c469a2cc56 100644 --- a/runtime/interpreter/mterp/arm/unopWide.S +++ b/runtime/interpreter/mterp/arm/unopWide.S @@ -9,8 +9,8 @@ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/unopWider.S b/runtime/interpreter/mterp/arm/unopWider.S index 23b6b9d2f52609285eb7b2166a68eacf9a163b26..9d504899b838138ef06101beedf2f7152a229758 100644 --- a/runtime/interpreter/mterp/arm/unopWider.S +++ b/runtime/interpreter/mterp/arm/unopWider.S @@ -10,7 +10,7 @@ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A GET_VREG r0, r3 @ r0<- vB - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] $preinstr @ optional op; may set condition codes CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST diff --git a/runtime/interpreter/mterp/arm/zcmp.S b/runtime/interpreter/mterp/arm/zcmp.S index 800804d95eddfb8d56c6d5d0485cf669f0587fb4..3d7dec006dacbe4f43a8496edaa161e367ca98a3 100644 --- a/runtime/interpreter/mterp/arm/zcmp.S +++ b/runtime/interpreter/mterp/arm/zcmp.S @@ -6,13 +6,14 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - b${revcmp} .L_${opcode}_not_taken + mov${revcmp} rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -20,25 +21,9 @@ bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif 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 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 rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/bincmp.S b/runtime/interpreter/mterp/arm64/bincmp.S index ed850fc49d2acc402592eeceedd2f1f126a0f624..2356ecbb89eeab178947a2307eaae9928ebe25ac 100644 --- a/runtime/interpreter/mterp/arm64/bincmp.S +++ b/runtime/interpreter/mterp/arm64/bincmp.S @@ -6,43 +6,26 @@ * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - 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: + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. 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 +#endif 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 GOTO_OPCODE ip // jump to next instruction -#else - 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 - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S index f9073ab5d98000aca21dc8774a6c18457942a0d8..23e656e826986eaeaaead495a8b8c10d02269635 100644 --- a/runtime/interpreter/mterp/arm64/entry.S +++ b/runtime/interpreter/mterp/arm64/entry.S @@ -46,7 +46,7 @@ ExecuteMterpImpl: /* set up "named" registers */ mov xSELF, x0 ldr w0, [x2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to insns[] (i.e. - the dalivk byte code). + add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to vregs. add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, #CODEITEM_INSNS_OFFSET // Point to base of insns[] diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 722375002b63934434d5f7a528668476966a8620..7101ba972cf9fbe3ccf18378d140ee726e59d260 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -272,7 +272,7 @@ codes. * Convert a virtual register index into an address. */ .macro VREG_INDEX_TO_ADDR reg, vreg - add \reg, xFP, \vreg, lsl #2 /* WARNING/FIXME: handle shadow frame vreg zero if store */ + add \reg, xFP, \vreg, lsl #2 /* WARNING: handle shadow frame vreg zero if store */ .endm /* diff --git a/runtime/interpreter/mterp/arm64/op_iput_wide.S b/runtime/interpreter/mterp/arm64/op_iput_wide.S index 4ce95251f642511ccde6b61b19981504a8d91cfa..e1ab1271f54d6efbd76aae457ea16ef7e45fad3c 100644 --- a/runtime/interpreter/mterp/arm64/op_iput_wide.S +++ b/runtime/interpreter/mterp/arm64/op_iput_wide.S @@ -5,7 +5,7 @@ lsr w1, wINST, #12 // w1<- B GET_VREG w1, w1 // w1<- fp[B], the object pointer ubfx w2, wINST, #8, #4 // w2<- A - add x2, xFP, x2, lsl #2 // w2<- &fp[A] + VREG_INDEX_TO_ADDR x2, x2 // w2<- &fp[A] ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp diff --git a/runtime/interpreter/mterp/arm64/op_sput_wide.S b/runtime/interpreter/mterp/arm64/op_sput_wide.S index 1d034ecf2ff89ef9825afa05cd1a749488f93503..a79b1a6172b5e5693767c8ca23196e0534646a29 100644 --- a/runtime/interpreter/mterp/arm64/op_sput_wide.S +++ b/runtime/interpreter/mterp/arm64/op_sput_wide.S @@ -8,7 +8,7 @@ FETCH w0, 1 // w0<- field ref BBBB ldr x1, [xFP, #OFF_FP_METHOD] lsr w2, wINST, #8 // w3<- AA - add x2, xFP, w2, lsl #2 + VREG_INDEX_TO_ADDR x2, w2 mov x3, xSELF PREFETCH_INST 2 // Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp diff --git a/runtime/interpreter/mterp/arm64/zcmp.S b/runtime/interpreter/mterp/arm64/zcmp.S index e528d9f03032c997d11f4dc6815c8a3cb3f1f24f..3f1e1b180f19aebd3eb1273e018cd0d1462cdeb2 100644 --- a/runtime/interpreter/mterp/arm64/zcmp.S +++ b/runtime/interpreter/mterp/arm64/zcmp.S @@ -6,39 +6,24 @@ * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - 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: + csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. 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] 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 - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif diff --git a/runtime/interpreter/mterp/config_mips b/runtime/interpreter/mterp/config_mips index d1221f731bef7e9fcd3f74925d22e3d79040a7a1..c6292c3c3761a6575905fb7d823cbba4b68ce567 100644 --- a/runtime/interpreter/mterp/config_mips +++ b/runtime/interpreter/mterp/config_mips @@ -1,4 +1,4 @@ -# 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. @@ -13,7 +13,7 @@ # limitations under the License. # -# Configuration for MIPS_32 +# Configuration for MIPS_32 targets. # handler-style computed-goto @@ -33,265 +33,265 @@ fallback-stub mips/fallback.S # opcode list; argument to op-start is default directory op-start mips - # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp - # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK + # (override example:) op op_sub_float_2addr arm-vfp + # (fallback example:) op op_sub_float_2addr FALLBACK - op op_nop FALLBACK - op op_move FALLBACK - op op_move_from16 FALLBACK - op op_move_16 FALLBACK - op op_move_wide FALLBACK - op op_move_wide_from16 FALLBACK - op op_move_wide_16 FALLBACK - op op_move_object FALLBACK - op op_move_object_from16 FALLBACK - op op_move_object_16 FALLBACK - op op_move_result FALLBACK - op op_move_result_wide FALLBACK - op op_move_result_object FALLBACK - op op_move_exception FALLBACK - op op_return_void FALLBACK - op op_return FALLBACK - op op_return_wide FALLBACK - op op_return_object FALLBACK - op op_const_4 FALLBACK - op op_const_16 FALLBACK - op op_const FALLBACK - op op_const_high16 FALLBACK - op op_const_wide_16 FALLBACK - op op_const_wide_32 FALLBACK - op op_const_wide FALLBACK - op op_const_wide_high16 FALLBACK - op op_const_string FALLBACK - op op_const_string_jumbo FALLBACK - op op_const_class FALLBACK - op op_monitor_enter FALLBACK - op op_monitor_exit FALLBACK - op op_check_cast FALLBACK - op op_instance_of FALLBACK - op op_array_length FALLBACK - op op_new_instance FALLBACK - op op_new_array FALLBACK - op op_filled_new_array FALLBACK - op op_filled_new_array_range FALLBACK - op op_fill_array_data FALLBACK - op op_throw FALLBACK - op op_goto FALLBACK - op op_goto_16 FALLBACK - op op_goto_32 FALLBACK - op op_packed_switch FALLBACK - op op_sparse_switch FALLBACK - op op_cmpl_float FALLBACK - op op_cmpg_float FALLBACK - op op_cmpl_double FALLBACK - op op_cmpg_double FALLBACK - op op_cmp_long FALLBACK - op op_if_eq FALLBACK - op op_if_ne FALLBACK - op op_if_lt FALLBACK - op op_if_ge FALLBACK - op op_if_gt FALLBACK - op op_if_le FALLBACK - op op_if_eqz FALLBACK - op op_if_nez FALLBACK - op op_if_ltz FALLBACK - op op_if_gez FALLBACK - op op_if_gtz FALLBACK - op op_if_lez FALLBACK - op_unused_3e FALLBACK - op_unused_3f FALLBACK - op_unused_40 FALLBACK - op_unused_41 FALLBACK - op_unused_42 FALLBACK - op_unused_43 FALLBACK - op op_aget FALLBACK - op op_aget_wide FALLBACK - op op_aget_object FALLBACK - op op_aget_boolean FALLBACK - op op_aget_byte FALLBACK - op op_aget_char FALLBACK - op op_aget_short FALLBACK - op op_aput FALLBACK - op op_aput_wide FALLBACK - op op_aput_object FALLBACK - op op_aput_boolean FALLBACK - op op_aput_byte FALLBACK - op op_aput_char FALLBACK - op op_aput_short FALLBACK - op op_iget FALLBACK - op op_iget_wide FALLBACK - op op_iget_object FALLBACK - op op_iget_boolean FALLBACK - op op_iget_byte FALLBACK - op op_iget_char FALLBACK - op op_iget_short FALLBACK - op op_iput FALLBACK - op op_iput_wide FALLBACK - op op_iput_object FALLBACK - op op_iput_boolean FALLBACK - op op_iput_byte FALLBACK - op op_iput_char FALLBACK - op op_iput_short FALLBACK - op op_sget FALLBACK - op op_sget_wide FALLBACK - op op_sget_object FALLBACK - op op_sget_boolean FALLBACK - op op_sget_byte FALLBACK - op op_sget_char FALLBACK - op op_sget_short FALLBACK - op op_sput FALLBACK - op op_sput_wide FALLBACK - op op_sput_object FALLBACK - op op_sput_boolean FALLBACK - op op_sput_byte FALLBACK - op op_sput_char FALLBACK - op op_sput_short FALLBACK - op op_invoke_virtual FALLBACK - op op_invoke_super FALLBACK - op op_invoke_direct FALLBACK - op op_invoke_static FALLBACK - op op_invoke_interface FALLBACK - op op_return_void_no_barrier FALLBACK - op op_invoke_virtual_range FALLBACK - op op_invoke_super_range FALLBACK - op op_invoke_direct_range FALLBACK - op op_invoke_static_range FALLBACK - op op_invoke_interface_range FALLBACK - op_unused_79 FALLBACK - op_unused_7a FALLBACK - op op_neg_int FALLBACK - op op_not_int FALLBACK - op op_neg_long FALLBACK - op op_not_long FALLBACK - op op_neg_float FALLBACK - op op_neg_double FALLBACK - op op_int_to_long FALLBACK - op op_int_to_float FALLBACK - op op_int_to_double FALLBACK - op op_long_to_int FALLBACK - op op_long_to_float FALLBACK - op op_long_to_double FALLBACK - op op_float_to_int FALLBACK - op op_float_to_long FALLBACK - op op_float_to_double FALLBACK - op op_double_to_int FALLBACK - op op_double_to_long FALLBACK - op op_double_to_float FALLBACK - op op_int_to_byte FALLBACK - op op_int_to_char FALLBACK - op op_int_to_short FALLBACK - op op_add_int FALLBACK - op op_sub_int FALLBACK - op op_mul_int FALLBACK - op op_div_int FALLBACK - op op_rem_int FALLBACK - op op_and_int FALLBACK - op op_or_int FALLBACK - op op_xor_int FALLBACK - op op_shl_int FALLBACK - op op_shr_int FALLBACK - op op_ushr_int FALLBACK - op op_add_long FALLBACK - op op_sub_long FALLBACK - op op_mul_long FALLBACK - op op_div_long FALLBACK - op op_rem_long FALLBACK - op op_and_long FALLBACK - op op_or_long FALLBACK - op op_xor_long FALLBACK - op op_shl_long FALLBACK - op op_shr_long FALLBACK - op op_ushr_long FALLBACK - op op_add_float FALLBACK - op op_sub_float FALLBACK - op op_mul_float FALLBACK - op op_div_float FALLBACK - op op_rem_float FALLBACK - op op_add_double FALLBACK - op op_sub_double FALLBACK - op op_mul_double FALLBACK - op op_div_double FALLBACK - op op_rem_double FALLBACK - op op_add_int_2addr FALLBACK - op op_sub_int_2addr FALLBACK - op op_mul_int_2addr FALLBACK - op op_div_int_2addr FALLBACK - op op_rem_int_2addr FALLBACK - op op_and_int_2addr FALLBACK - op op_or_int_2addr FALLBACK - op op_xor_int_2addr FALLBACK - op op_shl_int_2addr FALLBACK - op op_shr_int_2addr FALLBACK - op op_ushr_int_2addr FALLBACK - op op_add_long_2addr FALLBACK - op op_sub_long_2addr FALLBACK - op op_mul_long_2addr FALLBACK - op op_div_long_2addr FALLBACK - op op_rem_long_2addr FALLBACK - op op_and_long_2addr FALLBACK - op op_or_long_2addr FALLBACK - op op_xor_long_2addr FALLBACK - op op_shl_long_2addr FALLBACK - op op_shr_long_2addr FALLBACK - op op_ushr_long_2addr FALLBACK - op op_add_float_2addr FALLBACK - op op_sub_float_2addr FALLBACK - op op_mul_float_2addr FALLBACK - op op_div_float_2addr FALLBACK - op op_rem_float_2addr FALLBACK - op op_add_double_2addr FALLBACK - op op_sub_double_2addr FALLBACK - op op_mul_double_2addr FALLBACK - op op_div_double_2addr FALLBACK - op op_rem_double_2addr FALLBACK - op op_add_int_lit16 FALLBACK - op op_rsub_int FALLBACK - op op_mul_int_lit16 FALLBACK - op op_div_int_lit16 FALLBACK - op op_rem_int_lit16 FALLBACK - op op_and_int_lit16 FALLBACK - op op_or_int_lit16 FALLBACK - op op_xor_int_lit16 FALLBACK - op op_add_int_lit8 FALLBACK - op op_rsub_int_lit8 FALLBACK - op op_mul_int_lit8 FALLBACK - op op_div_int_lit8 FALLBACK - op op_rem_int_lit8 FALLBACK - op op_and_int_lit8 FALLBACK - op op_or_int_lit8 FALLBACK - op op_xor_int_lit8 FALLBACK - op op_shl_int_lit8 FALLBACK - op op_shr_int_lit8 FALLBACK - op op_ushr_int_lit8 FALLBACK - op op_iget_quick FALLBACK - op op_iget_wide_quick FALLBACK - op op_iget_object_quick FALLBACK - op op_iput_quick FALLBACK - op op_iput_wide_quick FALLBACK - op op_iput_object_quick FALLBACK - op op_invoke_virtual_quick FALLBACK - op op_invoke_virtual_range_quick FALLBACK - op op_iput_boolean_quick FALLBACK - op op_iput_byte_quick FALLBACK - op op_iput_char_quick FALLBACK - op op_iput_short_quick FALLBACK - op op_iget_boolean_quick FALLBACK - op op_iget_byte_quick FALLBACK - op op_iget_char_quick FALLBACK - op op_iget_short_quick FALLBACK - op_unused_f3 FALLBACK - op_unused_f4 FALLBACK - op_unused_f5 FALLBACK - op_unused_f6 FALLBACK - op_unused_f7 FALLBACK - op_unused_f8 FALLBACK - op_unused_f9 FALLBACK - op_unused_fa FALLBACK - op_unused_fb FALLBACK - op_unused_fc FALLBACK - op_unused_fd FALLBACK - op_unused_fe FALLBACK - op_unused_ff FALLBACK + # op op_nop FALLBACK + # op op_move FALLBACK + # op op_move_from16 FALLBACK + # op op_move_16 FALLBACK + # op op_move_wide FALLBACK + # op op_move_wide_from16 FALLBACK + # op op_move_wide_16 FALLBACK + # op op_move_object FALLBACK + # op op_move_object_from16 FALLBACK + # op op_move_object_16 FALLBACK + # op op_move_result FALLBACK + # op op_move_result_wide FALLBACK + # op op_move_result_object FALLBACK + # op op_move_exception FALLBACK + # op op_return_void FALLBACK + # op op_return FALLBACK + # op op_return_wide FALLBACK + # op op_return_object FALLBACK + # op op_const_4 FALLBACK + # op op_const_16 FALLBACK + # op op_const FALLBACK + # op op_const_high16 FALLBACK + # op op_const_wide_16 FALLBACK + # op op_const_wide_32 FALLBACK + # op op_const_wide FALLBACK + # op op_const_wide_high16 FALLBACK + # op op_const_string FALLBACK + # op op_const_string_jumbo FALLBACK + # op op_const_class FALLBACK + # op op_monitor_enter FALLBACK + # op op_monitor_exit FALLBACK + # op op_check_cast FALLBACK + # op op_instance_of FALLBACK + # op op_array_length FALLBACK + # op op_new_instance FALLBACK + # op op_new_array FALLBACK + # op op_filled_new_array FALLBACK + # op op_filled_new_array_range FALLBACK + # op op_fill_array_data FALLBACK + # op op_throw FALLBACK + # op op_goto FALLBACK + # op op_goto_16 FALLBACK + # op op_goto_32 FALLBACK + # op op_packed_switch FALLBACK + # op op_sparse_switch FALLBACK + # op op_cmpl_float FALLBACK + # op op_cmpg_float FALLBACK + # op op_cmpl_double FALLBACK + # op op_cmpg_double FALLBACK + # op op_cmp_long FALLBACK + # op op_if_eq FALLBACK + # op op_if_ne FALLBACK + # op op_if_lt FALLBACK + # op op_if_ge FALLBACK + # op op_if_gt FALLBACK + # op op_if_le FALLBACK + # op op_if_eqz FALLBACK + # op op_if_nez FALLBACK + # op op_if_ltz FALLBACK + # op op_if_gez FALLBACK + # op op_if_gtz FALLBACK + # op op_if_lez FALLBACK + # op op_unused_3e FALLBACK + # op op_unused_3f FALLBACK + # op op_unused_40 FALLBACK + # op op_unused_41 FALLBACK + # op op_unused_42 FALLBACK + # op op_unused_43 FALLBACK + # op op_aget FALLBACK + # op op_aget_wide FALLBACK + # op op_aget_object FALLBACK + # op op_aget_boolean FALLBACK + # op op_aget_byte FALLBACK + # op op_aget_char FALLBACK + # op op_aget_short FALLBACK + # op op_aput FALLBACK + # op op_aput_wide FALLBACK + # op op_aput_object FALLBACK + # op op_aput_boolean FALLBACK + # op op_aput_byte FALLBACK + # op op_aput_char FALLBACK + # op op_aput_short FALLBACK + # op op_iget FALLBACK + # op op_iget_wide FALLBACK + # op op_iget_object FALLBACK + # op op_iget_boolean FALLBACK + # op op_iget_byte FALLBACK + # op op_iget_char FALLBACK + # op op_iget_short FALLBACK + # op op_iput FALLBACK + # op op_iput_wide FALLBACK + # op op_iput_object FALLBACK + # op op_iput_boolean FALLBACK + # op op_iput_byte FALLBACK + # op op_iput_char FALLBACK + # op op_iput_short FALLBACK + # op op_sget FALLBACK + # op op_sget_wide FALLBACK + # op op_sget_object FALLBACK + # op op_sget_boolean FALLBACK + # op op_sget_byte FALLBACK + # op op_sget_char FALLBACK + # op op_sget_short FALLBACK + # op op_sput FALLBACK + # op op_sput_wide FALLBACK + # op op_sput_object FALLBACK + # op op_sput_boolean FALLBACK + # op op_sput_byte FALLBACK + # op op_sput_char FALLBACK + # op op_sput_short FALLBACK + # op op_invoke_virtual FALLBACK + # op op_invoke_super FALLBACK + # op op_invoke_direct FALLBACK + # op op_invoke_static FALLBACK + # op op_invoke_interface FALLBACK + # op op_return_void_no_barrier FALLBACK + # op op_invoke_virtual_range FALLBACK + # op op_invoke_super_range FALLBACK + # op op_invoke_direct_range FALLBACK + # op op_invoke_static_range FALLBACK + # op op_invoke_interface_range FALLBACK + # op op_unused_79 FALLBACK + # op op_unused_7a FALLBACK + # op op_neg_int FALLBACK + # op op_not_int FALLBACK + # op op_neg_long FALLBACK + # op op_not_long FALLBACK + # op op_neg_float FALLBACK + # op op_neg_double FALLBACK + # op op_int_to_long FALLBACK + # op op_int_to_float FALLBACK + # op op_int_to_double FALLBACK + # op op_long_to_int FALLBACK + # op op_long_to_float FALLBACK + # op op_long_to_double FALLBACK + # op op_float_to_int FALLBACK + # op op_float_to_long FALLBACK + # op op_float_to_double FALLBACK + # op op_double_to_int FALLBACK + # op op_double_to_long FALLBACK + # op op_double_to_float FALLBACK + # op op_int_to_byte FALLBACK + # op op_int_to_char FALLBACK + # op op_int_to_short FALLBACK + # op op_add_int FALLBACK + # op op_sub_int FALLBACK + # op op_mul_int FALLBACK + # op op_div_int FALLBACK + # op op_rem_int FALLBACK + # op op_and_int FALLBACK + # op op_or_int FALLBACK + # op op_xor_int FALLBACK + # op op_shl_int FALLBACK + # op op_shr_int FALLBACK + # op op_ushr_int FALLBACK + # op op_add_long FALLBACK + # op op_sub_long FALLBACK + # op op_mul_long FALLBACK + # op op_div_long FALLBACK + # op op_rem_long FALLBACK + # op op_and_long FALLBACK + # op op_or_long FALLBACK + # op op_xor_long FALLBACK + # op op_shl_long FALLBACK + # op op_shr_long FALLBACK + # op op_ushr_long FALLBACK + # op op_add_float FALLBACK + # op op_sub_float FALLBACK + # op op_mul_float FALLBACK + # op op_div_float FALLBACK + # op op_rem_float FALLBACK + # op op_add_double FALLBACK + # op op_sub_double FALLBACK + # op op_mul_double FALLBACK + # op op_div_double FALLBACK + # op op_rem_double FALLBACK + # op op_add_int_2addr FALLBACK + # op op_sub_int_2addr FALLBACK + # op op_mul_int_2addr FALLBACK + # op op_div_int_2addr FALLBACK + # op op_rem_int_2addr FALLBACK + # op op_and_int_2addr FALLBACK + # op op_or_int_2addr FALLBACK + # op op_xor_int_2addr FALLBACK + # op op_shl_int_2addr FALLBACK + # op op_shr_int_2addr FALLBACK + # op op_ushr_int_2addr FALLBACK + # op op_add_long_2addr FALLBACK + # op op_sub_long_2addr FALLBACK + # op op_mul_long_2addr FALLBACK + # op op_div_long_2addr FALLBACK + # op op_rem_long_2addr FALLBACK + # op op_and_long_2addr FALLBACK + # op op_or_long_2addr FALLBACK + # op op_xor_long_2addr FALLBACK + # op op_shl_long_2addr FALLBACK + # op op_shr_long_2addr FALLBACK + # op op_ushr_long_2addr FALLBACK + # op op_add_float_2addr FALLBACK + # op op_sub_float_2addr FALLBACK + # op op_mul_float_2addr FALLBACK + # op op_div_float_2addr FALLBACK + # op op_rem_float_2addr FALLBACK + # op op_add_double_2addr FALLBACK + # op op_sub_double_2addr FALLBACK + # op op_mul_double_2addr FALLBACK + # op op_div_double_2addr FALLBACK + # op op_rem_double_2addr FALLBACK + # op op_add_int_lit16 FALLBACK + # op op_rsub_int FALLBACK + # op op_mul_int_lit16 FALLBACK + # op op_div_int_lit16 FALLBACK + # op op_rem_int_lit16 FALLBACK + # op op_and_int_lit16 FALLBACK + # op op_or_int_lit16 FALLBACK + # op op_xor_int_lit16 FALLBACK + # op op_add_int_lit8 FALLBACK + # op op_rsub_int_lit8 FALLBACK + # op op_mul_int_lit8 FALLBACK + # op op_div_int_lit8 FALLBACK + # op op_rem_int_lit8 FALLBACK + # op op_and_int_lit8 FALLBACK + # op op_or_int_lit8 FALLBACK + # op op_xor_int_lit8 FALLBACK + # op op_shl_int_lit8 FALLBACK + # op op_shr_int_lit8 FALLBACK + # op op_ushr_int_lit8 FALLBACK + # op op_iget_quick FALLBACK + # op op_iget_wide_quick FALLBACK + # op op_iget_object_quick FALLBACK + # op op_iput_quick FALLBACK + # op op_iput_wide_quick FALLBACK + # op op_iput_object_quick FALLBACK + # op op_invoke_virtual_quick FALLBACK + # op op_invoke_virtual_range_quick FALLBACK + # op op_iput_boolean_quick FALLBACK + # op op_iput_byte_quick FALLBACK + # op op_iput_char_quick FALLBACK + # op op_iput_short_quick FALLBACK + # op op_iget_boolean_quick FALLBACK + # op op_iget_byte_quick FALLBACK + # op op_iget_char_quick FALLBACK + # op op_iget_short_quick FALLBACK + op op_invoke_lambda FALLBACK + # op op_unused_f4 FALLBACK + op op_capture_variable FALLBACK + op op_create_lambda FALLBACK + op op_liberate_variable FALLBACK + op op_box_lambda FALLBACK + op op_unbox_lambda FALLBACK + # op op_unused_fa FALLBACK + # op op_unused_fb FALLBACK + # op op_unused_fc FALLBACK + # op op_unused_fd FALLBACK + # op op_unused_fe FALLBACK + # op op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/config_mips64 b/runtime/interpreter/mterp/config_mips64 index f804ce5566f79431d2b956a630ed7440b3c847dd..c40c007eecfe34247551f7ce3ba733c8f2ca3503 100644 --- a/runtime/interpreter/mterp/config_mips64 +++ b/runtime/interpreter/mterp/config_mips64 @@ -36,262 +36,262 @@ op-start mips64 # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK - op op_nop FALLBACK - op op_move FALLBACK - op op_move_from16 FALLBACK - op op_move_16 FALLBACK - op op_move_wide FALLBACK - op op_move_wide_from16 FALLBACK - op op_move_wide_16 FALLBACK - op op_move_object FALLBACK - op op_move_object_from16 FALLBACK - op op_move_object_16 FALLBACK - op op_move_result FALLBACK - op op_move_result_wide FALLBACK - op op_move_result_object FALLBACK - op op_move_exception FALLBACK - op op_return_void FALLBACK - op op_return FALLBACK - op op_return_wide FALLBACK - op op_return_object FALLBACK - op op_const_4 FALLBACK - op op_const_16 FALLBACK - op op_const FALLBACK - op op_const_high16 FALLBACK - op op_const_wide_16 FALLBACK - op op_const_wide_32 FALLBACK - op op_const_wide FALLBACK - op op_const_wide_high16 FALLBACK - op op_const_string FALLBACK - op op_const_string_jumbo FALLBACK - op op_const_class FALLBACK - op op_monitor_enter FALLBACK - op op_monitor_exit FALLBACK - op op_check_cast FALLBACK - op op_instance_of FALLBACK - op op_array_length FALLBACK - op op_new_instance FALLBACK - op op_new_array FALLBACK - op op_filled_new_array FALLBACK - op op_filled_new_array_range FALLBACK - op op_fill_array_data FALLBACK - op op_throw FALLBACK - op op_goto FALLBACK - op op_goto_16 FALLBACK - op op_goto_32 FALLBACK - op op_packed_switch FALLBACK - op op_sparse_switch FALLBACK - op op_cmpl_float FALLBACK - op op_cmpg_float FALLBACK - op op_cmpl_double FALLBACK - op op_cmpg_double FALLBACK - op op_cmp_long FALLBACK - op op_if_eq FALLBACK - op op_if_ne FALLBACK - op op_if_lt FALLBACK - op op_if_ge FALLBACK - op op_if_gt FALLBACK - op op_if_le FALLBACK - op op_if_eqz FALLBACK - op op_if_nez FALLBACK - op op_if_ltz FALLBACK - op op_if_gez FALLBACK - op op_if_gtz FALLBACK - op op_if_lez FALLBACK - op_unused_3e FALLBACK - op_unused_3f FALLBACK - op_unused_40 FALLBACK - op_unused_41 FALLBACK - op_unused_42 FALLBACK - op_unused_43 FALLBACK - op op_aget FALLBACK - op op_aget_wide FALLBACK - op op_aget_object FALLBACK - op op_aget_boolean FALLBACK - op op_aget_byte FALLBACK - op op_aget_char FALLBACK - op op_aget_short FALLBACK - op op_aput FALLBACK - op op_aput_wide FALLBACK - op op_aput_object FALLBACK - op op_aput_boolean FALLBACK - op op_aput_byte FALLBACK - op op_aput_char FALLBACK - op op_aput_short FALLBACK - op op_iget FALLBACK - op op_iget_wide FALLBACK - op op_iget_object FALLBACK - op op_iget_boolean FALLBACK - op op_iget_byte FALLBACK - op op_iget_char FALLBACK - op op_iget_short FALLBACK - op op_iput FALLBACK - op op_iput_wide FALLBACK - op op_iput_object FALLBACK - op op_iput_boolean FALLBACK - op op_iput_byte FALLBACK - op op_iput_char FALLBACK - op op_iput_short FALLBACK - op op_sget FALLBACK - op op_sget_wide FALLBACK - op op_sget_object FALLBACK - op op_sget_boolean FALLBACK - op op_sget_byte FALLBACK - op op_sget_char FALLBACK - op op_sget_short FALLBACK - op op_sput FALLBACK - op op_sput_wide FALLBACK - op op_sput_object FALLBACK - op op_sput_boolean FALLBACK - op op_sput_byte FALLBACK - op op_sput_char FALLBACK - op op_sput_short FALLBACK - op op_invoke_virtual FALLBACK - op op_invoke_super FALLBACK - op op_invoke_direct FALLBACK - op op_invoke_static FALLBACK - op op_invoke_interface FALLBACK - op op_return_void_no_barrier FALLBACK - op op_invoke_virtual_range FALLBACK - op op_invoke_super_range FALLBACK - op op_invoke_direct_range FALLBACK - op op_invoke_static_range FALLBACK - op op_invoke_interface_range FALLBACK - op_unused_79 FALLBACK - op_unused_7a FALLBACK - op op_neg_int FALLBACK - op op_not_int FALLBACK - op op_neg_long FALLBACK - op op_not_long FALLBACK - op op_neg_float FALLBACK - op op_neg_double FALLBACK - op op_int_to_long FALLBACK - op op_int_to_float FALLBACK - op op_int_to_double FALLBACK - op op_long_to_int FALLBACK - op op_long_to_float FALLBACK - op op_long_to_double FALLBACK - op op_float_to_int FALLBACK - op op_float_to_long FALLBACK - op op_float_to_double FALLBACK - op op_double_to_int FALLBACK - op op_double_to_long FALLBACK - op op_double_to_float FALLBACK - op op_int_to_byte FALLBACK - op op_int_to_char FALLBACK - op op_int_to_short FALLBACK - op op_add_int FALLBACK - op op_sub_int FALLBACK - op op_mul_int FALLBACK - op op_div_int FALLBACK - op op_rem_int FALLBACK - op op_and_int FALLBACK - op op_or_int FALLBACK - op op_xor_int FALLBACK - op op_shl_int FALLBACK - op op_shr_int FALLBACK - op op_ushr_int FALLBACK - op op_add_long FALLBACK - op op_sub_long FALLBACK - op op_mul_long FALLBACK - op op_div_long FALLBACK - op op_rem_long FALLBACK - op op_and_long FALLBACK - op op_or_long FALLBACK - op op_xor_long FALLBACK - op op_shl_long FALLBACK - op op_shr_long FALLBACK - op op_ushr_long FALLBACK - op op_add_float FALLBACK - op op_sub_float FALLBACK - op op_mul_float FALLBACK - op op_div_float FALLBACK - op op_rem_float FALLBACK - op op_add_double FALLBACK - op op_sub_double FALLBACK - op op_mul_double FALLBACK - op op_div_double FALLBACK - op op_rem_double FALLBACK - op op_add_int_2addr FALLBACK - op op_sub_int_2addr FALLBACK - op op_mul_int_2addr FALLBACK - op op_div_int_2addr FALLBACK - op op_rem_int_2addr FALLBACK - op op_and_int_2addr FALLBACK - op op_or_int_2addr FALLBACK - op op_xor_int_2addr FALLBACK - op op_shl_int_2addr FALLBACK - op op_shr_int_2addr FALLBACK - op op_ushr_int_2addr FALLBACK - op op_add_long_2addr FALLBACK - op op_sub_long_2addr FALLBACK - op op_mul_long_2addr FALLBACK - op op_div_long_2addr FALLBACK - op op_rem_long_2addr FALLBACK - op op_and_long_2addr FALLBACK - op op_or_long_2addr FALLBACK - op op_xor_long_2addr FALLBACK - op op_shl_long_2addr FALLBACK - op op_shr_long_2addr FALLBACK - op op_ushr_long_2addr FALLBACK - op op_add_float_2addr FALLBACK - op op_sub_float_2addr FALLBACK - op op_mul_float_2addr FALLBACK - op op_div_float_2addr FALLBACK - op op_rem_float_2addr FALLBACK - op op_add_double_2addr FALLBACK - op op_sub_double_2addr FALLBACK - op op_mul_double_2addr FALLBACK - op op_div_double_2addr FALLBACK - op op_rem_double_2addr FALLBACK - op op_add_int_lit16 FALLBACK - op op_rsub_int FALLBACK - op op_mul_int_lit16 FALLBACK - op op_div_int_lit16 FALLBACK - op op_rem_int_lit16 FALLBACK - op op_and_int_lit16 FALLBACK - op op_or_int_lit16 FALLBACK - op op_xor_int_lit16 FALLBACK - op op_add_int_lit8 FALLBACK - op op_rsub_int_lit8 FALLBACK - op op_mul_int_lit8 FALLBACK - op op_div_int_lit8 FALLBACK - op op_rem_int_lit8 FALLBACK - op op_and_int_lit8 FALLBACK - op op_or_int_lit8 FALLBACK - op op_xor_int_lit8 FALLBACK - op op_shl_int_lit8 FALLBACK - op op_shr_int_lit8 FALLBACK - op op_ushr_int_lit8 FALLBACK - op op_iget_quick FALLBACK - op op_iget_wide_quick FALLBACK - op op_iget_object_quick FALLBACK - op op_iput_quick FALLBACK - op op_iput_wide_quick FALLBACK - op op_iput_object_quick FALLBACK - op op_invoke_virtual_quick FALLBACK - op op_invoke_virtual_range_quick FALLBACK - op op_iput_boolean_quick FALLBACK - op op_iput_byte_quick FALLBACK - op op_iput_char_quick FALLBACK - op op_iput_short_quick FALLBACK - op op_iget_boolean_quick FALLBACK - op op_iget_byte_quick FALLBACK - op op_iget_char_quick FALLBACK - op op_iget_short_quick FALLBACK - op_unused_f3 FALLBACK - op_unused_f4 FALLBACK - op_unused_f5 FALLBACK - op_unused_f6 FALLBACK - op_unused_f7 FALLBACK - op_unused_f8 FALLBACK - op_unused_f9 FALLBACK - op_unused_fa FALLBACK - op_unused_fb FALLBACK - op_unused_fc FALLBACK - op_unused_fd FALLBACK - op_unused_fe FALLBACK - op_unused_ff FALLBACK + # op op_nop FALLBACK + # op op_move FALLBACK + # op op_move_from16 FALLBACK + # op op_move_16 FALLBACK + # op op_move_wide FALLBACK + # op op_move_wide_from16 FALLBACK + # op op_move_wide_16 FALLBACK + # op op_move_object FALLBACK + # op op_move_object_from16 FALLBACK + # op op_move_object_16 FALLBACK + # op op_move_result FALLBACK + # op op_move_result_wide FALLBACK + # op op_move_result_object FALLBACK + # op op_move_exception FALLBACK + # op op_return_void FALLBACK + # op op_return FALLBACK + # op op_return_wide FALLBACK + # op op_return_object FALLBACK + # op op_const_4 FALLBACK + # op op_const_16 FALLBACK + # op op_const FALLBACK + # op op_const_high16 FALLBACK + # op op_const_wide_16 FALLBACK + # op op_const_wide_32 FALLBACK + # op op_const_wide FALLBACK + # op op_const_wide_high16 FALLBACK + # op op_const_string FALLBACK + # op op_const_string_jumbo FALLBACK + # op op_const_class FALLBACK + # op op_monitor_enter FALLBACK + # op op_monitor_exit FALLBACK + # op op_check_cast FALLBACK + # op op_instance_of FALLBACK + # op op_array_length FALLBACK + # op op_new_instance FALLBACK + # op op_new_array FALLBACK + # op op_filled_new_array FALLBACK + # op op_filled_new_array_range FALLBACK + # op op_fill_array_data FALLBACK + # op op_throw FALLBACK + # op op_goto FALLBACK + # op op_goto_16 FALLBACK + # op op_goto_32 FALLBACK + # op op_packed_switch FALLBACK + # op op_sparse_switch FALLBACK + # op op_cmpl_float FALLBACK + # op op_cmpg_float FALLBACK + # op op_cmpl_double FALLBACK + # op op_cmpg_double FALLBACK + # op op_cmp_long FALLBACK + # op op_if_eq FALLBACK + # op op_if_ne FALLBACK + # op op_if_lt FALLBACK + # op op_if_ge FALLBACK + # op op_if_gt FALLBACK + # op op_if_le FALLBACK + # op op_if_eqz FALLBACK + # op op_if_nez FALLBACK + # op op_if_ltz FALLBACK + # op op_if_gez FALLBACK + # op op_if_gtz FALLBACK + # op op_if_lez FALLBACK + # op op_unused_3e FALLBACK + # op op_unused_3f FALLBACK + # op op_unused_40 FALLBACK + # op op_unused_41 FALLBACK + # op op_unused_42 FALLBACK + # op op_unused_43 FALLBACK + # op op_aget FALLBACK + # op op_aget_wide FALLBACK + # op op_aget_object FALLBACK + # op op_aget_boolean FALLBACK + # op op_aget_byte FALLBACK + # op op_aget_char FALLBACK + # op op_aget_short FALLBACK + # op op_aput FALLBACK + # op op_aput_wide FALLBACK + # op op_aput_object FALLBACK + # op op_aput_boolean FALLBACK + # op op_aput_byte FALLBACK + # op op_aput_char FALLBACK + # op op_aput_short FALLBACK + # op op_iget FALLBACK + # op op_iget_wide FALLBACK + # op op_iget_object FALLBACK + # op op_iget_boolean FALLBACK + # op op_iget_byte FALLBACK + # op op_iget_char FALLBACK + # op op_iget_short FALLBACK + # op op_iput FALLBACK + # op op_iput_wide FALLBACK + # op op_iput_object FALLBACK + # op op_iput_boolean FALLBACK + # op op_iput_byte FALLBACK + # op op_iput_char FALLBACK + # op op_iput_short FALLBACK + # op op_sget FALLBACK + # op op_sget_wide FALLBACK + # op op_sget_object FALLBACK + # op op_sget_boolean FALLBACK + # op op_sget_byte FALLBACK + # op op_sget_char FALLBACK + # op op_sget_short FALLBACK + # op op_sput FALLBACK + # op op_sput_wide FALLBACK + # op op_sput_object FALLBACK + # op op_sput_boolean FALLBACK + # op op_sput_byte FALLBACK + # op op_sput_char FALLBACK + # op op_sput_short FALLBACK + # op op_invoke_virtual FALLBACK + # op op_invoke_super FALLBACK + # op op_invoke_direct FALLBACK + # op op_invoke_static FALLBACK + # op op_invoke_interface FALLBACK + # op op_return_void_no_barrier FALLBACK + # op op_invoke_virtual_range FALLBACK + # op op_invoke_super_range FALLBACK + # op op_invoke_direct_range FALLBACK + # op op_invoke_static_range FALLBACK + # op op_invoke_interface_range FALLBACK + # op op_unused_79 FALLBACK + # op op_unused_7a FALLBACK + # op op_neg_int FALLBACK + # op op_not_int FALLBACK + # op op_neg_long FALLBACK + # op op_not_long FALLBACK + # op op_neg_float FALLBACK + # op op_neg_double FALLBACK + # op op_int_to_long FALLBACK + # op op_int_to_float FALLBACK + # op op_int_to_double FALLBACK + # op op_long_to_int FALLBACK + # op op_long_to_float FALLBACK + # op op_long_to_double FALLBACK + # op op_float_to_int FALLBACK + # op op_float_to_long FALLBACK + # op op_float_to_double FALLBACK + # op op_double_to_int FALLBACK + # op op_double_to_long FALLBACK + # op op_double_to_float FALLBACK + # op op_int_to_byte FALLBACK + # op op_int_to_char FALLBACK + # op op_int_to_short FALLBACK + # op op_add_int FALLBACK + # op op_sub_int FALLBACK + # op op_mul_int FALLBACK + # op op_div_int FALLBACK + # op op_rem_int FALLBACK + # op op_and_int FALLBACK + # op op_or_int FALLBACK + # op op_xor_int FALLBACK + # op op_shl_int FALLBACK + # op op_shr_int FALLBACK + # op op_ushr_int FALLBACK + # op op_add_long FALLBACK + # op op_sub_long FALLBACK + # op op_mul_long FALLBACK + # op op_div_long FALLBACK + # op op_rem_long FALLBACK + # op op_and_long FALLBACK + # op op_or_long FALLBACK + # op op_xor_long FALLBACK + # op op_shl_long FALLBACK + # op op_shr_long FALLBACK + # op op_ushr_long FALLBACK + # op op_add_float FALLBACK + # op op_sub_float FALLBACK + # op op_mul_float FALLBACK + # op op_div_float FALLBACK + # op op_rem_float FALLBACK + # op op_add_double FALLBACK + # op op_sub_double FALLBACK + # op op_mul_double FALLBACK + # op op_div_double FALLBACK + # op op_rem_double FALLBACK + # op op_add_int_2addr FALLBACK + # op op_sub_int_2addr FALLBACK + # op op_mul_int_2addr FALLBACK + # op op_div_int_2addr FALLBACK + # op op_rem_int_2addr FALLBACK + # op op_and_int_2addr FALLBACK + # op op_or_int_2addr FALLBACK + # op op_xor_int_2addr FALLBACK + # op op_shl_int_2addr FALLBACK + # op op_shr_int_2addr FALLBACK + # op op_ushr_int_2addr FALLBACK + # op op_add_long_2addr FALLBACK + # op op_sub_long_2addr FALLBACK + # op op_mul_long_2addr FALLBACK + # op op_div_long_2addr FALLBACK + # op op_rem_long_2addr FALLBACK + # op op_and_long_2addr FALLBACK + # op op_or_long_2addr FALLBACK + # op op_xor_long_2addr FALLBACK + # op op_shl_long_2addr FALLBACK + # op op_shr_long_2addr FALLBACK + # op op_ushr_long_2addr FALLBACK + # op op_add_float_2addr FALLBACK + # op op_sub_float_2addr FALLBACK + # op op_mul_float_2addr FALLBACK + # op op_div_float_2addr FALLBACK + # op op_rem_float_2addr FALLBACK + # op op_add_double_2addr FALLBACK + # op op_sub_double_2addr FALLBACK + # op op_mul_double_2addr FALLBACK + # op op_div_double_2addr FALLBACK + # op op_rem_double_2addr FALLBACK + # op op_add_int_lit16 FALLBACK + # op op_rsub_int FALLBACK + # op op_mul_int_lit16 FALLBACK + # op op_div_int_lit16 FALLBACK + # op op_rem_int_lit16 FALLBACK + # op op_and_int_lit16 FALLBACK + # op op_or_int_lit16 FALLBACK + # op op_xor_int_lit16 FALLBACK + # op op_add_int_lit8 FALLBACK + # op op_rsub_int_lit8 FALLBACK + # op op_mul_int_lit8 FALLBACK + # op op_div_int_lit8 FALLBACK + # op op_rem_int_lit8 FALLBACK + # op op_and_int_lit8 FALLBACK + # op op_or_int_lit8 FALLBACK + # op op_xor_int_lit8 FALLBACK + # op op_shl_int_lit8 FALLBACK + # op op_shr_int_lit8 FALLBACK + # op op_ushr_int_lit8 FALLBACK + # op op_iget_quick FALLBACK + # op op_iget_wide_quick FALLBACK + # op op_iget_object_quick FALLBACK + # op op_iput_quick FALLBACK + # op op_iput_wide_quick FALLBACK + # op op_iput_object_quick FALLBACK + # op op_invoke_virtual_quick FALLBACK + # op op_invoke_virtual_range_quick FALLBACK + # op op_iput_boolean_quick FALLBACK + # op op_iput_byte_quick FALLBACK + # op op_iput_char_quick FALLBACK + # op op_iput_short_quick FALLBACK + # op op_iget_boolean_quick FALLBACK + # op op_iget_byte_quick FALLBACK + # op op_iget_char_quick FALLBACK + # op op_iget_short_quick FALLBACK + op op_invoke_lambda FALLBACK + # op op_unused_f4 FALLBACK + op op_capture_variable FALLBACK + op op_create_lambda FALLBACK + op op_liberate_variable FALLBACK + op op_box_lambda FALLBACK + op op_unbox_lambda FALLBACK + # op op_unused_fa FALLBACK + # op op_unused_fb FALLBACK + # op op_unused_fc FALLBACK + # op op_unused_fd FALLBACK + # op op_unused_fe FALLBACK + # op op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/config_x86_64 b/runtime/interpreter/mterp/config_x86_64 index a002dc2873c5218d6aed4055c990601baedac69e..1d7eb038bb28f585a63d1f9d2842ac797478ebd2 100644 --- a/runtime/interpreter/mterp/config_x86_64 +++ b/runtime/interpreter/mterp/config_x86_64 @@ -19,6 +19,10 @@ handler-style computed-goto handler-size 128 +function-type-format FUNCTION_TYPE(%s) +function-size-format SIZE(%s,%s) +global-name-format SYMBOL(%s) + # source for alternate entry stub asm-alt-stub x86_64/alt_stub.S @@ -36,262 +40,262 @@ op-start x86_64 # (override example:) op OP_SUB_FLOAT_2ADDR arm-vfp # (fallback example:) op OP_SUB_FLOAT_2ADDR FALLBACK - op op_nop FALLBACK - op op_move FALLBACK - op op_move_from16 FALLBACK - op op_move_16 FALLBACK - op op_move_wide FALLBACK - op op_move_wide_from16 FALLBACK - op op_move_wide_16 FALLBACK - op op_move_object FALLBACK - op op_move_object_from16 FALLBACK - op op_move_object_16 FALLBACK - op op_move_result FALLBACK - op op_move_result_wide FALLBACK - op op_move_result_object FALLBACK - op op_move_exception FALLBACK - op op_return_void FALLBACK - op op_return FALLBACK - op op_return_wide FALLBACK - op op_return_object FALLBACK - op op_const_4 FALLBACK - op op_const_16 FALLBACK - op op_const FALLBACK - op op_const_high16 FALLBACK - op op_const_wide_16 FALLBACK - op op_const_wide_32 FALLBACK - op op_const_wide FALLBACK - op op_const_wide_high16 FALLBACK - op op_const_string FALLBACK - op op_const_string_jumbo FALLBACK - op op_const_class FALLBACK - op op_monitor_enter FALLBACK - op op_monitor_exit FALLBACK - op op_check_cast FALLBACK - op op_instance_of FALLBACK - op op_array_length FALLBACK - op op_new_instance FALLBACK - op op_new_array FALLBACK - op op_filled_new_array FALLBACK - op op_filled_new_array_range FALLBACK - op op_fill_array_data FALLBACK - op op_throw FALLBACK - op op_goto FALLBACK - op op_goto_16 FALLBACK - op op_goto_32 FALLBACK - op op_packed_switch FALLBACK - op op_sparse_switch FALLBACK - op op_cmpl_float FALLBACK - op op_cmpg_float FALLBACK - op op_cmpl_double FALLBACK - op op_cmpg_double FALLBACK - op op_cmp_long FALLBACK - op op_if_eq FALLBACK - op op_if_ne FALLBACK - op op_if_lt FALLBACK - op op_if_ge FALLBACK - op op_if_gt FALLBACK - op op_if_le FALLBACK - op op_if_eqz FALLBACK - op op_if_nez FALLBACK - op op_if_ltz FALLBACK - op op_if_gez FALLBACK - op op_if_gtz FALLBACK - op op_if_lez FALLBACK - op_unused_3e FALLBACK - op_unused_3f FALLBACK - op_unused_40 FALLBACK - op_unused_41 FALLBACK - op_unused_42 FALLBACK - op_unused_43 FALLBACK - op op_aget FALLBACK - op op_aget_wide FALLBACK - op op_aget_object FALLBACK - op op_aget_boolean FALLBACK - op op_aget_byte FALLBACK - op op_aget_char FALLBACK - op op_aget_short FALLBACK - op op_aput FALLBACK - op op_aput_wide FALLBACK - op op_aput_object FALLBACK - op op_aput_boolean FALLBACK - op op_aput_byte FALLBACK - op op_aput_char FALLBACK - op op_aput_short FALLBACK - op op_iget FALLBACK - op op_iget_wide FALLBACK - op op_iget_object FALLBACK - op op_iget_boolean FALLBACK - op op_iget_byte FALLBACK - op op_iget_char FALLBACK - op op_iget_short FALLBACK - op op_iput FALLBACK - op op_iput_wide FALLBACK - op op_iput_object FALLBACK - op op_iput_boolean FALLBACK - op op_iput_byte FALLBACK - op op_iput_char FALLBACK - op op_iput_short FALLBACK - op op_sget FALLBACK - op op_sget_wide FALLBACK - op op_sget_object FALLBACK - op op_sget_boolean FALLBACK - op op_sget_byte FALLBACK - op op_sget_char FALLBACK - op op_sget_short FALLBACK - op op_sput FALLBACK - op op_sput_wide FALLBACK - op op_sput_object FALLBACK - op op_sput_boolean FALLBACK - op op_sput_byte FALLBACK - op op_sput_char FALLBACK - op op_sput_short FALLBACK - op op_invoke_virtual FALLBACK - op op_invoke_super FALLBACK - op op_invoke_direct FALLBACK - op op_invoke_static FALLBACK - op op_invoke_interface FALLBACK - op op_return_void_no_barrier FALLBACK - op op_invoke_virtual_range FALLBACK - op op_invoke_super_range FALLBACK - op op_invoke_direct_range FALLBACK - op op_invoke_static_range FALLBACK - op op_invoke_interface_range FALLBACK - op_unused_79 FALLBACK - op_unused_7a FALLBACK - op op_neg_int FALLBACK - op op_not_int FALLBACK - op op_neg_long FALLBACK - op op_not_long FALLBACK - op op_neg_float FALLBACK - op op_neg_double FALLBACK - op op_int_to_long FALLBACK - op op_int_to_float FALLBACK - op op_int_to_double FALLBACK - op op_long_to_int FALLBACK - op op_long_to_float FALLBACK - op op_long_to_double FALLBACK - op op_float_to_int FALLBACK - op op_float_to_long FALLBACK - op op_float_to_double FALLBACK - op op_double_to_int FALLBACK - op op_double_to_long FALLBACK - op op_double_to_float FALLBACK - op op_int_to_byte FALLBACK - op op_int_to_char FALLBACK - op op_int_to_short FALLBACK - op op_add_int FALLBACK - op op_sub_int FALLBACK - op op_mul_int FALLBACK - op op_div_int FALLBACK - op op_rem_int FALLBACK - op op_and_int FALLBACK - op op_or_int FALLBACK - op op_xor_int FALLBACK - op op_shl_int FALLBACK - op op_shr_int FALLBACK - op op_ushr_int FALLBACK - op op_add_long FALLBACK - op op_sub_long FALLBACK - op op_mul_long FALLBACK - op op_div_long FALLBACK - op op_rem_long FALLBACK - op op_and_long FALLBACK - op op_or_long FALLBACK - op op_xor_long FALLBACK - op op_shl_long FALLBACK - op op_shr_long FALLBACK - op op_ushr_long FALLBACK - op op_add_float FALLBACK - op op_sub_float FALLBACK - op op_mul_float FALLBACK - op op_div_float FALLBACK - op op_rem_float FALLBACK - op op_add_double FALLBACK - op op_sub_double FALLBACK - op op_mul_double FALLBACK - op op_div_double FALLBACK - op op_rem_double FALLBACK - op op_add_int_2addr FALLBACK - op op_sub_int_2addr FALLBACK - op op_mul_int_2addr FALLBACK - op op_div_int_2addr FALLBACK - op op_rem_int_2addr FALLBACK - op op_and_int_2addr FALLBACK - op op_or_int_2addr FALLBACK - op op_xor_int_2addr FALLBACK - op op_shl_int_2addr FALLBACK - op op_shr_int_2addr FALLBACK - op op_ushr_int_2addr FALLBACK - op op_add_long_2addr FALLBACK - op op_sub_long_2addr FALLBACK - op op_mul_long_2addr FALLBACK - op op_div_long_2addr FALLBACK - op op_rem_long_2addr FALLBACK - op op_and_long_2addr FALLBACK - op op_or_long_2addr FALLBACK - op op_xor_long_2addr FALLBACK - op op_shl_long_2addr FALLBACK - op op_shr_long_2addr FALLBACK - op op_ushr_long_2addr FALLBACK - op op_add_float_2addr FALLBACK - op op_sub_float_2addr FALLBACK - op op_mul_float_2addr FALLBACK - op op_div_float_2addr FALLBACK - op op_rem_float_2addr FALLBACK - op op_add_double_2addr FALLBACK - op op_sub_double_2addr FALLBACK - op op_mul_double_2addr FALLBACK - op op_div_double_2addr FALLBACK - op op_rem_double_2addr FALLBACK - op op_add_int_lit16 FALLBACK - op op_rsub_int FALLBACK - op op_mul_int_lit16 FALLBACK - op op_div_int_lit16 FALLBACK - op op_rem_int_lit16 FALLBACK - op op_and_int_lit16 FALLBACK - op op_or_int_lit16 FALLBACK - op op_xor_int_lit16 FALLBACK - op op_add_int_lit8 FALLBACK - op op_rsub_int_lit8 FALLBACK - op op_mul_int_lit8 FALLBACK - op op_div_int_lit8 FALLBACK - op op_rem_int_lit8 FALLBACK - op op_and_int_lit8 FALLBACK - op op_or_int_lit8 FALLBACK - op op_xor_int_lit8 FALLBACK - op op_shl_int_lit8 FALLBACK - op op_shr_int_lit8 FALLBACK - op op_ushr_int_lit8 FALLBACK - op op_iget_quick FALLBACK - op op_iget_wide_quick FALLBACK - op op_iget_object_quick FALLBACK - op op_iput_quick FALLBACK - op op_iput_wide_quick FALLBACK - op op_iput_object_quick FALLBACK - op op_invoke_virtual_quick FALLBACK - op op_invoke_virtual_range_quick FALLBACK - op op_iput_boolean_quick FALLBACK - op op_iput_byte_quick FALLBACK - op op_iput_char_quick FALLBACK - op op_iput_short_quick FALLBACK - op op_iget_boolean_quick FALLBACK - op op_iget_byte_quick FALLBACK - op op_iget_char_quick FALLBACK - op op_iget_short_quick FALLBACK - op_unused_f3 FALLBACK - op_unused_f4 FALLBACK - op_unused_f5 FALLBACK - op_unused_f6 FALLBACK - op_unused_f7 FALLBACK - op_unused_f8 FALLBACK - op_unused_f9 FALLBACK - op_unused_fa FALLBACK - op_unused_fb FALLBACK - op_unused_fc FALLBACK - op_unused_fd FALLBACK - op_unused_fe FALLBACK - op_unused_ff FALLBACK + # op op_nop FALLBACK + # op op_move FALLBACK + # op op_move_from16 FALLBACK + # op op_move_16 FALLBACK + # op op_move_wide FALLBACK + # op op_move_wide_from16 FALLBACK + # op op_move_wide_16 FALLBACK + # op op_move_object FALLBACK + # op op_move_object_from16 FALLBACK + # op op_move_object_16 FALLBACK + # op op_move_result FALLBACK + # op op_move_result_wide FALLBACK + # op op_move_result_object FALLBACK + # op op_move_exception FALLBACK + # op op_return_void FALLBACK + # op op_return FALLBACK + # op op_return_wide FALLBACK + # op op_return_object FALLBACK + # op op_const_4 FALLBACK + # op op_const_16 FALLBACK + # op op_const FALLBACK + # op op_const_high16 FALLBACK + # op op_const_wide_16 FALLBACK + # op op_const_wide_32 FALLBACK + # op op_const_wide FALLBACK + # op op_const_wide_high16 FALLBACK + # op op_const_string FALLBACK + # op op_const_string_jumbo FALLBACK + # op op_const_class FALLBACK + # op op_monitor_enter FALLBACK + # op op_monitor_exit FALLBACK + # op op_check_cast FALLBACK + # op op_instance_of FALLBACK + # op op_array_length FALLBACK + # op op_new_instance FALLBACK + # op op_new_array FALLBACK + # op op_filled_new_array FALLBACK + # op op_filled_new_array_range FALLBACK + # op op_fill_array_data FALLBACK + # op op_throw FALLBACK + # op op_goto FALLBACK + # op op_goto_16 FALLBACK + # op op_goto_32 FALLBACK + # op op_packed_switch FALLBACK + # op op_sparse_switch FALLBACK + # op op_cmpl_float FALLBACK + # op op_cmpg_float FALLBACK + # op op_cmpl_double FALLBACK + # op op_cmpg_double FALLBACK + # op op_cmp_long FALLBACK + # op op_if_eq FALLBACK + # op op_if_ne FALLBACK + # op op_if_lt FALLBACK + # op op_if_ge FALLBACK + # op op_if_gt FALLBACK + # op op_if_le FALLBACK + # op op_if_eqz FALLBACK + # op op_if_nez FALLBACK + # op op_if_ltz FALLBACK + # op op_if_gez FALLBACK + # op op_if_gtz FALLBACK + # op op_if_lez FALLBACK + # op op_unused_3e FALLBACK + # op op_unused_3f FALLBACK + # op op_unused_40 FALLBACK + # op op_unused_41 FALLBACK + # op op_unused_42 FALLBACK + # op op_unused_43 FALLBACK + # op op_aget FALLBACK + # op op_aget_wide FALLBACK + # op op_aget_object FALLBACK + # op op_aget_boolean FALLBACK + # op op_aget_byte FALLBACK + # op op_aget_char FALLBACK + # op op_aget_short FALLBACK + # op op_aput FALLBACK + # op op_aput_wide FALLBACK + # op op_aput_object FALLBACK + # op op_aput_boolean FALLBACK + # op op_aput_byte FALLBACK + # op op_aput_char FALLBACK + # op op_aput_short FALLBACK + # op op_iget FALLBACK + # op op_iget_wide FALLBACK + # op op_iget_object FALLBACK + # op op_iget_boolean FALLBACK + # op op_iget_byte FALLBACK + # op op_iget_char FALLBACK + # op op_iget_short FALLBACK + # op op_iput FALLBACK + # op op_iput_wide FALLBACK + # op op_iput_object FALLBACK + # op op_iput_boolean FALLBACK + # op op_iput_byte FALLBACK + # op op_iput_char FALLBACK + # op op_iput_short FALLBACK + # op op_sget FALLBACK + # op op_sget_wide FALLBACK + # op op_sget_object FALLBACK + # op op_sget_boolean FALLBACK + # op op_sget_byte FALLBACK + # op op_sget_char FALLBACK + # op op_sget_short FALLBACK + # op op_sput FALLBACK + # op op_sput_wide FALLBACK + # op op_sput_object FALLBACK + # op op_sput_boolean FALLBACK + # op op_sput_byte FALLBACK + # op op_sput_char FALLBACK + # op op_sput_short FALLBACK + # op op_invoke_virtual FALLBACK + # op op_invoke_super FALLBACK + # op op_invoke_direct FALLBACK + # op op_invoke_static FALLBACK + # op op_invoke_interface FALLBACK + # op op_return_void_no_barrier FALLBACK + # op op_invoke_virtual_range FALLBACK + # op op_invoke_super_range FALLBACK + # op op_invoke_direct_range FALLBACK + # op op_invoke_static_range FALLBACK + # op op_invoke_interface_range FALLBACK + # op op_unused_79 FALLBACK + # op op_unused_7a FALLBACK + # op op_neg_int FALLBACK + # op op_not_int FALLBACK + # op op_neg_long FALLBACK + # op op_not_long FALLBACK + # op op_neg_float FALLBACK + # op op_neg_double FALLBACK + # op op_int_to_long FALLBACK + # op op_int_to_float FALLBACK + # op op_int_to_double FALLBACK + # op op_long_to_int FALLBACK + # op op_long_to_float FALLBACK + # op op_long_to_double FALLBACK + # op op_float_to_int FALLBACK + # op op_float_to_long FALLBACK + # op op_float_to_double FALLBACK + # op op_double_to_int FALLBACK + # op op_double_to_long FALLBACK + # op op_double_to_float FALLBACK + # op op_int_to_byte FALLBACK + # op op_int_to_char FALLBACK + # op op_int_to_short FALLBACK + # op op_add_int FALLBACK + # op op_sub_int FALLBACK + # op op_mul_int FALLBACK + # op op_div_int FALLBACK + # op op_rem_int FALLBACK + # op op_and_int FALLBACK + # op op_or_int FALLBACK + # op op_xor_int FALLBACK + # op op_shl_int FALLBACK + # op op_shr_int FALLBACK + # op op_ushr_int FALLBACK + # op op_add_long FALLBACK + # op op_sub_long FALLBACK + # op op_mul_long FALLBACK + # op op_div_long FALLBACK + # op op_rem_long FALLBACK + # op op_and_long FALLBACK + # op op_or_long FALLBACK + # op op_xor_long FALLBACK + # op op_shl_long FALLBACK + # op op_shr_long FALLBACK + # op op_ushr_long FALLBACK + # op op_add_float FALLBACK + # op op_sub_float FALLBACK + # op op_mul_float FALLBACK + # op op_div_float FALLBACK + # op op_rem_float FALLBACK + # op op_add_double FALLBACK + # op op_sub_double FALLBACK + # op op_mul_double FALLBACK + # op op_div_double FALLBACK + # op op_rem_double FALLBACK + # op op_add_int_2addr FALLBACK + # op op_sub_int_2addr FALLBACK + # op op_mul_int_2addr FALLBACK + # op op_div_int_2addr FALLBACK + # op op_rem_int_2addr FALLBACK + # op op_and_int_2addr FALLBACK + # op op_or_int_2addr FALLBACK + # op op_xor_int_2addr FALLBACK + # op op_shl_int_2addr FALLBACK + # op op_shr_int_2addr FALLBACK + # op op_ushr_int_2addr FALLBACK + # op op_add_long_2addr FALLBACK + # op op_sub_long_2addr FALLBACK + # op op_mul_long_2addr FALLBACK + # op op_div_long_2addr FALLBACK + # op op_rem_long_2addr FALLBACK + # op op_and_long_2addr FALLBACK + # op op_or_long_2addr FALLBACK + # op op_xor_long_2addr FALLBACK + # op op_shl_long_2addr FALLBACK + # op op_shr_long_2addr FALLBACK + # op op_ushr_long_2addr FALLBACK + # op op_add_float_2addr FALLBACK + # op op_sub_float_2addr FALLBACK + # op op_mul_float_2addr FALLBACK + # op op_div_float_2addr FALLBACK + # op op_rem_float_2addr FALLBACK + # op op_add_double_2addr FALLBACK + # op op_sub_double_2addr FALLBACK + # op op_mul_double_2addr FALLBACK + # op op_div_double_2addr FALLBACK + # op op_rem_double_2addr FALLBACK + # op op_add_int_lit16 FALLBACK + # op op_rsub_int FALLBACK + # op op_mul_int_lit16 FALLBACK + # op op_div_int_lit16 FALLBACK + # op op_rem_int_lit16 FALLBACK + # op op_and_int_lit16 FALLBACK + # op op_or_int_lit16 FALLBACK + # op op_xor_int_lit16 FALLBACK + # op op_add_int_lit8 FALLBACK + # op op_rsub_int_lit8 FALLBACK + # op op_mul_int_lit8 FALLBACK + # op op_div_int_lit8 FALLBACK + # op op_rem_int_lit8 FALLBACK + # op op_and_int_lit8 FALLBACK + # op op_or_int_lit8 FALLBACK + # op op_xor_int_lit8 FALLBACK + # op op_shl_int_lit8 FALLBACK + # op op_shr_int_lit8 FALLBACK + # op op_ushr_int_lit8 FALLBACK + # op op_iget_quick FALLBACK + # op op_iget_wide_quick FALLBACK + # op op_iget_object_quick FALLBACK + # op op_iput_quick FALLBACK + # op op_iput_wide_quick FALLBACK + # op op_iput_object_quick FALLBACK + # op op_invoke_virtual_quick FALLBACK + # op op_invoke_virtual_range_quick FALLBACK + # op op_iput_boolean_quick FALLBACK + # op op_iput_byte_quick FALLBACK + # op op_iput_char_quick FALLBACK + # op op_iput_short_quick FALLBACK + # op op_iget_boolean_quick FALLBACK + # op op_iget_byte_quick FALLBACK + # op op_iget_char_quick FALLBACK + # op op_iget_short_quick FALLBACK + op op_invoke_lambda FALLBACK + # op op_unused_f4 FALLBACK + op op_capture_variable FALLBACK + op op_create_lambda FALLBACK + op op_liberate_variable FALLBACK + op op_box_lambda FALLBACK + op op_unbox_lambda FALLBACK + # op op_unused_fa FALLBACK + # op op_unused_fb FALLBACK + # op op_unused_fc FALLBACK + # op op_unused_fd FALLBACK + # op op_unused_fe FALLBACK + # op op_unused_ff FALLBACK op-end # common subroutines for asm diff --git a/runtime/interpreter/mterp/mips/alt_stub.S b/runtime/interpreter/mterp/mips/alt_stub.S new file mode 100644 index 0000000000000000000000000000000000000000..45980610b8caf96f9150db3cb11a5619fde6532d --- /dev/null +++ b/runtime/interpreter/mterp/mips/alt_stub.S @@ -0,0 +1,13 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (${opnum} * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) diff --git a/runtime/interpreter/mterp/mips/bincmp.S b/runtime/interpreter/mterp/mips/bincmp.S new file mode 100644 index 0000000000000000000000000000000000000000..70057f6792417d8101bff3a9bc5345775becf875 --- /dev/null +++ b/runtime/interpreter/mterp/mips/bincmp.S @@ -0,0 +1,37 @@ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + b${revcmp} a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_${opcode}_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + +%break + +.L_${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/binop.S b/runtime/interpreter/mterp/mips/binop.S new file mode 100644 index 0000000000000000000000000000000000000000..66627e27191114a5130da0afc06ceb56d8e5b506 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binop.S @@ -0,0 +1,33 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if $chkzero + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 11-14 instructions */ diff --git a/runtime/interpreter/mterp/mips/binop2addr.S b/runtime/interpreter/mterp/mips/binop2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..548cbcb08843a44f88f0ae357ff4429496c265aa --- /dev/null +++ b/runtime/interpreter/mterp/mips/binop2addr.S @@ -0,0 +1,29 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if $chkzero + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 10-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopLit16.S b/runtime/interpreter/mterp/mips/binopLit16.S new file mode 100644 index 0000000000000000000000000000000000000000..fc0c9ff630563d430f79eb9939d87bae1912e454 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopLit16.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if $chkzero + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 10-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopLit8.S b/runtime/interpreter/mterp/mips/binopLit8.S new file mode 100644 index 0000000000000000000000000000000000000000..a591408ef57c2ef45661bc78902e8f340e8d2908 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopLit8.S @@ -0,0 +1,31 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if $chkzero + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO($result, rOBJ, t0) # vAA <- $result + /* 10-12 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopWide.S b/runtime/interpreter/mterp/mips/binopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..608525bccde0902870f2cb2942490c34e721fed5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopWide.S @@ -0,0 +1,35 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"} + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64($arg0, $arg1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64($arg2, $arg3, t1) # a2/a3 <- vCC/vCC+1 + .if $chkzero + or t0, $arg2, $arg3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + $preinstr # optional op + $instr # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO($result0, $result1, rOBJ, t0) # vAA/vAA+1 <- $result0/$result1 + /* 14-17 instructions */ diff --git a/runtime/interpreter/mterp/mips/binopWide2addr.S b/runtime/interpreter/mterp/mips/binopWide2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..cc92149039307661d709ed402cc461f3402cba24 --- /dev/null +++ b/runtime/interpreter/mterp/mips/binopWide2addr.S @@ -0,0 +1,33 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1", "chkzero":"0", "arg0":"a0", "arg1":"a1", "arg2":"a2", "arg3":"a3"} + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64($arg2, $arg3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64($arg0, $arg1, t0) # a0/a1 <- vAA/vAA+1 + .if $chkzero + or t0, $arg2, $arg3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + $preinstr # optional op + $instr # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64($result0, $result1, rOBJ) # vAA/vAA+1 <- $result0/$result1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S new file mode 100644 index 0000000000000000000000000000000000000000..5771a4f4022a5a007dac29e5709fb392f4b16894 --- /dev/null +++ b/runtime/interpreter/mterp/mips/entry.S @@ -0,0 +1,67 @@ +/* + * 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. + */ +/* + * Interpreter entry point. + */ + + .text + .align 2 + .global ExecuteMterpImpl + .ent ExecuteMterpImpl + .frame sp, STACK_SIZE, ra +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ + +ExecuteMterpImpl: + .set noreorder + .cpload t9 + .set reorder +/* Save to the stack. Frame size = STACK_SIZE */ + STACK_STORE_FULL() +/* This directive will make sure all subsequent jal restore gp at a known offset */ + .cprestore STACK_OFFSET_GP + + /* Remember the return register */ + sw a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sw a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + lw a0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to vregs. + EAS2(rREFS, rFP, a0) # point to reference array in shadow frame + lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc + addu rPC, a1, CODEITEM_INSNS_OFFSET # Point to base of insns[] + EAS1(rPC, rPC, a0) # Create direct pointer to 1st dex opcode + + EXPORT_PC() + + /* Starting ibase */ + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + + /* start executing the instruction at rPC */ + FETCH_INST() # load rINST from rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/mips/fallback.S b/runtime/interpreter/mterp/mips/fallback.S new file mode 100644 index 0000000000000000000000000000000000000000..82cbc6348064dc1fd6484f90d00d85c68406ce49 --- /dev/null +++ b/runtime/interpreter/mterp/mips/fallback.S @@ -0,0 +1,2 @@ +/* Transfer stub to alternate interpreter */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips/fbinop.S b/runtime/interpreter/mterp/mips/fbinop.S new file mode 100644 index 0000000000000000000000000000000000000000..d0d39aeffe7ad23eb26a6604f8d79e0656b739e9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinop.S @@ -0,0 +1,19 @@ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $instr # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/fbinop2addr.S b/runtime/interpreter/mterp/mips/fbinop2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ccb67b1b906cb039431206975d3d7365c5fcc4c7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinop2addr.S @@ -0,0 +1,19 @@ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + $instr + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/fbinopWide.S b/runtime/interpreter/mterp/mips/fbinopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..3be9325f7cba1dd0348cdabf36ba6a5f391259d7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinopWide.S @@ -0,0 +1,28 @@ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $instr + SET_VREG64_F(fv0, fv0f, rOBJ) + b .L${opcode}_finish +%break + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/fbinopWide2addr.S b/runtime/interpreter/mterp/mips/fbinopWide2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..8541f119dd3bbda1b7c3f7321aecf63b91d8847a --- /dev/null +++ b/runtime/interpreter/mterp/mips/fbinopWide2addr.S @@ -0,0 +1,21 @@ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/footer.S b/runtime/interpreter/mterp/mips/footer.S new file mode 100644 index 0000000000000000000000000000000000000000..083dc152059163aa0f17d96d31d31d304ea11b7f --- /dev/null +++ b/runtime/interpreter/mterp/mips/footer.S @@ -0,0 +1,179 @@ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * 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. + */ +common_errDivideByZero: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogDivideByZeroException) +#endif + b MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogArrayIndexException) +#endif + b MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNegativeArraySizeException) +#endif + b MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNoSuchMethodException) +#endif + b MterpCommonFallback + +common_errNullObject: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNullObjectException) +#endif + b MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogExceptionThrownException) +#endif + b MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + lw a2, THREAD_FLAGS_OFFSET(rSELF) + JAL(MterpLogSuspendFallback) +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + lw a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqz a0, MterpFallback # If exception, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpHandleException) # (self, shadow_frame) + beqz v0, MterpExceptionReturn # no local catch, back to caller. + lw a0, OFF_FP_CODE_ITEM(rFP) + lw a1, OFF_FP_DEX_PC(rFP) + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + addu rPC, a0, CODEITEM_INSNS_OFFSET + sll a1, a1, 1 + addu rPC, rPC, a1 # generate new dex_pc_ptr + /* Do we need to switch interpreters? */ + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + /* resume execution at catch block */ + EXPORT_PC() + FETCH_INST() + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh rIBASE + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, 1f + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +1: + EXPORT_PC() + move a0, rSELF + JAL(MterpSuspendCheck) # (self) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpLogOSR) +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogFallback) +#endif +MterpCommonFallback: + move v0, zero # signal retry with reference interpreter. + b MterpDone +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and LR. Here we restore SP, restore the registers, and then restore + * LR to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +MterpReturn: + lw a2, OFF_FP_RESULT_REGISTER(rFP) + sw v0, 0(a2) + sw v1, 4(a2) + li v0, 1 # signal return to caller. +MterpDone: +/* Restore from the stack and return. Frame size = STACK_SIZE */ + STACK_LOAD_FULL() + jalr zero, ra + + .end ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/mips/funop.S b/runtime/interpreter/mterp/mips/funop.S new file mode 100644 index 0000000000000000000000000000000000000000..bfb93469f652366c57a6fc5df34dbd437141d5c2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/funop.S @@ -0,0 +1,18 @@ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: int-to-float, float-to-int + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t0 <- A+ + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + +.L${opcode}_set_vreg_f: + SET_VREG_F(fv0, rOBJ) + GET_INST_OPCODE(t1) # extract opcode from rINST + GOTO_OPCODE(t1) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/funopWide.S b/runtime/interpreter/mterp/mips/funopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..3d4cf2216e7d00188498d5483821a6f49e7c27da --- /dev/null +++ b/runtime/interpreter/mterp/mips/funopWide.S @@ -0,0 +1,22 @@ +%default {"preinstr":"", "ld_arg":"LOAD64_F(fa0, fa0f, a3)", "st_result":"SET_VREG64_F(fv0, fv0f, rOBJ)"} + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be a MIPS instruction or a function call. + * + * long-to-double, double-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + $ld_arg + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # a0/a1 <- op, a2-a3 changed + +.L${opcode}_set_vreg: + $st_result # vAA <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/funopWider.S b/runtime/interpreter/mterp/mips/funopWider.S new file mode 100644 index 0000000000000000000000000000000000000000..efb85f3ca99b1eb4a601e5b827ccb3ec3cb06e79 --- /dev/null +++ b/runtime/interpreter/mterp/mips/funopWider.S @@ -0,0 +1,19 @@ +%default {"st_result":"SET_VREG64_F(fv0, fv0f, rOBJ)"} + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + +.L${opcode}_set_vreg: + $st_result # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S new file mode 100644 index 0000000000000000000000000000000000000000..37ab21de5ce67b251044972d2873e7cdec2af38b --- /dev/null +++ b/runtime/interpreter/mterp/mips/header.S @@ -0,0 +1,484 @@ +/* + * 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. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +#include "asm_support.h" + +#if (__mips==32) && (__mips_isa_rev>=2) +#define MIPS32REVGE2 /* mips32r2 and greater */ +#if (__mips==32) && (__mips_isa_rev>=5) +#define FPU64 /* 64 bit FPU */ +#if (__mips==32) && (__mips_isa_rev>=6) +#define MIPS32REVGE6 /* mips32r6 and greater */ +#endif +#endif +#endif + +/* MIPS definitions and declarations + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rIBASE interpreted instruction base pointer, used for computed goto + s4 rINST first 16-bit code unit of current instruction + s6 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rIBASE s3 +#define rINST s4 +#define rOBJ s5 +#define rREFS s6 +#define rTEMP s7 + +#define rARG0 a0 +#define rARG1 a1 +#define rARG2 a2 +#define rARG3 a3 +#define rRESULT0 v0 +#define rRESULT1 v1 + +/* GP register definitions */ +#define zero $$0 /* always zero */ +#define AT $$at /* assembler temp */ +#define v0 $$2 /* return value */ +#define v1 $$3 +#define a0 $$4 /* argument registers */ +#define a1 $$5 +#define a2 $$6 +#define a3 $$7 +#define t0 $$8 /* temp registers (not saved across subroutine calls) */ +#define t1 $$9 +#define t2 $$10 +#define t3 $$11 +#define t4 $$12 +#define t5 $$13 +#define t6 $$14 +#define t7 $$15 +#define ta0 $$12 /* alias */ +#define ta1 $$13 +#define ta2 $$14 +#define ta3 $$15 +#define s0 $$16 /* saved across subroutine calls (callee saved) */ +#define s1 $$17 +#define s2 $$18 +#define s3 $$19 +#define s4 $$20 +#define s5 $$21 +#define s6 $$22 +#define s7 $$23 +#define t8 $$24 /* two more temp registers */ +#define t9 $$25 +#define k0 $$26 /* kernel temporary */ +#define k1 $$27 +#define gp $$28 /* global pointer */ +#define sp $$29 /* stack pointer */ +#define s8 $$30 /* one more callee saved */ +#define ra $$31 /* return address */ + +/* FP register definitions */ +#define fv0 $$f0 +#define fv0f $$f1 +#define fv1 $$f2 +#define fv1f $$f3 +#define fa0 $$f12 +#define fa0f $$f13 +#define fa1 $$f14 +#define fa1f $$f15 +#define ft0 $$f4 +#define ft0f $$f5 +#define ft1 $$f6 +#define ft1f $$f7 +#define ft2 $$f8 +#define ft2f $$f9 +#define ft3 $$f10 +#define ft3f $$f11 +#define ft4 $$f16 +#define ft4f $$f17 +#define ft5 $$f18 +#define ft5f $$f19 +#define fs0 $$f20 +#define fs0f $$f21 +#define fs1 $$f22 +#define fs1f $$f23 +#define fs2 $$f24 +#define fs2f $$f25 +#define fs3 $$f26 +#define fs3f $$f27 +#define fs4 $$f28 +#define fs4f $$f29 +#define fs5 $$f30 +#define fs5f $$f31 + +#ifndef MIPS32REVGE6 +#define fcc0 $$fcc0 +#define fcc1 $$fcc1 +#endif + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#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 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +#define EXPORT_PC() \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) + +#define EXPORT_DEX_PC(tmp) \ + lw tmp, OFF_FP_CODE_ITEM(rFP) \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) \ + addu tmp, CODEITEM_INSNS_OFFSET \ + subu tmp, rPC, tmp \ + sra tmp, tmp, 1 \ + sw tmp, OFF_FP_DEX_PC(rFP) + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +#define FETCH_INST() lhu rINST, (rPC) + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. "_count" is in 16-bit code units. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC().) + */ +#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \ + addu rPC, rPC, ((_count) * 2) + +/* + * The operation performed here is similar to FETCH_ADVANCE_INST, except the + * src and dest registers are parameterized (not hard-wired to rPC and rINST). + */ +#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \ + lhu _dreg, ((_count)*2)(_sreg) ; \ + addu _sreg, _sreg, (_count)*2 + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +#define PREFETCH_INST(_count) lhu rINST, ((_count)*2)(rPC) + +/* Advance rPC by some number of code units. */ +#define ADVANCE(_count) addu rPC, rPC, ((_count) * 2) + +/* + * Fetch the next instruction from an offset specified by rd. Updates + * rPC to point to the next instruction. "rd" must specify the distance + * in bytes, *not* 16-bit code units, and may be a signed value. + */ +#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \ + lhu rINST, (rPC) + +/* + * Fetch a half-word code unit from an offset past the current PC. The + * "_count" value is in 16-bit code units. Does not advance rPC. + * + * The "_S" variant works the same but treats the value as signed. + */ +#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC) +#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC) + +/* + * Fetch one byte from an offset past the current PC. Pass in the same + * "_count" as you would for FETCH, and an additional 0/1 indicating which + * byte of the halfword you want (lo/hi). + */ +#define FETCH_B(rd, _count, _byte) lbu rd, ((_count) * 2 + _byte)(rPC) + +/* + * Put the instruction's opcode field into the specified register. + */ +#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF + +/* + * Put the prefetched instruction's opcode field into the specified register. + */ +#define GET_PREFETCHED_OPCODE(dreg, sreg) andi dreg, sreg, 255 + +/* + * Begin executing the opcode in rd. + */ +#define GOTO_OPCODE(rd) sll rd, rd, ${handler_size_bits}; \ + addu rd, rIBASE, rd; \ + jalr zero, rd + +#define GOTO_OPCODE_BASE(_base, rd) sll rd, rd, ${handler_size_bits}; \ + addu rd, _base, rd; \ + jalr zero, rd + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix) + +#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \ + .set noat; l.s rd, (AT); .set at + +#define SET_VREG(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define SET_VREG64(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) + +#ifdef FPU64 +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rREFS, AT; \ + sw zero, 0(t8); \ + sw zero, 4(t8); \ + addu t8, rFP, AT; \ + mfhc1 AT, rlo; \ + sw AT, 4(t8); \ + .set at; \ + s.s rlo, 0(t8) +#else +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rlo, 0(t8); \ + s.s rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) +#endif + +#define SET_VREG_OBJECT(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw rd, 0(t8) + +/* Combination of the SET_VREG and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \ + sll dst, dst, ${handler_size_bits}; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + jalr zero, dst; \ + sw zero, 0(t8); \ + .set reorder + +/* Combination of the SET_VREG64 and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG64_GOTO(rlo, rhi, rix, dst) .set noreorder; \ + sll dst, dst, ${handler_size_bits}; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + jalr zero, dst; \ + sw zero, 4(t8); \ + .set reorder + +#define SET_VREG_F(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define GET_OPA(rd) srl rd, rINST, 8 +#ifdef MIPS32REVGE2 +#define GET_OPA4(rd) ext rd, rINST, 8, 4 +#else +#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf +#endif +#define GET_OPB(rd) srl rd, rINST, 12 + +/* + * Form an Effective Address rd = rbase + roff<>n; + * Uses reg AT + */ +#define ESRN(rd, rbase, roff, rshift) .set noat; \ + srl AT, roff, rshift; \ + addu rd, rbase, AT; \ + .set at + +#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; lw rd, 0(AT); .set at + +#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; sw rd, 0(AT); .set at + +#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase) +#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase) + +#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \ + sw rhi, (off+4)(rbase) +#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \ + lw rhi, (off+4)(rbase) + +#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0) +#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0) + +#ifdef FPU64 +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + .set noat; \ + mfhc1 AT, rlo; \ + sw AT, (off+4)(rbase); \ + .set at +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + .set noat; \ + lw AT, (off+4)(rbase); \ + mthc1 AT, rlo; \ + .set at +#else +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + s.s rhi, (off+4)(rbase) +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + l.s rhi, (off+4)(rbase) +#endif + +#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0) +#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0) + + +#define LOAD_base_offMirrorArray_length(rd, rbase) LOAD_RB_OFF(rd, rbase, MIRROR_ARRAY_LENGTH_OFFSET) + +#define STACK_STORE(rd, off) sw rd, off(sp) +#define STACK_LOAD(rd, off) lw rd, off(sp) +#define CREATE_STACK(n) subu sp, sp, n +#define DELETE_STACK(n) addu sp, sp, n + +#define LOAD_ADDR(dest, addr) la dest, addr +#define LOAD_IMM(dest, imm) li dest, imm +#define MOVE_REG(dest, src) move dest, src +#define STACK_SIZE 128 + +#define STACK_OFFSET_ARG04 16 +#define STACK_OFFSET_ARG05 20 +#define STACK_OFFSET_ARG06 24 +#define STACK_OFFSET_ARG07 28 +#define STACK_OFFSET_GP 84 + +#define JAL(n) jal n +#define BAL(n) bal n + +/* + * FP register usage restrictions: + * 1) We don't use the callee save FP registers so we don't have to save them. + * 2) We don't use the odd FP registers so we can share code with mips32r6. + */ +#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \ + STACK_STORE(ra, 124); \ + STACK_STORE(s8, 120); \ + STACK_STORE(s0, 116); \ + STACK_STORE(s1, 112); \ + STACK_STORE(s2, 108); \ + STACK_STORE(s3, 104); \ + STACK_STORE(s4, 100); \ + STACK_STORE(s5, 96); \ + STACK_STORE(s6, 92); \ + STACK_STORE(s7, 88); + +#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \ + STACK_LOAD(s7, 88); \ + STACK_LOAD(s6, 92); \ + STACK_LOAD(s5, 96); \ + STACK_LOAD(s4, 100); \ + STACK_LOAD(s3, 104); \ + STACK_LOAD(s2, 108); \ + STACK_LOAD(s1, 112); \ + STACK_LOAD(s0, 116); \ + STACK_LOAD(s8, 120); \ + STACK_LOAD(ra, 124); \ + DELETE_STACK(STACK_SIZE) diff --git a/runtime/interpreter/mterp/mips/invoke.S b/runtime/interpreter/mterp/mips/invoke.S new file mode 100644 index 0000000000000000000000000000000000000000..bcd3a57a716883e7bf05eed7822507b0e8acc577 --- /dev/null +++ b/runtime/interpreter/mterp/mips/invoke.S @@ -0,0 +1,19 @@ +%default { "helper":"UndefinedInvokeHandler" } + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern $helper + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL($helper) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) diff --git a/runtime/interpreter/mterp/mips/op_add_double.S b/runtime/interpreter/mterp/mips/op_add_double.S new file mode 100644 index 0000000000000000000000000000000000000000..12ef0cf3cd7f8539b4860d7c6adbd9a5eeddb20d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"add.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_double_2addr.S b/runtime/interpreter/mterp/mips/op_add_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..c57add572c980efd79a9d48e94edfecd33bc72f9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"add.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_float.S b/runtime/interpreter/mterp/mips/op_add_float.S new file mode 100644 index 0000000000000000000000000000000000000000..6a46cf0ab9b4d03bfe2508bc5cf5ce364a65fb3e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"add.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_float_2addr.S b/runtime/interpreter/mterp/mips/op_add_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..6ab5cc17309f867594133fbcd7ea5c398b29f0ea --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"add.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int.S b/runtime/interpreter/mterp/mips/op_add_int.S new file mode 100644 index 0000000000000000000000000000000000000000..53a0cb128fd1dfd880580c0a6a86524b75224939 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int_2addr.S b/runtime/interpreter/mterp/mips/op_add_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ddd92145c343513f7e5e04b9e2d43fe4bf9f422e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int_lit16.S b/runtime/interpreter/mterp/mips/op_add_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..05535c15dc451e058a9934100a781ac59f0f61fc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_int_lit8.S b/runtime/interpreter/mterp/mips/op_add_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..fd021b31a964d15a5dfa21e417bf1b9112c40df9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_add_long.S b/runtime/interpreter/mterp/mips/op_add_long.S new file mode 100644 index 0000000000000000000000000000000000000000..faacc6a3cc7ce3f4c5abb76e1918f6da6d74a03f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_long.S @@ -0,0 +1,9 @@ +/* + * The compiler generates the following sequence for + * [v1 v0] = [a1 a0] + [a3 a2]; + * addu v0,a2,a0 + * addu a1,a3,a1 + * sltu v1,v0,a2 + * addu v1,v1,a1 + */ +%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" } diff --git a/runtime/interpreter/mterp/mips/op_add_long_2addr.S b/runtime/interpreter/mterp/mips/op_add_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..bf827c10d4e3202d59f7fcf1f8a28079e5d3cd8a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_add_long_2addr.S @@ -0,0 +1,4 @@ +/* + * See op_add_long.S for details + */ +%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"addu v0, a2, a0", "instr":"addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1" } diff --git a/runtime/interpreter/mterp/mips/op_aget.S b/runtime/interpreter/mterp/mips/op_aget.S new file mode 100644 index 0000000000000000000000000000000000000000..8aa8992472f5f4cac6b8b57dd9f9cc346c03d22a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget.S @@ -0,0 +1,32 @@ +%default { "load":"lw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if $shift + EASN(a0, a0, a1, $shift) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + $load a2, $data_offset(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 diff --git a/runtime/interpreter/mterp/mips/op_aget_boolean.S b/runtime/interpreter/mterp/mips/op_aget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..59f7f82a84e5490a51b555a6110981f0c0fabcfd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_boolean.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lbu", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_byte.S b/runtime/interpreter/mterp/mips/op_aget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..11038fa7e10296c54347f87288a3469a0705fb35 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_byte.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_char.S b/runtime/interpreter/mterp/mips/op_aget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..96f2ab65dc533867267f68db1c739074dcdb9fef --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_char.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lhu", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_object.S b/runtime/interpreter/mterp/mips/op_aget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..e3ab9d8462042a1dd05cebaa5aceed79bf655da2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_object.S @@ -0,0 +1,20 @@ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + JAL(artAGetObjectFromMterp) # v0 <- GetObj(array, index) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + SET_VREG_OBJECT(v0, rOBJ) # vAA <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_aget_short.S b/runtime/interpreter/mterp/mips/op_aget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..cd7f7bf62f968b8c202042660bbb629de270ff9f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_short.S @@ -0,0 +1 @@ +%include "mips/op_aget.S" { "load":"lh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aget_wide.S b/runtime/interpreter/mterp/mips/op_aget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..08822f56c32a141e6fd348a6fef1e67838873a96 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aget_wide.S @@ -0,0 +1,22 @@ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + * Arrays of long/double are 64-bit aligned. + */ + /* aget-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a2, a3, rOBJ, t0) # vAA/vAA+1 <- a2/a3 diff --git a/runtime/interpreter/mterp/mips/op_and_int.S b/runtime/interpreter/mterp/mips/op_and_int.S new file mode 100644 index 0000000000000000000000000000000000000000..98fe4af7d22032dfc858d3f6a1e112656edf6dfc --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_int_2addr.S b/runtime/interpreter/mterp/mips/op_and_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..7f90ed45350b0905b4d4f33edd87d73fb87b1fb2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_int_lit16.S b/runtime/interpreter/mterp/mips/op_and_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..e46f23ba2e487f4f8e61e28f4eea807e70e8e342 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_int_lit8.S b/runtime/interpreter/mterp/mips/op_and_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..3332883dc229507d59f1460c600e2885bb92c636 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_and_long.S b/runtime/interpreter/mterp/mips/op_and_long.S new file mode 100644 index 0000000000000000000000000000000000000000..a98a6dfbd8f8d76772300397961783dff895c2b1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_and_long_2addr.S b/runtime/interpreter/mterp/mips/op_and_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..350c044f986c13fa3011e78cae57d7a5c9ec032e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"preinstr":"and a0, a0, a2", "instr":"and a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_aput.S b/runtime/interpreter/mterp/mips/op_aput.S new file mode 100644 index 0000000000000000000000000000000000000000..53d6ae02ff525b3365d2cbac483af8d201195f35 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput.S @@ -0,0 +1,30 @@ +%default { "store":"sw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if $shift + EASN(a0, a0, a1, $shift) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + $store a2, $data_offset(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_aput_boolean.S b/runtime/interpreter/mterp/mips/op_aput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..9cae5efbaf4f0bc0b3fcd79352b3437edeebba48 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_boolean.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_byte.S b/runtime/interpreter/mterp/mips/op_aput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..3bbd12cec110f9346205256b2d40e2d1f7caf166 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_byte.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_char.S b/runtime/interpreter/mterp/mips/op_aput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..ae697173d79aaab670a315437c59bef3f8c8a10b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_char.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_object.S b/runtime/interpreter/mterp/mips/op_aput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..55b13b1449ae7b5ae4b64fc488d56f5d4a9def7d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_object.S @@ -0,0 +1,14 @@ + /* + * Store an object into an array. vBB[vCC] <- vAA. + * + */ + /* op vAA, vBB, vCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpAputObject) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_aput_short.S b/runtime/interpreter/mterp/mips/op_aput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..9586259a240aa68d263d8464594e97acbeaeea18 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_short.S @@ -0,0 +1 @@ +%include "mips/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips/op_aput_wide.S b/runtime/interpreter/mterp/mips/op_aput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..ef99261a5c548df69ffbdef9391e99986bffc754 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_aput_wide.S @@ -0,0 +1,25 @@ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + * Arrays of long/double are 64-bit aligned, so it's okay to use STRD. + */ + /* aput-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t0) # t0 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + EAS2(rOBJ, rFP, t0) # rOBJ <- &fp[AA] + # compare unsigned index, length + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64(a2, a3, rOBJ) # a2/a3 <- vAA/vAA+1 + GET_INST_OPCODE(t0) # extract opcode from rINST + STORE64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) # a2/a3 <- vBB[vCC] + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_array_length.S b/runtime/interpreter/mterp/mips/op_array_length.S new file mode 100644 index 0000000000000000000000000000000000000000..2b4a86f1fdbc5460e564536cf3fb8dc708e5076f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_array_length.S @@ -0,0 +1,12 @@ + /* + * Return the length of an array. + */ + GET_OPB(a1) # a1 <- B + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a0, a1) # a0 <- vB (object ref) + # is object null? + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- array length + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a3, a2, t0) # vA <- length diff --git a/runtime/interpreter/mterp/mips/op_check_cast.S b/runtime/interpreter/mterp/mips/op_check_cast.S new file mode 100644 index 0000000000000000000000000000000000000000..9a6cefad6d0c87ad11f9628a579870918f42ec0f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_check_cast.S @@ -0,0 +1,16 @@ + /* + * Check to see if a cast from one class to another is allowed. + */ + # check-cast vAA, class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + JAL(MterpCheckCast) # v0 <- CheckCast(index, &obj, method, self) + PREFETCH_INST(2) + bnez v0, MterpPossibleException + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_cmp_long.S b/runtime/interpreter/mterp/mips/op_cmp_long.S new file mode 100644 index 0000000000000000000000000000000000000000..44806c3d067006ae82682f759be45f0ecfdede61 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmp_long.S @@ -0,0 +1,34 @@ + /* + * Compare two 64-bit values + * x = y return 0 + * x < y return -1 + * x > y return 1 + * + * I think I can improve on the ARM code by the following observation + * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 + * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 + * subu v0, t0, t1 # v0= -1:1:0 for [ < > = ] + */ + /* cmp-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(a3, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, a3) # a2/a3 <- vCC/vCC+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + slt t0, a1, a3 # compare hi + sgt t1, a1, a3 + subu v0, t1, t0 # v0 <- (-1, 1, 0) + bnez v0, .L${opcode}_finish + # at this point x.hi==y.hi + sltu t0, a0, a2 # compare lo + sgtu t1, a0, a2 + subu v0, t1, t0 # v0 <- (-1, 1, 0) for [< > =] + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vAA <- v0 diff --git a/runtime/interpreter/mterp/mips/op_cmpg_double.S b/runtime/interpreter/mterp/mips/op_cmpg_double.S new file mode 100644 index 0000000000000000000000000000000000000000..e7965a7d8ac9888364de3d5ec4ed5720f94f7373 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpg_double.S @@ -0,0 +1 @@ +%include "mips/op_cmpl_double.S" { "naninst":"li rTEMP, 1" } diff --git a/runtime/interpreter/mterp/mips/op_cmpg_float.S b/runtime/interpreter/mterp/mips/op_cmpg_float.S new file mode 100644 index 0000000000000000000000000000000000000000..53519a6c97f4009fc0587d966116a94b052323ea --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpg_float.S @@ -0,0 +1 @@ +%include "mips/op_cmpl_float.S" { "naninst":"li rTEMP, 1" } diff --git a/runtime/interpreter/mterp/mips/op_cmpl_double.S b/runtime/interpreter/mterp/mips/op_cmpl_double.S new file mode 100644 index 0000000000000000000000000000000000000000..5a47fd7921d64c6dfdd240a287038641f0f64b82 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpl_double.S @@ -0,0 +1,54 @@ +%default { "naninst":"li rTEMP, -1" } + /* + * Compare two floating-point values. Puts 0(==), 1(>), or -1(<) + * into the destination register (rTEMP) based on the comparison results. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * See op_cmpl_float for more details. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + + FETCH(a0, 1) # a0 <- CCBB + and rOBJ, a0, 255 # s5 <- BB + srl t0, a0, 8 # t0 <- CC + EAS2(rOBJ, rFP, rOBJ) # s5 <- &fp[BB] + EAS2(t0, rFP, t0) # t0 <- &fp[CC] + LOAD64_F(ft0, ft0f, rOBJ) + LOAD64_F(ft1, ft1f, t0) +#ifdef MIPS32REVGE6 + cmp.ult.d ft2, ft0, ft1 + li rTEMP, -1 + bc1nez ft2, .L${opcode}_finish + cmp.ult.d ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .L${opcode}_finish + cmp.eq.d ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .L${opcode}_finish + b .L${opcode}_nan +#else + c.olt.d fcc0, ft0, ft1 + li rTEMP, -1 + bc1t fcc0, .L${opcode}_finish + c.olt.d fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .L${opcode}_finish + c.eq.d fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .L${opcode}_finish + b .L${opcode}_nan +#endif +%break + +.L${opcode}_nan: + $naninst + +.L${opcode}_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP diff --git a/runtime/interpreter/mterp/mips/op_cmpl_float.S b/runtime/interpreter/mterp/mips/op_cmpl_float.S new file mode 100644 index 0000000000000000000000000000000000000000..cfd87ee3bcf5047bf5e5e3ad1209f2650f0f7396 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_cmpl_float.S @@ -0,0 +1,61 @@ +%default { "naninst":"li rTEMP, -1" } + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register rTEMP based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1 or 1}; // one or both operands was NaN + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + + /* "clasic" form */ + FETCH(a0, 1) # a0 <- CCBB + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 + GET_VREG_F(ft0, a2) + GET_VREG_F(ft1, a3) +#ifdef MIPS32REVGE6 + cmp.ult.s ft2, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1nez ft2, .L${opcode}_finish + cmp.ult.s ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .L${opcode}_finish + cmp.eq.s ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .L${opcode}_finish + b .L${opcode}_nan +#else + c.olt.s fcc0, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1t fcc0, .L${opcode}_finish + c.olt.s fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .L${opcode}_finish + c.eq.s fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .L${opcode}_finish + b .L${opcode}_nan +#endif +%break + +.L${opcode}_nan: + $naninst + +.L${opcode}_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP diff --git a/runtime/interpreter/mterp/mips/op_const.S b/runtime/interpreter/mterp/mips/op_const.S new file mode 100644 index 0000000000000000000000000000000000000000..c50576103039c3cbd8abf51e1ebbfe9172906b0a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const.S @@ -0,0 +1,9 @@ + # const vAA, /* +BBBBbbbb */ + GET_OPA(a3) # a3 <- AA + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a1, a1, 16 + or a0, a1, a0 # a0 <- BBBBbbbb + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 diff --git a/runtime/interpreter/mterp/mips/op_const_16.S b/runtime/interpreter/mterp/mips/op_const_16.S new file mode 100644 index 0000000000000000000000000000000000000000..5e47633f3be33de22f8c5d269ec92b73150e31fd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_16.S @@ -0,0 +1,6 @@ + # const/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 diff --git a/runtime/interpreter/mterp/mips/op_const_4.S b/runtime/interpreter/mterp/mips/op_const_4.S new file mode 100644 index 0000000000000000000000000000000000000000..8b662f95e8e8ec75ba70531edd80d880cb432802 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_4.S @@ -0,0 +1,8 @@ + # const/4 vA, /* +B */ + sll a1, rINST, 16 # a1 <- Bxxx0000 + GET_OPA(a0) # a0 <- A+ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sra a1, a1, 28 # a1 <- sssssssB (sign-extended) + and a0, a0, 15 + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a1, a0, t0) # fp[A] <- a1 diff --git a/runtime/interpreter/mterp/mips/op_const_class.S b/runtime/interpreter/mterp/mips/op_const_class.S new file mode 100644 index 0000000000000000000000000000000000000000..7202b1158156c8bd2654bef57a0a30f68990a0b5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_class.S @@ -0,0 +1,12 @@ + # const/class vAA, Class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstClass) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_high16.S b/runtime/interpreter/mterp/mips/op_const_high16.S new file mode 100644 index 0000000000000000000000000000000000000000..36c1c350493da943c8e313ed6ed93ddf0e211a83 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_high16.S @@ -0,0 +1,7 @@ + # const/high16 vAA, /* +BBBB0000 */ + FETCH(a0, 1) # a0 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + sll a0, a0, 16 # a0 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 diff --git a/runtime/interpreter/mterp/mips/op_const_string.S b/runtime/interpreter/mterp/mips/op_const_string.S new file mode 100644 index 0000000000000000000000000000000000000000..d8eeb46b836c1310863cb01510a979fc2dc5c58f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_string.S @@ -0,0 +1,12 @@ + # const/string vAA, String /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_string_jumbo.S b/runtime/interpreter/mterp/mips/op_const_string_jumbo.S new file mode 100644 index 0000000000000000000000000000000000000000..d732ca151ab086d3f5078b6ffeb9f5bba82ca0d6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_string_jumbo.S @@ -0,0 +1,15 @@ + # const/string vAA, String /* BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a2, 2) # a2 <- BBBB (high) + GET_OPA(a1) # a1 <- AA + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(3) # load rINST + bnez v0, MterpPossibleException + ADVANCE(3) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide.S b/runtime/interpreter/mterp/mips/op_const_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..01d0f87c699da726e0ef53c1340e2b30c0c8b0c6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide.S @@ -0,0 +1,14 @@ + # const-wide vAA, /* +HHHHhhhhBBBBbbbb */ + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (low middle) + FETCH(a2, 3) # a2 <- hhhh (high middle) + sll a1, 16 # + or a0, a1 # a0 <- BBBBbbbb (low word) + FETCH(a3, 4) # a3 <- HHHH (high) + GET_OPA(t1) # t1 <- AA + sll a3, 16 + or a1, a3, a2 # a1 <- HHHHhhhh (high word) + FETCH_ADVANCE_INST(5) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, t1) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide_16.S b/runtime/interpreter/mterp/mips/op_const_wide_16.S new file mode 100644 index 0000000000000000000000000000000000000000..583d9efe2a73555d427b1bb44abcebc88e7397b7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide_16.S @@ -0,0 +1,8 @@ + # const-wide/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + sra a1, a0, 31 # a1 <- ssssssss + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide_32.S b/runtime/interpreter/mterp/mips/op_const_wide_32.S new file mode 100644 index 0000000000000000000000000000000000000000..3eb4574e2f222f2813391c5b35858f9a07ead687 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide_32.S @@ -0,0 +1,11 @@ + # const-wide/32 vAA, /* +BBBBbbbb */ + FETCH(a0, 1) # a0 <- 0000bbbb (low) + GET_OPA(a3) # a3 <- AA + FETCH_S(a2, 2) # a2 <- ssssBBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + sra a1, a0, 31 # a1 <- ssssssss + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_const_wide_high16.S b/runtime/interpreter/mterp/mips/op_const_wide_high16.S new file mode 100644 index 0000000000000000000000000000000000000000..88382c6fe3c341a9270ff4636231c18006bf08e1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_const_wide_high16.S @@ -0,0 +1,9 @@ + # const-wide/high16 vAA, /* +BBBB000000000000 */ + FETCH(a1, 1) # a1 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + li a0, 0 # a0 <- 00000000 + sll a1, 16 # a1 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_div_double.S b/runtime/interpreter/mterp/mips/op_div_double.S new file mode 100644 index 0000000000000000000000000000000000000000..84e4c4e3174774b3caa4a5dcee50abf55efe65c3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"div.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_double_2addr.S b/runtime/interpreter/mterp/mips/op_div_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..65b92e37db357125247e7252c07ce6c92421db37 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"div.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_float.S b/runtime/interpreter/mterp/mips/op_div_float.S new file mode 100644 index 0000000000000000000000000000000000000000..44b8d47e0b951ce9b7c4fb9cc4067f967d4e9835 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"div.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_float_2addr.S b/runtime/interpreter/mterp/mips/op_div_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..e5fff92c8c5006478954aebc20dca9e2e2c6d1e0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"div.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_div_int.S b/runtime/interpreter/mterp/mips/op_div_int.S new file mode 100644 index 0000000000000000000000000000000000000000..5d28c84d6b09b6c5be52d8707e2139e7c4908515 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_int_2addr.S b/runtime/interpreter/mterp/mips/op_div_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..6c079e04c4f9576a8cf2a04a53c24ca39f163ee3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int_2addr.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop2addr.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop2addr.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_int_lit16.S b/runtime/interpreter/mterp/mips/op_div_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..ee7452ce1a910f138744c8374f3d78786e79ffdf --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int_lit16.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit16.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit16.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_int_lit8.S b/runtime/interpreter/mterp/mips/op_div_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..d2964b8065e07ef3f4b2b3ffaab6f9eb9caedad6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_int_lit8.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit8.S" {"instr":"div a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit8.S" {"preinstr":"div zero, a0, a1", "instr":"mflo a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_div_long.S b/runtime/interpreter/mterp/mips/op_div_long.S new file mode 100644 index 0000000000000000000000000000000000000000..2097866886cfa51aeb90b40516de94097b2ce2fb --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_div_long_2addr.S b/runtime/interpreter/mterp/mips/op_div_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..c27930514233808c2069587f8ea7ccb067ce017b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_div_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"result0":"v0", "result1":"v1", "instr":"JAL(__divdi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_double_to_float.S b/runtime/interpreter/mterp/mips/op_double_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..1d32c2e1e4b84c556f2cdaae433dc4a30a245d9b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_double_to_float.S @@ -0,0 +1 @@ +%include "mips/unopNarrower.S" {"instr":"cvt.s.d fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_double_to_int.S b/runtime/interpreter/mterp/mips/op_double_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..30a0a73e617e3032f0e9554445ec50232b2abe1c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_double_to_int.S @@ -0,0 +1,58 @@ +%include "mips/unopNarrower.S" {"instr":"b d2i_doconv"} +/* + * Convert the double in a0/a1 to an int in a0. + * + * We have to clip values to int min/max per the specification. The + * expected common case is a "reasonable" value that converts directly + * to modest integer. The EABI convert function isn't doing this for us. + */ +%break + +d2i_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1nez ft2, .L${opcode}_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1nez ft2, .L${opcode}_set_vreg_f + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .L${opcode}_set_vreg_f +#else + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1t .L${opcode}_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1t .L${opcode}_set_vreg_f + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .L${opcode}_set_vreg_f +#endif + + trunc.w.d fv0, fa0 + b .L${opcode}_set_vreg_f + +.LDOUBLE_TO_INT_max: + .dword 0x41dfffffffc00000 +.LDOUBLE_TO_INT_min: + .dword 0xc1e0000000000000 # minint, as a double (high word) +.LDOUBLE_TO_INT_maxret: + .word 0x7fffffff +.LDOUBLE_TO_INT_minret: + .word 0x80000000 diff --git a/runtime/interpreter/mterp/mips/op_double_to_long.S b/runtime/interpreter/mterp/mips/op_double_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..4f9e367279e77a9b7c4617fece9b8c9a6e19c281 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_double_to_long.S @@ -0,0 +1,56 @@ +%include "mips/funopWide.S" {"instr":"b d2l_doconv", "st_result":"SET_VREG64(rRESULT0, rRESULT1, rOBJ)"} +%break + +d2l_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .L${opcode}_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .L${opcode}_set_vreg + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .L${opcode}_set_vreg +#else + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .L${opcode}_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .L${opcode}_set_vreg + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .L${opcode}_set_vreg +#endif + JAL(__fixdfdi) + b .L${opcode}_set_vreg + +.LDOUBLE_TO_LONG_max: + .dword 0x43e0000000000000 # maxlong, as a double (high word) +.LDOUBLE_TO_LONG_min: + .dword 0xc3e0000000000000 # minlong, as a double (high word) +.LDOUBLE_TO_LONG_ret_max: + .dword 0x7fffffffffffffff +.LDOUBLE_TO_LONG_ret_min: + .dword 0x8000000000000000 diff --git a/runtime/interpreter/mterp/mips/op_fill_array_data.S b/runtime/interpreter/mterp/mips/op_fill_array_data.S new file mode 100644 index 0000000000000000000000000000000000000000..86057462db988b2dc33a4b761f1a1e815acf6f61 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_fill_array_data.S @@ -0,0 +1,14 @@ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll a1, a1, 16 # a1 <- BBBBbbbb + or a1, a0, a1 # a1 <- BBBBbbbb + GET_VREG(a0, a3) # a0 <- vAA (array object) + EAS1(a1, rPC, a1) # a1 <- PC + BBBBbbbb*2 (array data off.) + JAL(MterpFillArrayData) # v0 <- Mterp(obj, payload) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_filled_new_array.S b/runtime/interpreter/mterp/mips/op_filled_new_array.S new file mode 100644 index 0000000000000000000000000000000000000000..3f62faef1cab3b06412a4eece3963d713aceb081 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_filled_new_array.S @@ -0,0 +1,18 @@ +%default { "helper":"MterpFilledNewArray" } + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, type /* BBBB */ + .extern $helper + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME # a0 <- shadow frame + move a1, rPC + move a2, rSELF + JAL($helper) # v0 <- helper(shadow_frame, pc, self) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_filled_new_array_range.S b/runtime/interpreter/mterp/mips/op_filled_new_array_range.S new file mode 100644 index 0000000000000000000000000000000000000000..f8dcb0e03783bd7a7e0d69391174da688c7d2d66 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "mips/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/mips/op_float_to_double.S b/runtime/interpreter/mterp/mips/op_float_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..1315255b5c64e4aa47acd1b68cda2d170c5c82d3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_float_to_double.S @@ -0,0 +1 @@ +%include "mips/funopWider.S" {"instr":"cvt.d.s fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_float_to_int.S b/runtime/interpreter/mterp/mips/op_float_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..e032869873e1717b8cf89645ba720bdbfdc4a036 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_float_to_int.S @@ -0,0 +1,50 @@ +%include "mips/funop.S" {"instr":"b f2i_doconv"} +%break + +/* + * Not an entry point as it is used only once !! + */ +f2i_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LFLOAT_TO_INT_max + cmp.ule.s ft2, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1nez ft2, .L${opcode}_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + cmp.ule.s ft2, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1nez ft2, .L${opcode}_set_vreg_f + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .L${opcode}_set_vreg_f +#else + l.s fa1, .LFLOAT_TO_INT_max + c.ole.s fcc0, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1t .L${opcode}_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + c.ole.s fcc0, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1t .L${opcode}_set_vreg_f + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .L${opcode}_set_vreg_f +#endif + + trunc.w.s fv0, fa0 + b .L${opcode}_set_vreg_f + +.LFLOAT_TO_INT_max: + .word 0x4f000000 +.LFLOAT_TO_INT_min: + .word 0xcf000000 +.LFLOAT_TO_INT_ret_max: + .word 0x7fffffff +.LFLOAT_TO_INT_ret_min: + .word 0x80000000 diff --git a/runtime/interpreter/mterp/mips/op_float_to_long.S b/runtime/interpreter/mterp/mips/op_float_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..77b2c46b4e3e23297773c67cbca370b9abc18cbe --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_float_to_long.S @@ -0,0 +1,51 @@ +%include "mips/funopWider.S" {"instr":"b f2l_doconv", "st_result":"SET_VREG64(rRESULT0, rRESULT1, rOBJ)"} +%break + +f2l_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LLONG_TO_max + cmp.ule.s ft2, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1nez ft2, .L${opcode}_set_vreg + + l.s fa1, .LLONG_TO_min + cmp.ule.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1nez ft2, .L${opcode}_set_vreg + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .L${opcode}_set_vreg +#else + l.s fa1, .LLONG_TO_max + c.ole.s fcc0, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1t .L${opcode}_set_vreg + + l.s fa1, .LLONG_TO_min + c.ole.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1t .L${opcode}_set_vreg + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .L${opcode}_set_vreg +#endif + + JAL(__fixsfdi) + + b .L${opcode}_set_vreg + +.LLONG_TO_max: + .word 0x5f000000 + +.LLONG_TO_min: + .word 0xdf000000 diff --git a/runtime/interpreter/mterp/mips/op_goto.S b/runtime/interpreter/mterp/mips/op_goto.S new file mode 100644 index 0000000000000000000000000000000000000000..d6f21c9b2cf513d624e1ca8bc51a51b7bc7112b5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_goto.S @@ -0,0 +1,38 @@ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ +#if MTERP_PROFILE_BRANCHES + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a2, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif diff --git a/runtime/interpreter/mterp/mips/op_goto_16.S b/runtime/interpreter/mterp/mips/op_goto_16.S new file mode 100644 index 0000000000000000000000000000000000000000..cec4432599ca10c20330432f66ee5b602adea0d9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_goto_16.S @@ -0,0 +1,34 @@ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif diff --git a/runtime/interpreter/mterp/mips/op_goto_32.S b/runtime/interpreter/mterp/mips/op_goto_32.S new file mode 100644 index 0000000000000000000000000000000000000000..083acd1ef9e81342973e7f9813f0a7dbcd1ec941 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_goto_32.S @@ -0,0 +1,43 @@ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif diff --git a/runtime/interpreter/mterp/mips/op_if_eq.S b/runtime/interpreter/mterp/mips/op_if_eq.S new file mode 100644 index 0000000000000000000000000000000000000000..e7190d819736c6198bb9f5c1a9b1f3dbe70c22cb --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_eq.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/mips/op_if_eqz.S b/runtime/interpreter/mterp/mips/op_if_eqz.S new file mode 100644 index 0000000000000000000000000000000000000000..0a78fd98aca074cde53494bbb0f61cc52cc0ba06 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_eqz.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/mips/op_if_ge.S b/runtime/interpreter/mterp/mips/op_if_ge.S new file mode 100644 index 0000000000000000000000000000000000000000..b2629ba4e93d7817a59e1594e4f8609f894a4dbb --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_ge.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"lt" } diff --git a/runtime/interpreter/mterp/mips/op_if_gez.S b/runtime/interpreter/mterp/mips/op_if_gez.S new file mode 100644 index 0000000000000000000000000000000000000000..b02f67709f7611560beedb63be4d6b006acf995c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_gez.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"lt" } diff --git a/runtime/interpreter/mterp/mips/op_if_gt.S b/runtime/interpreter/mterp/mips/op_if_gt.S new file mode 100644 index 0000000000000000000000000000000000000000..f620d4a1fd454e0cf1561223105b39b42936519f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_gt.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/mips/op_if_gtz.S b/runtime/interpreter/mterp/mips/op_if_gtz.S new file mode 100644 index 0000000000000000000000000000000000000000..5e5dd708fa4bbe4c574b12ebc56da13e61856d95 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_gtz.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/mips/op_if_le.S b/runtime/interpreter/mterp/mips/op_if_le.S new file mode 100644 index 0000000000000000000000000000000000000000..a4e8b1ad51cef036e7760285dfc98482ca2fcd33 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_le.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"gt" } diff --git a/runtime/interpreter/mterp/mips/op_if_lez.S b/runtime/interpreter/mterp/mips/op_if_lez.S new file mode 100644 index 0000000000000000000000000000000000000000..af551a62fd6ece1986615e42531ceb73e9a86fc4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_lez.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"gt" } diff --git a/runtime/interpreter/mterp/mips/op_if_lt.S b/runtime/interpreter/mterp/mips/op_if_lt.S new file mode 100644 index 0000000000000000000000000000000000000000..f33b9a4c051e446db836a13c92b897496544d275 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_lt.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/mips/op_if_ltz.S b/runtime/interpreter/mterp/mips/op_if_ltz.S new file mode 100644 index 0000000000000000000000000000000000000000..18fcb1d477ad5e95e50e44cfe1a136bb3d49f981 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_ltz.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/mips/op_if_ne.S b/runtime/interpreter/mterp/mips/op_if_ne.S new file mode 100644 index 0000000000000000000000000000000000000000..e0a102b443d06fb971b0205b97f7781eaf7cbebf --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_ne.S @@ -0,0 +1 @@ +%include "mips/bincmp.S" { "revcmp":"eq" } diff --git a/runtime/interpreter/mterp/mips/op_if_nez.S b/runtime/interpreter/mterp/mips/op_if_nez.S new file mode 100644 index 0000000000000000000000000000000000000000..d1866a0a022063d056eb1b717b4a7f7036fb83e3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_if_nez.S @@ -0,0 +1 @@ +%include "mips/zcmp.S" { "revcmp":"eq" } diff --git a/runtime/interpreter/mterp/mips/op_iget.S b/runtime/interpreter/mterp/mips/op_iget.S new file mode 100644 index 0000000000000000000000000000000000000000..86d44fa531bf275443b195f1917be600899138f8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget.S @@ -0,0 +1,25 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode"} + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL($helper) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if $is_object + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iget_boolean.S b/runtime/interpreter/mterp/mips/op_iget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..e03364e34bfd2f1d1d31cecf8b9eab5f61dda7da --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_boolean.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_boolean_quick.S b/runtime/interpreter/mterp/mips/op_iget_boolean_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..f3032b3d8432cbe52185b7bdb361b5590d417253 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lbu" } diff --git a/runtime/interpreter/mterp/mips/op_iget_byte.S b/runtime/interpreter/mterp/mips/op_iget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..dc87cfecf8754687f8e57e55aea2b9a01aaaed32 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_byte.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_byte_quick.S b/runtime/interpreter/mterp/mips/op_iget_byte_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..d93f84486d52a0b33c254917b359d9e1ce939ca0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lb" } diff --git a/runtime/interpreter/mterp/mips/op_iget_char.S b/runtime/interpreter/mterp/mips/op_iget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..55f8a93ff474ca50f4f8f4ce32b5747a34dad64e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_char.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_char_quick.S b/runtime/interpreter/mterp/mips/op_iget_char_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..6f6d6088e04474495ceed7535c390aad0e2a067c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lhu" } diff --git a/runtime/interpreter/mterp/mips/op_iget_object.S b/runtime/interpreter/mterp/mips/op_iget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..11d93a46d762fa52aa17bc4d8fc6c16ddfe8473f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_object.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_object_quick.S b/runtime/interpreter/mterp/mips/op_iget_object_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..31d94b9cb9d98430d455f448e0e80113ef446b59 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_object_quick.S @@ -0,0 +1,15 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + GET_OPB(a2) # a2 <- B + FETCH(a1, 1) # a1 <- field byte offset + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- object we're operating on + JAL(artIGetObjectFromMterp) # v0 <- GetObj(obj, offset) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iget_quick.S b/runtime/interpreter/mterp/mips/op_iget_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..fbafa5b0999634cc07526f2ed46409936990e494 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_quick.S @@ -0,0 +1,14 @@ +%default { "load":"lw" } + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + $load a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 diff --git a/runtime/interpreter/mterp/mips/op_iget_short.S b/runtime/interpreter/mterp/mips/op_iget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..9086246c97813229f2b1786acf51a88c4a132d2b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_short.S @@ -0,0 +1 @@ +%include "mips/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips/op_iget_short_quick.S b/runtime/interpreter/mterp/mips/op_iget_short_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..899a0feb656acdaefe7b404832959bbeaf2188ce --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "mips/op_iget_quick.S" { "load":"lh" } diff --git a/runtime/interpreter/mterp/mips/op_iget_wide.S b/runtime/interpreter/mterp/mips/op_iget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..8fe3089eefdd91875b6f6a590739cb18d9613b68 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_wide.S @@ -0,0 +1,20 @@ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field byte offset + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGet64InstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpException # bail out + SET_VREG64(v0, v1, a2) # fp[A] <- v0/v1 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iget_wide_quick.S b/runtime/interpreter/mterp/mips/op_iget_wide_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..4d2f29187a1f2f52c5ad5abe8e1b5b61127c7d42 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iget_wide_quick.S @@ -0,0 +1,13 @@ + # iget-wide-quick vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 # t0 <- a3 + a1 + LOAD64(a0, a1, t0) # a0 <- obj.field (64 bits, aligned) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_instance_of.S b/runtime/interpreter/mterp/mips/op_instance_of.S new file mode 100644 index 0000000000000000000000000000000000000000..d2679bdd000ba028566e10a48f148b4091c9dcac --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_instance_of.S @@ -0,0 +1,21 @@ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + # instance-of vA, vB, class /* CCCC */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- CCCC + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + GET_OPA4(rOBJ) # rOBJ <- A+ + JAL(MterpInstanceOf) # v0 <- Mterp(index, &obj, method, self) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vA <- v0 diff --git a/runtime/interpreter/mterp/mips/op_int_to_byte.S b/runtime/interpreter/mterp/mips/op_int_to_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..77314c62aaec3faef7032dd98e97938855dbd9be --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_byte.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"preinstr":"sll a0, a0, 24", "instr":"sra a0, a0, 24"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_char.S b/runtime/interpreter/mterp/mips/op_int_to_char.S new file mode 100644 index 0000000000000000000000000000000000000000..1b74a6e2494bb7a5f4a23e08bec5a2f17ae70b14 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_char.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"preinstr":"", "instr":"and a0, 0xffff"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_double.S b/runtime/interpreter/mterp/mips/op_int_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..89484ce34ffa12a98f25c4b91f1c5a1169f8eff3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_double.S @@ -0,0 +1 @@ +%include "mips/funopWider.S" {"instr":"cvt.d.w fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_float.S b/runtime/interpreter/mterp/mips/op_int_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..d6f4b3609f893ad71f9b1dcc89a3a9a3d94a7ef3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_float.S @@ -0,0 +1 @@ +%include "mips/funop.S" {"instr":"cvt.s.w fv0, fa0"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_long.S b/runtime/interpreter/mterp/mips/op_int_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..99074639508a73da979508bfe0e1648bfebbaed2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_long.S @@ -0,0 +1 @@ +%include "mips/unopWider.S" {"instr":"sra a1, a0, 31"} diff --git a/runtime/interpreter/mterp/mips/op_int_to_short.S b/runtime/interpreter/mterp/mips/op_int_to_short.S new file mode 100644 index 0000000000000000000000000000000000000000..5649c2a849fdac9e2eba0f68633763374f753f4d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_int_to_short.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"preinstr":"sll a0, 16", "instr":"sra a0, 16"} diff --git a/runtime/interpreter/mterp/mips/op_invoke_direct.S b/runtime/interpreter/mterp/mips/op_invoke_direct.S new file mode 100644 index 0000000000000000000000000000000000000000..1ef198a43462db4b1b30b8146d9ebc2614627476 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_direct.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_direct_range.S b/runtime/interpreter/mterp/mips/op_invoke_direct_range.S new file mode 100644 index 0000000000000000000000000000000000000000..af7477f2cd82d73cf4d65e0e02d97442ed1c13bd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_interface.S b/runtime/interpreter/mterp/mips/op_invoke_interface.S new file mode 100644 index 0000000000000000000000000000000000000000..80a485a077924dfca16e6dc3279ae29d4a9e2269 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_interface.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeInterface" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_interface_range.S b/runtime/interpreter/mterp/mips/op_invoke_interface_range.S new file mode 100644 index 0000000000000000000000000000000000000000..8d725dc2049da7cbd0d0ed6fbd8acee20a54b941 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_static.S b/runtime/interpreter/mterp/mips/op_invoke_static.S new file mode 100644 index 0000000000000000000000000000000000000000..46253cb3a7ee137169954a6dbb0ad04e55e112ca --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_static.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeStatic" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_static_range.S b/runtime/interpreter/mterp/mips/op_invoke_static_range.S new file mode 100644 index 0000000000000000000000000000000000000000..96abafe41d9418d88239c7ec21cb1ca84059c621 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_super.S b/runtime/interpreter/mterp/mips/op_invoke_super.S new file mode 100644 index 0000000000000000000000000000000000000000..473951bcce4ac2366da46483c8fd88e4764da73d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_super.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeSuper" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_super_range.S b/runtime/interpreter/mterp/mips/op_invoke_super_range.S new file mode 100644 index 0000000000000000000000000000000000000000..963ff27ec5b47896c6aa8b45b971d7afee4000de --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual.S b/runtime/interpreter/mterp/mips/op_invoke_virtual.S new file mode 100644 index 0000000000000000000000000000000000000000..ea51e98abc20d7dbd4d90618466eab1fb2288c84 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtual" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/mips/op_invoke_virtual_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..0c00091219e4a6d56757714a2635a49e674e28bd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual_range.S b/runtime/interpreter/mterp/mips/op_invoke_virtual_range.S new file mode 100644 index 0000000000000000000000000000000000000000..82201e726e45d470db2be51cc1525a5d094f772e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/mips/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/mips/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..c783675dae9dbf4fe391f1a0b0ba52734f40bdb8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "mips/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/mips/op_iput.S b/runtime/interpreter/mterp/mips/op_iput.S new file mode 100644 index 0000000000000000000000000000000000000000..732a9a45f16a2c4d9b9e324ac079fec923c1ed61 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput.S @@ -0,0 +1,21 @@ +%default { "handler":"artSet32InstanceFromMterp" } + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern $handler + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL($handler) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_boolean.S b/runtime/interpreter/mterp/mips/op_iput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..da28c978a4495629d85a768a089c81ead252ef0c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_boolean.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_boolean_quick.S b/runtime/interpreter/mterp/mips/op_iput_boolean_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..7d5caf6d6af22ac015214277200152d40eec78b8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips/op_iput_byte.S b/runtime/interpreter/mterp/mips/op_iput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..da28c978a4495629d85a768a089c81ead252ef0c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_byte.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_byte_quick.S b/runtime/interpreter/mterp/mips/op_iput_byte_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..7d5caf6d6af22ac015214277200152d40eec78b8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips/op_iput_char.S b/runtime/interpreter/mterp/mips/op_iput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..389b0bf19b06fbff9f095e59774a50724d2235ef --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_char.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_char_quick.S b/runtime/interpreter/mterp/mips/op_iput_char_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..4bc84eb581eb55096654695dc85fc876b99e320a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips/op_iput_object.S b/runtime/interpreter/mterp/mips/op_iput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..6b856e7bb4abb36e4bb57ca6f413128e09700e18 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_object.S @@ -0,0 +1,16 @@ + /* + * 32-bit instance field put. + * + * for: iput-object, iput-object-volatile + */ + # op vA, vB, field /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpIputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_object_quick.S b/runtime/interpreter/mterp/mips/op_iput_object_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..c3f152655102198adf3493ed6eeef2c00a8a14f7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_object_quick.S @@ -0,0 +1,11 @@ + /* For: iput-object-quick */ + # op vA, vB, offset /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpIputObjectQuick) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_quick.S b/runtime/interpreter/mterp/mips/op_iput_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..08296667a9e7c692c000510019cc5ec3190d8348 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_quick.S @@ -0,0 +1,14 @@ +%default { "store":"sw" } + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + $store a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_short.S b/runtime/interpreter/mterp/mips/op_iput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..389b0bf19b06fbff9f095e59774a50724d2235ef --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_short.S @@ -0,0 +1 @@ +%include "mips/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips/op_iput_short_quick.S b/runtime/interpreter/mterp/mips/op_iput_short_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..4bc84eb581eb55096654695dc85fc876b99e320a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "mips/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips/op_iput_wide.S b/runtime/interpreter/mterp/mips/op_iput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..6d23f8ccfb3f9f05824a3473c284a8e5f5eb1e6a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_wide.S @@ -0,0 +1,15 @@ + # iput-wide vA, vB, field /* CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + EAS2(a2, rFP, a2) # a2 <- &fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet64InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_iput_wide_quick.S b/runtime/interpreter/mterp/mips/op_iput_wide_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..9fdb8472735ac6363a99bb38ab73a79db2745456 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_iput_wide_quick.S @@ -0,0 +1,14 @@ + # iput-wide-quick vA, vB, offset /* CCCC */ + GET_OPA4(a0) # a0 <- A(+) + GET_OPB(a1) # a1 <- B + GET_VREG(a2, a1) # a2 <- fp[B], the object pointer + # check object for null + beqz a2, common_errNullObject # object was null + EAS2(a3, rFP, a0) # a3 <- &fp[A] + LOAD64(a0, a1, a3) # a0/a1 <- fp[A] + FETCH(a3, 1) # a3 <- field byte offset + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu a2, a2, a3 # obj.field (64 bits, aligned) <- a0/a1 + STORE64(a0, a1, a2) # obj.field (64 bits, aligned) <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_long_to_double.S b/runtime/interpreter/mterp/mips/op_long_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..b83aaf41d8812406811de7b1fbcf848ccaad4154 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_long_to_double.S @@ -0,0 +1 @@ +%include "mips/funopWide.S" {"instr":"JAL(__floatdidf)", "ld_arg":"LOAD64(rARG0, rARG1, a3)"} diff --git a/runtime/interpreter/mterp/mips/op_long_to_float.S b/runtime/interpreter/mterp/mips/op_long_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..27faba57dd0cd9d6ffb1c86e649132484577a1b6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_long_to_float.S @@ -0,0 +1 @@ +%include "mips/unopNarrower.S" {"instr":"JAL(__floatdisf)", "load":"LOAD64(rARG0, rARG1, a3)"} diff --git a/runtime/interpreter/mterp/mips/op_long_to_int.S b/runtime/interpreter/mterp/mips/op_long_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..949c180f3121797f275dc426dc5e52164638b457 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_long_to_int.S @@ -0,0 +1,2 @@ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +%include "mips/op_move.S" diff --git a/runtime/interpreter/mterp/mips/op_monitor_enter.S b/runtime/interpreter/mterp/mips/op_monitor_enter.S new file mode 100644 index 0000000000000000000000000000000000000000..20d90294ac7fabdf6a6a2089dc19d80056c89907 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_monitor_enter.S @@ -0,0 +1,13 @@ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artLockObjectFromCode) # v0 <- artLockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_monitor_exit.S b/runtime/interpreter/mterp/mips/op_monitor_exit.S new file mode 100644 index 0000000000000000000000000000000000000000..1eadff923b4df419853b79ea6284bf454766c5bd --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_monitor_exit.S @@ -0,0 +1,17 @@ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artUnlockObjectFromCode) # v0 <- artUnlockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move.S b/runtime/interpreter/mterp/mips/op_move.S new file mode 100644 index 0000000000000000000000000000000000000000..76588ba39e14cec4328142f69f2342e4a81f0c18 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if $is_object + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_16.S b/runtime/interpreter/mterp/mips/op_move_16.S new file mode 100644 index 0000000000000000000000000000000000000000..f7de6c20b5bc1b8c8e952a8d316c0d22fac8f685 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + FETCH(a1, 2) # a1 <- BBBB + FETCH(a0, 1) # a0 <- AAAA + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT(a2, a0) # fp[AAAA] <- a2 + .else + SET_VREG(a2, a0) # fp[AAAA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_exception.S b/runtime/interpreter/mterp/mips/op_move_exception.S new file mode 100644 index 0000000000000000000000000000000000000000..f04a0355138f09df30a643146d2dee041f7a5f74 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_exception.S @@ -0,0 +1,8 @@ + /* move-exception vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) # get exception obj + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG_OBJECT(a3, a2) # fp[AA] <- exception obj + GET_INST_OPCODE(t0) # extract opcode from rINST + sw zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_from16.S b/runtime/interpreter/mterp/mips/op_move_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..b8be7411aa4d22b5aaa7c708972e0637b39fd781 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_from16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + FETCH(a1, 1) # a1 <- BBBB + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT(a2, a0) # fp[AA] <- a2 + .else + SET_VREG(a2, a0) # fp[AA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_object.S b/runtime/interpreter/mterp/mips/op_move_object.S new file mode 100644 index 0000000000000000000000000000000000000000..9420ff359f13a729053bf6094be03726082e0670 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_object.S @@ -0,0 +1 @@ +%include "mips/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_object_16.S b/runtime/interpreter/mterp/mips/op_move_object_16.S new file mode 100644 index 0000000000000000000000000000000000000000..d6454c222d4528831731000ae7a945ce9d137a36 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_object_16.S @@ -0,0 +1 @@ +%include "mips/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_object_from16.S b/runtime/interpreter/mterp/mips/op_move_object_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..db0aca1f83a0d4d1963da2d2d8e165c836647055 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_object_from16.S @@ -0,0 +1 @@ +%include "mips/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_result.S b/runtime/interpreter/mterp/mips/op_move_result.S new file mode 100644 index 0000000000000000000000000000000000000000..315c68ef3f9d71808e4d62d03a92f5bff9144c74 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_result.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + GET_OPA(a2) # a2 <- AA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + lw a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE(t0) # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT(a0, a2) # fp[AA] <- a0 + .else + SET_VREG(a0, a2) # fp[AA] <- a0 + .endif + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_result_object.S b/runtime/interpreter/mterp/mips/op_move_result_object.S new file mode 100644 index 0000000000000000000000000000000000000000..fcbffee28111d1c09edc3df2e9d2d1c73084219a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_result_object.S @@ -0,0 +1 @@ +%include "mips/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips/op_move_result_wide.S b/runtime/interpreter/mterp/mips/op_move_result_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..940c1ff9b20cd032cf3af26ae8f5e534da7f545d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_result_wide.S @@ -0,0 +1,8 @@ + /* move-result-wide vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + LOAD64(a0, a1, a3) # a0/a1 <- retval.j + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_wide.S b/runtime/interpreter/mterp/mips/op_move_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..dd224c390e0aa0fca2acd21d5dc06254c2ee76e2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_wide.S @@ -0,0 +1,10 @@ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + GET_OPA4(a2) # a2 <- A(+) + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- fp[B] + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_wide_16.S b/runtime/interpreter/mterp/mips/op_move_wide_16.S new file mode 100644 index 0000000000000000000000000000000000000000..d8761eb2effc40b4dcdeee8950a241a3305798b5 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_wide_16.S @@ -0,0 +1,10 @@ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 2) # a3 <- BBBB + FETCH(a2, 1) # a2 <- AAAA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AAAA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_move_wide_from16.S b/runtime/interpreter/mterp/mips/op_move_wide_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..2103fa19aeca50794ceb0be5939685133bebee2c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_move_wide_from16.S @@ -0,0 +1,10 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 1) # a3 <- BBBB + GET_OPA(a2) # a2 <- AA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_mul_double.S b/runtime/interpreter/mterp/mips/op_mul_double.S new file mode 100644 index 0000000000000000000000000000000000000000..44a473bac108ff9a8a9e77d09153cd53dc32e0e9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"mul.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_double_2addr.S b/runtime/interpreter/mterp/mips/op_mul_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..4e5c230bc5da0344e12b7a010ae1c0e6c560b8b3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"mul.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_float.S b/runtime/interpreter/mterp/mips/op_mul_float.S new file mode 100644 index 0000000000000000000000000000000000000000..abc939054382ba5465fcb4b59a51e9a9eceec0a2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"mul.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_float_2addr.S b/runtime/interpreter/mterp/mips/op_mul_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..24691095185ada93609a6cfd2aa13499e8c0ebb2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"mul.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int.S b/runtime/interpreter/mterp/mips/op_mul_int.S new file mode 100644 index 0000000000000000000000000000000000000000..266823c2c7c03013fa03e9897f980940d9a68195 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int_2addr.S b/runtime/interpreter/mterp/mips/op_mul_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..b7dc5d3fb1ba37234ad79f0fc2a664ce61e0dc12 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int_lit16.S b/runtime/interpreter/mterp/mips/op_mul_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..fb4c8ec27d2583eec69b540646067508a3769d4e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_int_lit8.S b/runtime/interpreter/mterp/mips/op_mul_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..6d2e7ded0cccb920bc6e1909ceb5f82442279706 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_mul_long.S b/runtime/interpreter/mterp/mips/op_mul_long.S new file mode 100644 index 0000000000000000000000000000000000000000..803bbecf17fe6e69830605b34ad30d9df7124049 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_long.S @@ -0,0 +1,43 @@ + /* + * Signed 64-bit integer multiply. + * a1 a0 + * x a3 a2 + * ------------- + * a2a1 a2a0 + * a3a0 + * a3a1 (<= unused) + * --------------- + * v1 v0 + */ + /* mul-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + and t0, a0, 255 # a2 <- BB + srl t1, a0, 8 # a3 <- CC + EAS2(t0, rFP, t0) # t0 <- &fp[BB] + LOAD64(a0, a1, t0) # a0/a1 <- vBB/vBB+1 + + EAS2(t1, rFP, t1) # t0 <- &fp[CC] + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 +#endif + mul t0, a2, a1 # t0= a2a1 + addu v1, v1, t1 # v1+= hi(a2a0) + addu v1, v1, t0 # v1= a3a0 + a2a1; + + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + b .L${opcode}_finish +%break + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, a0) # vAA::vAA+1 <- v0(low) :: v1(high) + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_mul_long_2addr.S b/runtime/interpreter/mterp/mips/op_mul_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..6950b7170b172af4288ce114cfc2ab875db42949 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_mul_long_2addr.S @@ -0,0 +1,31 @@ + /* + * See op_mul_long.S for more details + */ + /* mul-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # vAA.low / high + + GET_OPB(t1) # t1 <- B + EAS2(t1, rFP, t1) # t1 <- &fp[B] + LOAD64(a2, a3, t1) # vBB.low / high + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 + #endif + mul t2, a2, a1 # t2= a2a1 + addu v1, v1, t1 # v1= a3a0 + hi(a2a0) + addu v1, v1, t2 # v1= v1 + a2a1; + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t1) # extract opcode from rINST + # vAA <- v0 (low) + SET_VREG64(v0, v1, rOBJ) # vAA+1 <- v1 (high) + GOTO_OPCODE(t1) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_neg_double.S b/runtime/interpreter/mterp/mips/op_neg_double.S new file mode 100644 index 0000000000000000000000000000000000000000..89cc918b8048b87644276ed9cd4eb9345a6f90c9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_double.S @@ -0,0 +1 @@ +%include "mips/unopWide.S" {"instr":"addu a1, a1, 0x80000000"} diff --git a/runtime/interpreter/mterp/mips/op_neg_float.S b/runtime/interpreter/mterp/mips/op_neg_float.S new file mode 100644 index 0000000000000000000000000000000000000000..e702755f11284770e12a424fc3519d834da1bbc0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_float.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"instr":"addu a0, a0, 0x80000000"} diff --git a/runtime/interpreter/mterp/mips/op_neg_int.S b/runtime/interpreter/mterp/mips/op_neg_int.S new file mode 100644 index 0000000000000000000000000000000000000000..44617314659e9a80be8b8b836b9cbb616ce56c31 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_int.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"instr":"negu a0, a0"} diff --git a/runtime/interpreter/mterp/mips/op_neg_long.S b/runtime/interpreter/mterp/mips/op_neg_long.S new file mode 100644 index 0000000000000000000000000000000000000000..71e60f59be8b9fafd881901dd18d4c4fa1edbd75 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_neg_long.S @@ -0,0 +1 @@ +%include "mips/unopWide.S" {"result0":"v0", "result1":"v1", "preinstr":"negu v0, a0", "instr":"negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0"} diff --git a/runtime/interpreter/mterp/mips/op_new_array.S b/runtime/interpreter/mterp/mips/op_new_array.S new file mode 100644 index 0000000000000000000000000000000000000000..4a6512d7c5f3e9fdaefaf8020fa049532404c262 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_new_array.S @@ -0,0 +1,18 @@ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpNewArray) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_new_instance.S b/runtime/interpreter/mterp/mips/op_new_instance.S new file mode 100644 index 0000000000000000000000000000000000000000..51a09b2e90919b505eb34b1c7ed9161a3e301ace --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_new_instance.S @@ -0,0 +1,13 @@ + /* + * Create a new instance of a class. + */ + # new-instance vAA, class /* BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + JAL(MterpNewInstance) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_nop.S b/runtime/interpreter/mterp/mips/op_nop.S new file mode 100644 index 0000000000000000000000000000000000000000..3565631e03bc2b728515bfbe3cc974bc967d42e3 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_nop.S @@ -0,0 +1,3 @@ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_not_int.S b/runtime/interpreter/mterp/mips/op_not_int.S new file mode 100644 index 0000000000000000000000000000000000000000..55d8cc11d175d2087f989d11c1bcaaa815634e1e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_not_int.S @@ -0,0 +1 @@ +%include "mips/unop.S" {"instr":"not a0, a0"} diff --git a/runtime/interpreter/mterp/mips/op_not_long.S b/runtime/interpreter/mterp/mips/op_not_long.S new file mode 100644 index 0000000000000000000000000000000000000000..9e7c95bcea19c08fa952750bf106e53954df1770 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_not_long.S @@ -0,0 +1 @@ +%include "mips/unopWide.S" {"preinstr":"not a0, a0", "instr":"not a1, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int.S b/runtime/interpreter/mterp/mips/op_or_int.S new file mode 100644 index 0000000000000000000000000000000000000000..c7ce760a73cd134850c3a3ade4d5bc216c94a086 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int_2addr.S b/runtime/interpreter/mterp/mips/op_or_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..192d611e8d826ce4565b2b9852553be8b21980ec --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int_lit16.S b/runtime/interpreter/mterp/mips/op_or_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..f4ef75fff8713f44c9a0d751338e041653ea83f6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_int_lit8.S b/runtime/interpreter/mterp/mips/op_or_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..f6212e217d6d3b69e60b6eafb5445a0128666bb8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_or_long.S b/runtime/interpreter/mterp/mips/op_or_long.S new file mode 100644 index 0000000000000000000000000000000000000000..0f94486e421770fb73c0790a239a269671ba09cb --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_or_long_2addr.S b/runtime/interpreter/mterp/mips/op_or_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..43c3d05d41667add386de2ffe2d82420cc4fac89 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"preinstr":"or a0, a0, a2", "instr":"or a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_packed_switch.S b/runtime/interpreter/mterp/mips/op_packed_switch.S new file mode 100644 index 0000000000000000000000000000000000000000..93fae973e5a6fe8ebf8e2b92ad554e7802f2ca4d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_packed_switch.S @@ -0,0 +1,57 @@ +%default { "func":"MterpDoPackedSwitch" } + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL($func) # a0 <- code-unit branch offset + move rINST, v0 + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, .L${opcode}_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +#else + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL($func) # a0 <- code-unit branch offset + move rINST, v0 + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +%break + +.L${opcode}_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_rem_double.S b/runtime/interpreter/mterp/mips/op_rem_double.S new file mode 100644 index 0000000000000000000000000000000000000000..a6890a8029ee8430d5bbc2825c625d31af9d61a7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"JAL(fmod)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_double_2addr.S b/runtime/interpreter/mterp/mips/op_rem_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..a24e1604fac707642b924536ce566101ce10c0c7 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"JAL(fmod)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_float.S b/runtime/interpreter/mterp/mips/op_rem_float.S new file mode 100644 index 0000000000000000000000000000000000000000..ac3d50ce75f02afa20a08833289543345cf505d0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"JAL(fmodf)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_float_2addr.S b/runtime/interpreter/mterp/mips/op_rem_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..7f0a9320c82fda042d1ba808f5f74bf7e6e4c471 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"JAL(fmodf)"} diff --git a/runtime/interpreter/mterp/mips/op_rem_int.S b/runtime/interpreter/mterp/mips/op_rem_int.S new file mode 100644 index 0000000000000000000000000000000000000000..c2a334a8791a811201c12d5c9ec4647129b52cc6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_int_2addr.S b/runtime/interpreter/mterp/mips/op_rem_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..46c353fa83f574d79f7a9ff732b6ff104c7397ca --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int_2addr.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binop2addr.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binop2addr.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_int_lit16.S b/runtime/interpreter/mterp/mips/op_rem_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..2894ad37a29c14fcfe63651a37dcc68b74d1d998 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int_lit16.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit16.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit16.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_int_lit8.S b/runtime/interpreter/mterp/mips/op_rem_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..582248ba8fe4e70ec50afe53f02ce259f0c7c54f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_int_lit8.S @@ -0,0 +1,5 @@ +#ifdef MIPS32REVGE6 +%include "mips/binopLit8.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} +#else +%include "mips/binopLit8.S" {"preinstr":"div zero, a0, a1", "instr":"mfhi a0", "chkzero":"1"} +#endif diff --git a/runtime/interpreter/mterp/mips/op_rem_long.S b/runtime/interpreter/mterp/mips/op_rem_long.S new file mode 100644 index 0000000000000000000000000000000000000000..e3eb19bed4a5ac0ef5a2749050575a5eac9fe030 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_rem_long_2addr.S b/runtime/interpreter/mterp/mips/op_rem_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..8fc9fdb15f5a1d5983f563719061024c455b470c --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "instr":"JAL(__moddi3)", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips/op_return.S b/runtime/interpreter/mterp/mips/op_return.S new file mode 100644 index 0000000000000000000000000000000000000000..894ae18de084d74bd42cb8a9cfa69ca88eb797ba --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return.S @@ -0,0 +1,18 @@ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + GET_VREG(v0, a2) # v0 <- vAA + move v1, zero + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_return_object.S b/runtime/interpreter/mterp/mips/op_return_object.S new file mode 100644 index 0000000000000000000000000000000000000000..7350e008c552a98cf1a7e5aa167cba4309e842cf --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_object.S @@ -0,0 +1 @@ +%include "mips/op_return.S" diff --git a/runtime/interpreter/mterp/mips/op_return_void.S b/runtime/interpreter/mterp/mips/op_return_void.S new file mode 100644 index 0000000000000000000000000000000000000000..35c1326306d154e6a3bb3156ff86216383f72f03 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_void.S @@ -0,0 +1,11 @@ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S b/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S new file mode 100644 index 0000000000000000000000000000000000000000..56968b5fc44269781ff7d3f24eae3e5007c710f2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_void_no_barrier.S @@ -0,0 +1,9 @@ + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_return_wide.S b/runtime/interpreter/mterp/mips/op_return_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..91d62bf55037c8f1dd70d093892f7166f3d3eea9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_return_wide.S @@ -0,0 +1,16 @@ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + LOAD64(v0, v1, a2) # v0/v1 <- vAA/vAA+1 + b MterpReturn diff --git a/runtime/interpreter/mterp/mips/op_rsub_int.S b/runtime/interpreter/mterp/mips/op_rsub_int.S new file mode 100644 index 0000000000000000000000000000000000000000..f7e61bb2e91b5841b28b3bfd87541ebab9221975 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rsub_int.S @@ -0,0 +1,2 @@ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +%include "mips/binopLit16.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips/op_rsub_int_lit8.S b/runtime/interpreter/mterp/mips/op_rsub_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..3968a5ef8ca2e2ac26c20c2a6df8bb6d0a0e11a2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips/op_sget.S b/runtime/interpreter/mterp/mips/op_sget.S new file mode 100644 index 0000000000000000000000000000000000000000..3efcfbb7a5b201ea5e972368e4432fe87c8e015e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget.S @@ -0,0 +1,25 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode" } + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern $helper + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL($helper) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if $is_object + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sget_boolean.S b/runtime/interpreter/mterp/mips/op_sget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..45a5a70228d5768eb8b95c203831177713689cf1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_boolean.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetBooleanStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_byte.S b/runtime/interpreter/mterp/mips/op_sget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..319122cac0d5c690e064579efe94e53870d5fefe --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_byte.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetByteStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_char.S b/runtime/interpreter/mterp/mips/op_sget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..71038478e0f47ed9c65341efa9d198844954c80b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_char.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetCharStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_object.S b/runtime/interpreter/mterp/mips/op_sget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..b205f513aa06a2d34be3f1facd79cc42ae52c2a9 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_object.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_short.S b/runtime/interpreter/mterp/mips/op_sget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..3301823d86426262724f4d69dc29f94376ec8473 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_short.S @@ -0,0 +1 @@ +%include "mips/op_sget.S" {"helper":"artGetShortStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sget_wide.S b/runtime/interpreter/mterp/mips/op_sget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..7aee38655c5c8c3b657d4b1802884b1bf6ac9749 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sget_wide.S @@ -0,0 +1,17 @@ + /* + * 64-bit SGET handler. + */ + # sget-wide vAA, field /* BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGet64StaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + bnez a3, MterpException + GET_OPA(a1) # a1 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(v0, v1, a1) # vAA/vAA+1 <- v0/v1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_shl_int.S b/runtime/interpreter/mterp/mips/op_shl_int.S new file mode 100644 index 0000000000000000000000000000000000000000..15cbe94113794a170266f9cb638707e46ea570f0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shl_int_2addr.S b/runtime/interpreter/mterp/mips/op_shl_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ef9bd655ab71fc20df673187430685ca03d49b1f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shl_int_lit8.S b/runtime/interpreter/mterp/mips/op_shl_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..d2afb53e14e64359f760501779928554deba2be1 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shl_long.S b/runtime/interpreter/mterp/mips/op_shl_long.S new file mode 100644 index 0000000000000000000000000000000000000000..0121669a101fc88140dc156ce14dfc0ef3e203d2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_long.S @@ -0,0 +1,31 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shl-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t2) # t2 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .L${opcode}_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(zero, v0, t2, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_shl_long_2addr.S b/runtime/interpreter/mterp/mips/op_shl_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..8ce6058ce2946cfe8fe3689f3c5591e1cbdbf935 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shl_long_2addr.S @@ -0,0 +1,27 @@ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shl-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t2, rFP, rOBJ) # t2 <- &fp[A] + LOAD64(a0, a1, t2) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .L${opcode}_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(zero, v0, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_shr_int.S b/runtime/interpreter/mterp/mips/op_shr_int.S new file mode 100644 index 0000000000000000000000000000000000000000..611083999946fa2ab3a9d28171855393ea0e6232 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shr_int_2addr.S b/runtime/interpreter/mterp/mips/op_shr_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..e00ff5b2e6387c829286837c19d5e753a8eade97 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shr_int_lit8.S b/runtime/interpreter/mterp/mips/op_shr_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..d058f5862cfbbb9419b1a31925b03a8631c68507 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_shr_long.S b/runtime/interpreter/mterp/mips/op_shr_long.S new file mode 100644 index 0000000000000000000000000000000000000000..4c42758b83814ecd4324d7925aa25b534ed4748a --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_long.S @@ -0,0 +1,31 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t3) # t3 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/VAA+1 <- v0/v0 +%break + +.L${opcode}_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t3, t0) # vAA/VAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_shr_long_2addr.S b/runtime/interpreter/mterp/mips/op_shr_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..3adc085abf839d401be058974e124331a17bf06e --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_shr_long_2addr.S @@ -0,0 +1,27 @@ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shr-long/2addr vA, vB */ + GET_OPA4(t2) # t2 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t2) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t2, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_sparse_switch.S b/runtime/interpreter/mterp/mips/op_sparse_switch.S new file mode 100644 index 0000000000000000000000000000000000000000..670f4648a8595df099bde21167e0bf68571885c4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sparse_switch.S @@ -0,0 +1 @@ +%include "mips/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/mips/op_sput.S b/runtime/interpreter/mterp/mips/op_sput.S new file mode 100644 index 0000000000000000000000000000000000000000..ee313b9eccb035ce7365e350fd3dc0285ccad487 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput.S @@ -0,0 +1,19 @@ +%default { "helper":"artSet32StaticFromCode"} + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL($helper) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sput_boolean.S b/runtime/interpreter/mterp/mips/op_sput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..7909ef5622b0d890741bae8738254472d54ffcc2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_boolean.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_byte.S b/runtime/interpreter/mterp/mips/op_sput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..7909ef5622b0d890741bae8738254472d54ffcc2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_byte.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_char.S b/runtime/interpreter/mterp/mips/op_sput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..188195cc3ae099868820c42ab496f3bdb6c242e2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_char.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_object.S b/runtime/interpreter/mterp/mips/op_sput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..4f9034ec0e2a964098f6cbdcaa7d04cde37e1c7d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_object.S @@ -0,0 +1,16 @@ + /* + * General 32-bit SPUT handler. + * + * for: sput-object, + */ + /* op vAA, field@BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpSputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sput_short.S b/runtime/interpreter/mterp/mips/op_sput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..188195cc3ae099868820c42ab496f3bdb6c242e2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_short.S @@ -0,0 +1 @@ +%include "mips/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips/op_sput_wide.S b/runtime/interpreter/mterp/mips/op_sput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..1e11466670ea8be2c1e6431067f8a29f7ddae658 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sput_wide.S @@ -0,0 +1,17 @@ + /* + * 64-bit SPUT handler. + */ + # sput-wide vAA, field /* BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet64IndirectStaticFromMterp) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/op_sub_double.S b/runtime/interpreter/mterp/mips/op_sub_double.S new file mode 100644 index 0000000000000000000000000000000000000000..9473218e89324e45b4a6c77fce31ce3bed4483f0 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_double.S @@ -0,0 +1 @@ +%include "mips/fbinopWide.S" {"instr":"sub.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_double_2addr.S b/runtime/interpreter/mterp/mips/op_sub_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..7ce7c74330aa83cb4b0849108f43746e557e84a2 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinopWide2addr.S" {"instr":"sub.d fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_float.S b/runtime/interpreter/mterp/mips/op_sub_float.S new file mode 100644 index 0000000000000000000000000000000000000000..04650d9125b24f5fae4ed6d1c7c1d56764795114 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_float.S @@ -0,0 +1 @@ +%include "mips/fbinop.S" {"instr":"sub.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_float_2addr.S b/runtime/interpreter/mterp/mips/op_sub_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..dfe935c8cfec5878c0fdf639585dffc47abeafa8 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "mips/fbinop2addr.S" {"instr":"sub.s fv0, fa0, fa1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_int.S b/runtime/interpreter/mterp/mips/op_sub_int.S new file mode 100644 index 0000000000000000000000000000000000000000..43da1b617a853384abdc4122a88edc8f751de104 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_int_2addr.S b/runtime/interpreter/mterp/mips/op_sub_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..cf34aa69dc4ae5c9980b3943774dd6ddfe693885 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_sub_long.S b/runtime/interpreter/mterp/mips/op_sub_long.S new file mode 100644 index 0000000000000000000000000000000000000000..0f58e8e891b41a14884d3721374cc5087b7dbe33 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_long.S @@ -0,0 +1,8 @@ +/* + * For little endian the code sequence looks as follows: + * subu v0,a0,a2 + * subu v1,a1,a3 + * sltu a0,a0,v0 + * subu v1,v1,a0 + */ +%include "mips/binopWide.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" } diff --git a/runtime/interpreter/mterp/mips/op_sub_long_2addr.S b/runtime/interpreter/mterp/mips/op_sub_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..aa256c20f86951c95f11de8cec47e31c5c3cb774 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_sub_long_2addr.S @@ -0,0 +1,4 @@ +/* + * See op_sub_long.S for more details + */ +%include "mips/binopWide2addr.S" { "result0":"v0", "result1":"v1", "preinstr":"subu v0, a0, a2", "instr":"subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0" } diff --git a/runtime/interpreter/mterp/mips/op_throw.S b/runtime/interpreter/mterp/mips/op_throw.S new file mode 100644 index 0000000000000000000000000000000000000000..adc8b047cae0cc133f2cc5e362297e9d261e6a24 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_throw.S @@ -0,0 +1,11 @@ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC() # exception handler can throw + GET_OPA(a2) # a2 <- AA + GET_VREG(a1, a2) # a1 <- vAA (exception object) + # null object? + beqz a1, common_errNullObject # yes, throw an NPE instead + sw a1, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException diff --git a/runtime/interpreter/mterp/mips/op_unused_3e.S b/runtime/interpreter/mterp/mips/op_unused_3e.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_3e.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_3f.S b/runtime/interpreter/mterp/mips/op_unused_3f.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_3f.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_40.S b/runtime/interpreter/mterp/mips/op_unused_40.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_40.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_41.S b/runtime/interpreter/mterp/mips/op_unused_41.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_41.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_42.S b/runtime/interpreter/mterp/mips/op_unused_42.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_42.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_43.S b/runtime/interpreter/mterp/mips/op_unused_43.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_43.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_73.S b/runtime/interpreter/mterp/mips/op_unused_73.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_73.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_79.S b/runtime/interpreter/mterp/mips/op_unused_79.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_79.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_7a.S b/runtime/interpreter/mterp/mips/op_unused_7a.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_7a.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f3.S b/runtime/interpreter/mterp/mips/op_unused_f3.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f3.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f4.S b/runtime/interpreter/mterp/mips/op_unused_f4.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f4.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f5.S b/runtime/interpreter/mterp/mips/op_unused_f5.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f5.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f6.S b/runtime/interpreter/mterp/mips/op_unused_f6.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f6.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f7.S b/runtime/interpreter/mterp/mips/op_unused_f7.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f7.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f8.S b/runtime/interpreter/mterp/mips/op_unused_f8.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f8.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_f9.S b/runtime/interpreter/mterp/mips/op_unused_f9.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_f9.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fa.S b/runtime/interpreter/mterp/mips/op_unused_fa.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fa.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fb.S b/runtime/interpreter/mterp/mips/op_unused_fb.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fb.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fc.S b/runtime/interpreter/mterp/mips/op_unused_fc.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fc.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fd.S b/runtime/interpreter/mterp/mips/op_unused_fd.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fd.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_fe.S b/runtime/interpreter/mterp/mips/op_unused_fe.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_fe.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_unused_ff.S b/runtime/interpreter/mterp/mips/op_unused_ff.S new file mode 100644 index 0000000000000000000000000000000000000000..99ef3cf3082af1624b138c623dd253b795e84c52 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_unused_ff.S @@ -0,0 +1 @@ +%include "mips/unused.S" diff --git a/runtime/interpreter/mterp/mips/op_ushr_int.S b/runtime/interpreter/mterp/mips/op_ushr_int.S new file mode 100644 index 0000000000000000000000000000000000000000..b95472b30e6fe396f4d68d25020562c3b2de6d34 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_ushr_int_2addr.S b/runtime/interpreter/mterp/mips/op_ushr_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..fc1777810099328e64836a6aa56f06fa172f89e6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"srl a0, a0, a1 "} diff --git a/runtime/interpreter/mterp/mips/op_ushr_int_lit8.S b/runtime/interpreter/mterp/mips/op_ushr_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..c82cfba15c37e67b5a6cba15f27976d7dd410242 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_ushr_long.S b/runtime/interpreter/mterp/mips/op_ushr_long.S new file mode 100644 index 0000000000000000000000000000000000000000..2e227a94af674d9a9185aa9f4fa220fbc542000b --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_long.S @@ -0,0 +1,31 @@ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* ushr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(v1, zero, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_ushr_long_2addr.S b/runtime/interpreter/mterp/mips/op_ushr_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ccf1f7e8f813363913ea456e1a81e35c36a60ba4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_ushr_long_2addr.S @@ -0,0 +1,27 @@ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* ushr-long/2addr vA, vB */ + GET_OPA4(t3) # t3 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t3) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .L${opcode}_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/vAA+1 <- a0/a1 +%break + +.L${opcode}_finish: + SET_VREG64_GOTO(v1, zero, t3, t0) # vAA/vAA+1 <- rlo/rhi diff --git a/runtime/interpreter/mterp/mips/op_xor_int.S b/runtime/interpreter/mterp/mips/op_xor_int.S new file mode 100644 index 0000000000000000000000000000000000000000..6c23f1f3786ac8a4f1770970b8519ab5b7353dc6 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int.S @@ -0,0 +1 @@ +%include "mips/binop.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_int_2addr.S b/runtime/interpreter/mterp/mips/op_xor_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..5ee1667f8d76feb88be87031cab86796da2b96fb --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "mips/binop2addr.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_int_lit16.S b/runtime/interpreter/mterp/mips/op_xor_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..2af37a611636261e6be2ed69c9edd5ec1507f487 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "mips/binopLit16.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_int_lit8.S b/runtime/interpreter/mterp/mips/op_xor_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..944ed692317d743d075b40b898666584a7553e39 --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "mips/binopLit8.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips/op_xor_long.S b/runtime/interpreter/mterp/mips/op_xor_long.S new file mode 100644 index 0000000000000000000000000000000000000000..93f8f70a2143033ce6436c474e081bae7039ca1f --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_long.S @@ -0,0 +1 @@ +%include "mips/binopWide.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/op_xor_long_2addr.S b/runtime/interpreter/mterp/mips/op_xor_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..49f3fa42f44166ff1fe16b29270ca3e8cd1e300d --- /dev/null +++ b/runtime/interpreter/mterp/mips/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "mips/binopWide2addr.S" {"preinstr":"xor a0, a0, a2", "instr":"xor a1, a1, a3"} diff --git a/runtime/interpreter/mterp/mips/unop.S b/runtime/interpreter/mterp/mips/unop.S new file mode 100644 index 0000000000000000000000000000000000000000..52a8f0ac980df5fe547f3dbd5e5c853f89acd108 --- /dev/null +++ b/runtime/interpreter/mterp/mips/unop.S @@ -0,0 +1,19 @@ +%default {"preinstr":"", "result0":"a0"} + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO($result0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ diff --git a/runtime/interpreter/mterp/mips/unopNarrower.S b/runtime/interpreter/mterp/mips/unopNarrower.S new file mode 100644 index 0000000000000000000000000000000000000000..9c38badba52d5307c0618b270a347a5dbaa9ac6a --- /dev/null +++ b/runtime/interpreter/mterp/mips/unopNarrower.S @@ -0,0 +1,24 @@ +%default {"load":"LOAD64_F(fa0, fa0f, a3)"} + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + $load + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $instr + +.L${opcode}_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips/unopWide.S b/runtime/interpreter/mterp/mips/unopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..fd25dffa3fc8d8bbfc1349a1db74229bc59b4316 --- /dev/null +++ b/runtime/interpreter/mterp/mips/unopWide.S @@ -0,0 +1,20 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1"} + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64($result0, $result1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ diff --git a/runtime/interpreter/mterp/mips/unopWider.S b/runtime/interpreter/mterp/mips/unopWider.S new file mode 100644 index 0000000000000000000000000000000000000000..1c18837775b1600cc6657b00a154755f4cb04ab4 --- /dev/null +++ b/runtime/interpreter/mterp/mips/unopWider.S @@ -0,0 +1,19 @@ +%default {"preinstr":"", "result0":"a0", "result1":"a1"} + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + $preinstr # optional op + $instr # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64($result0, $result1, rOBJ) # vA/vA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 10-11 instructions */ diff --git a/runtime/interpreter/mterp/mips/unused.S b/runtime/interpreter/mterp/mips/unused.S new file mode 100644 index 0000000000000000000000000000000000000000..ffa00becfdb2bb4ba58ee7659a0609978599110e --- /dev/null +++ b/runtime/interpreter/mterp/mips/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips/zcmp.S b/runtime/interpreter/mterp/mips/zcmp.S new file mode 100644 index 0000000000000000000000000000000000000000..1fa13851c7edcdb0de92463779fcd3bdfe2dc430 --- /dev/null +++ b/runtime/interpreter/mterp/mips/zcmp.S @@ -0,0 +1,32 @@ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + b${revcmp} a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/alt_stub.S b/runtime/interpreter/mterp/mips64/alt_stub.S new file mode 100644 index 0000000000000000000000000000000000000000..bd76a1b464e3b5e5dec2a47eb1ce5571f92bd8be --- /dev/null +++ b/runtime/interpreter/mterp/mips64/alt_stub.S @@ -0,0 +1,14 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (${opnum} * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. diff --git a/runtime/interpreter/mterp/mips64/bincmp.S b/runtime/interpreter/mterp/mips64/bincmp.S new file mode 100644 index 0000000000000000000000000000000000000000..aa5e74b3dedafe2781d5f843790c9098b9da741e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/bincmp.S @@ -0,0 +1,32 @@ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + .extern MterpProfileBranch + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + b${condition}c a0, a1, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binop.S b/runtime/interpreter/mterp/mips64/binop.S new file mode 100644 index 0000000000000000000000000000000000000000..fab48b73b3d73e93f6ef4e9ec748adf3fde5c5de --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binop.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a4 # vAA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binop2addr.S b/runtime/interpreter/mterp/mips64/binop2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..1ae73f51d45ef2910998a691f096cae627b88374 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binop2addr.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a2 # vA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binopLit16.S b/runtime/interpreter/mterp/mips64/binopLit16.S new file mode 100644 index 0000000000000000000000000000000000000000..925775824cb41eaac65dc757e48a2b48965e7182 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopLit16.S @@ -0,0 +1,28 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a2 # vA <- $result + GOTO_OPCODE v0 # jump to next instruction + diff --git a/runtime/interpreter/mterp/mips64/binopLit8.S b/runtime/interpreter/mterp/mips64/binopLit8.S new file mode 100644 index 0000000000000000000000000000000000000000..f4a0bba9b919eef9b75f6e5ed9e41501ad8f0094 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopLit8.S @@ -0,0 +1,29 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG $result, a2 # vAA <- $result + GOTO_OPCODE v0 # jump to next instruction + diff --git a/runtime/interpreter/mterp/mips64/binopWide.S b/runtime/interpreter/mterp/mips64/binopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..732f0d60f9da4172d7034daee7f55f56ba8c2b5b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopWide.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE $result, a4 # vAA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/binopWide2addr.S b/runtime/interpreter/mterp/mips64/binopWide2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..45d8d829602aec518809939610e2eba9bf5d9896 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/binopWide2addr.S @@ -0,0 +1,30 @@ +%default {"preinstr":"", "result":"a0", "chkzero":"0"} + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if $chkzero + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $preinstr # optional op + $instr # $result <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE $result, a2 # vA <- $result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/entry.S b/runtime/interpreter/mterp/mips64/entry.S new file mode 100644 index 0000000000000000000000000000000000000000..ae6c26b70636b3307ea00c741324df6f8993f2e2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/entry.S @@ -0,0 +1,85 @@ +/* + * 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. + */ + +/* + * Interpreter entry point. + */ + + .set reorder + + .text + .global ExecuteMterpImpl + .type ExecuteMterpImpl, %function + .balign 16 +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ +ExecuteMterpImpl: + .cfi_startproc + .cpsetup t9, t8, ExecuteMterpImpl + + .cfi_def_cfa sp, 0 + daddu sp, sp, -STACK_SIZE + .cfi_adjust_cfa_offset STACK_SIZE + + sd t8, STACK_OFFSET_GP(sp) + .cfi_rel_offset 28, STACK_OFFSET_GP + sd ra, STACK_OFFSET_RA(sp) + .cfi_rel_offset 31, STACK_OFFSET_RA + + sd s0, STACK_OFFSET_S0(sp) + .cfi_rel_offset 16, STACK_OFFSET_S0 + sd s1, STACK_OFFSET_S1(sp) + .cfi_rel_offset 17, STACK_OFFSET_S1 + sd s2, STACK_OFFSET_S2(sp) + .cfi_rel_offset 18, STACK_OFFSET_S2 + sd s3, STACK_OFFSET_S3(sp) + .cfi_rel_offset 19, STACK_OFFSET_S3 + sd s4, STACK_OFFSET_S4(sp) + .cfi_rel_offset 20, STACK_OFFSET_S4 + sd s5, STACK_OFFSET_S5(sp) + .cfi_rel_offset 21, STACK_OFFSET_S5 + + /* Remember the return register */ + sd a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sd a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + daddu rFP, a2, SHADOWFRAME_VREGS_OFFSET + lw v0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + dlsa rREFS, v0, rFP, 2 + daddu rPC, a1, CODEITEM_INSNS_OFFSET + lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) + dlsa rPC, v0, rPC, 1 + EXPORT_PC + + /* Starting ibase */ + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/mips64/fallback.S b/runtime/interpreter/mterp/mips64/fallback.S new file mode 100644 index 0000000000000000000000000000000000000000..560b994b08e568c787b931292a937db1f8ec3ff9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fallback.S @@ -0,0 +1,2 @@ +/* Transfer stub to alternate interpreter */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips64/fbinop.S b/runtime/interpreter/mterp/mips64/fbinop.S new file mode 100644 index 0000000000000000000000000000000000000000..f19dd1c3d9fefc6e8aa40813b583c48b0b25ca68 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinop.S @@ -0,0 +1,18 @@ +%default {} + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fbinop2addr.S b/runtime/interpreter/mterp/mips64/fbinop2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..2e2cd7e8e9528447d5b578fee3d62c3cc95b1e97 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinop2addr.S @@ -0,0 +1,17 @@ +%default {} + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fbinopWide.S b/runtime/interpreter/mterp/mips64/fbinopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..8915c9447cf9301f9634c60ed02e8ba4d6d1bbdd --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinopWide.S @@ -0,0 +1,18 @@ +%default {} + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fbinopWide2addr.S b/runtime/interpreter/mterp/mips64/fbinopWide2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..a3f4eaa8cca421ca7fb666ae45d41fa7753b9ffc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fbinopWide2addr.S @@ -0,0 +1,17 @@ +%default {} + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + $instr # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcmp.S b/runtime/interpreter/mterp/mips64/fcmp.S new file mode 100644 index 0000000000000000000000000000000000000000..2e1a3e4c3d581d736007046ed83b03ca4d3e4e20 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcmp.S @@ -0,0 +1,32 @@ +%default {} + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + cmp.eq.s f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if $gt_bias + cmp.lt.s f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.s f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcmpWide.S b/runtime/interpreter/mterp/mips64/fcmpWide.S new file mode 100644 index 0000000000000000000000000000000000000000..2a3a341a3d329198ba6066325b35d0196fb811f2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcmpWide.S @@ -0,0 +1,32 @@ +%default {} + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + cmp.eq.d f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if $gt_bias + cmp.lt.d f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.d f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcvtFooter.S b/runtime/interpreter/mterp/mips64/fcvtFooter.S new file mode 100644 index 0000000000000000000000000000000000000000..06e9507817b8400d944a56f9166c153d8719aa0c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcvtFooter.S @@ -0,0 +1,18 @@ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG$suffix $valreg, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/fcvtHeader.S b/runtime/interpreter/mterp/mips64/fcvtHeader.S new file mode 100644 index 0000000000000000000000000000000000000000..8742e42c393d22c97fefca8c000f4b2c083845f8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/fcvtHeader.S @@ -0,0 +1,15 @@ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG$suffix $valreg, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST diff --git a/runtime/interpreter/mterp/mips64/footer.S b/runtime/interpreter/mterp/mips64/footer.S new file mode 100644 index 0000000000000000000000000000000000000000..14d5fe01f5768895041e51c6ed20be8c7500c299 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/footer.S @@ -0,0 +1,172 @@ +/* + * We've detected a condition that will result in an exception, but the exception + * 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. + */ + + .extern MterpLogDivideByZeroException +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogDivideByZeroException +#endif + b MterpCommonFallback + + .extern MterpLogArrayIndexException +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogArrayIndexException +#endif + b MterpCommonFallback + + .extern MterpLogNullObjectException +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogNullObjectException +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqzc a0, MterpFallback # If not, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ + .extern MterpHandleException + .extern MterpShouldSwitchInterpreters +MterpException: + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpHandleException # (self, shadow_frame) + beqzc v0, MterpExceptionReturn # no local catch, back to caller. + ld a0, OFF_FP_CODE_ITEM(rFP) + lwu a1, OFF_FP_DEX_PC(rFP) + REFRESH_IBASE + daddu rPC, a0, CODEITEM_INSNS_OFFSET + dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr + /* Do we need to switch interpreters? */ + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + /* resume execution at catch block */ + EXPORT_PC + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in ra. + */ + .extern MterpSuspendCheck +MterpCheckSuspendAndContinue: + REFRESH_IBASE + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, check1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction +check1: + EXPORT_PC + move a0, rSELF + jal MterpSuspendCheck # (self) + bnezc v0, MterpFallback # Something in the environment changed, switch interpreters + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST # rINST contains offset + jal MterpLogOSR +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* + * Bail out to reference interpreter. + */ + .extern MterpLogFallback +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogFallback +#endif +MterpCommonFallback: + li v0, 0 # signal retry with reference interpreter. + b MterpDone + +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and RA. Here we restore SP, restore the registers, and then restore + * RA to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +/* + * Returned value is expected in a0 and if it's not 64-bit, the 32 most + * significant bits of a0 must be 0. + */ +MterpReturn: + ld a2, OFF_FP_RESULT_REGISTER(rFP) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + sd a0, 0(a2) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, check2 + jal MterpSuspendCheck # (self) +check2: + li v0, 1 # signal return to caller. +MterpDone: + ld s5, STACK_OFFSET_S5(sp) + .cfi_restore 21 + ld s4, STACK_OFFSET_S4(sp) + .cfi_restore 20 + ld s3, STACK_OFFSET_S3(sp) + .cfi_restore 19 + ld s2, STACK_OFFSET_S2(sp) + .cfi_restore 18 + ld s1, STACK_OFFSET_S1(sp) + .cfi_restore 17 + ld s0, STACK_OFFSET_S0(sp) + .cfi_restore 16 + + ld ra, STACK_OFFSET_RA(sp) + .cfi_restore 31 + + ld t8, STACK_OFFSET_GP(sp) + .cpreturn + .cfi_restore 28 + + .set noreorder + jr ra + daddu sp, sp, STACK_SIZE + .cfi_adjust_cfa_offset -STACK_SIZE + + .cfi_endproc + .size ExecuteMterpImpl, .-ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S new file mode 100644 index 0000000000000000000000000000000000000000..dd0fbe0057886ab7bcf843b32db60d9171311bd0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/header.S @@ -0,0 +1,278 @@ +/* + * 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 + +/* TODO: add the missing file and use its FP register definitions. */ +/* #include */ +/* FP register definitions */ +#define f0 $$f0 +#define f1 $$f1 +#define f2 $$f2 +#define f3 $$f3 +#define f12 $$f12 +#define f13 $$f13 + +/* + * It looks like the GNU assembler currently does not support the blec and bgtc + * idioms, which should translate into bgec and bltc respectively with swapped + * left and right register operands. + * TODO: remove these macros when the assembler is fixed. + */ +.macro blec lreg, rreg, target + bgec \rreg, \lreg, \target +.endm +.macro bgtc lreg, rreg, target + bltc \rreg, \lreg, \target +.endm + +/* +Mterp and MIPS64 notes: + +The following registers have fixed assignments: + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rINST first 16-bit code unit of current instruction + s4 rIBASE interpreted instruction base pointer, used for computed goto + s5 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rINST s3 +#define rIBASE s4 +#define rREFS s5 + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#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 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + sd rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + */ +.macro REFRESH_IBASE + ld rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) +.endm + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +.macro FETCH_INST + lhu rINST, 0(rPC) +.endm + +/* Advance rPC by some number of code units. */ +.macro ADVANCE count + daddu rPC, rPC, (\count) * 2 +.endm + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC.) + */ +.macro FETCH_ADVANCE_INST count + ADVANCE \count + FETCH_INST +.endm + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +.macro PREFETCH_INST count + lhu rINST, ((\count) * 2)(rPC) +.endm + +/* + * Put the instruction's opcode field into the specified register. + */ +.macro GET_INST_OPCODE reg + and \reg, rINST, 255 +.endm + +/* + * Begin executing the opcode in _reg. + */ +.macro GOTO_OPCODE reg + .set noat + sll AT, \reg, 7 + daddu AT, rIBASE, AT + jic AT, 0 + .set at +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + * Note, GET_VREG does sign extension to 64 bits while + * GET_VREG_U does zero extension to 64 bits. + * One is useful for arithmetic while the other is + * useful for storing the result value as 64-bit. + */ +.macro GET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + .set at +.endm +.macro GET_VREG_U reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwu \reg, 0(AT) + .set at +.endm +.macro GET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + .set at +.endm +.macro SET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm +.macro SET_VREG_OBJECT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw \reg, 0(AT) + .set at +.endm +.macro SET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm + +/* + * Get/set the 64-bit value from a Dalvik register. + * Avoid unaligned memory accesses. + * Note, SET_VREG_WIDE clobbers the register containing the value being stored. + * Note, SET_VREG_DOUBLE clobbers the register containing the Dalvik register number. + */ +.macro GET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + lw AT, 4(AT) + dinsu \reg, AT, 32, 32 + .set at +.endm +.macro GET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + lw AT, 4(AT) + mthc1 AT, \reg + .set at +.endm +.macro SET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + drotr32 \reg, \reg, 0 + sw \reg, 4(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + .set at +.endm +.macro SET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + mfhc1 \vreg, \reg + sw \vreg, 4(AT) + .set at +.endm + +/* + * On-stack offsets for spilling/unspilling callee-saved registers + * and the frame size. + */ +#define STACK_OFFSET_RA 0 +#define STACK_OFFSET_GP 8 +#define STACK_OFFSET_S0 16 +#define STACK_OFFSET_S1 24 +#define STACK_OFFSET_S2 32 +#define STACK_OFFSET_S3 40 +#define STACK_OFFSET_S4 48 +#define STACK_OFFSET_S5 56 +#define STACK_SIZE 64 + +/* Constants for float/double_to_int/long conversions */ +#define INT_MIN 0x80000000 +#define INT_MIN_AS_FLOAT 0xCF000000 +#define INT_MIN_AS_DOUBLE 0xC1E0000000000000 +#define LONG_MIN 0x8000000000000000 +#define LONG_MIN_AS_FLOAT 0xDF000000 +#define LONG_MIN_AS_DOUBLE 0xC3E0000000000000 diff --git a/runtime/interpreter/mterp/mips64/invoke.S b/runtime/interpreter/mterp/mips64/invoke.S new file mode 100644 index 0000000000000000000000000000000000000000..be647b618ba946b1c5c3d5189ae584d9d3711859 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/invoke.S @@ -0,0 +1,20 @@ +%default { "helper":"UndefinedInvokeHandler" } + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern $helper + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal $helper + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 diff --git a/runtime/interpreter/mterp/mips64/op_add_double.S b/runtime/interpreter/mterp/mips64/op_add_double.S new file mode 100644 index 0000000000000000000000000000000000000000..1520e325f7f9a8b791a7b0eebfd45358900d2388 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"add.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_double_2addr.S b/runtime/interpreter/mterp/mips64/op_add_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..c14382ef20e275e6ba2d90ed06b9d2892596c67a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"add.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_float.S b/runtime/interpreter/mterp/mips64/op_add_float.S new file mode 100644 index 0000000000000000000000000000000000000000..c6ed558dc3b46e8fd0a5d3b714b08709009d28a8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"add.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_float_2addr.S b/runtime/interpreter/mterp/mips64/op_add_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..4c20547b227d7979e9a87cc9b753c32c44234fb5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"add.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int.S b/runtime/interpreter/mterp/mips64/op_add_int.S new file mode 100644 index 0000000000000000000000000000000000000000..6e569de71a5d8061cff7e809a7010647146fff63 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int_2addr.S b/runtime/interpreter/mterp/mips64/op_add_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..2a84124a3a802371d2f22c7f9b8d14491f1d9e0a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int_lit16.S b/runtime/interpreter/mterp/mips64/op_add_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..94b053bba3b64e2ead7e4e3649e7019f512c2868 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_int_lit8.S b/runtime/interpreter/mterp/mips64/op_add_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..3b6d734723c1b750b95b2ab0f91556ed37b5f1f4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"addu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_long.S b/runtime/interpreter/mterp/mips64/op_add_long.S new file mode 100644 index 0000000000000000000000000000000000000000..c8d702f29fdf93ec94c7e37c1988233aa9604abf --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"daddu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_add_long_2addr.S b/runtime/interpreter/mterp/mips64/op_add_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..928ff545659b06e61fb7503e8ec3af02196352e0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_add_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"daddu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_aget.S b/runtime/interpreter/mterp/mips64/op_aget.S new file mode 100644 index 0000000000000000000000000000000000000000..0472a0616bcaab47442574a33d2e8e0004472d88 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget.S @@ -0,0 +1,29 @@ +%default { "load":"lw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if $shift + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, $shift # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + $load a2, $data_offset(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aget_boolean.S b/runtime/interpreter/mterp/mips64/op_aget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..d5be01b7c5b1b7c1d1ca9e8d208d0207e477b7dd --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lbu", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_byte.S b/runtime/interpreter/mterp/mips64/op_aget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..084de8d4df54bbce503b5db306d4f588ef6cbe3f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_byte.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_char.S b/runtime/interpreter/mterp/mips64/op_aget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..6c99ed52ade1dfeb21e6b8a48fec150045b4d966 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_char.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lhu", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_object.S b/runtime/interpreter/mterp/mips64/op_aget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..6374a05e7b8b32e9b9e03088a12fc67096768641 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_object.S @@ -0,0 +1,21 @@ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + .extern artAGetObjectFromMterp + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + jal artAGetObjectFromMterp # (array, index) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + PREFETCH_INST 2 + bnez a1, MterpException + SET_VREG_OBJECT v0, a4 # vAA <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aget_short.S b/runtime/interpreter/mterp/mips64/op_aget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..0158b0a1a171a6d12f96954412deaef183ba5add --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_short.S @@ -0,0 +1 @@ +%include "mips64/op_aget.S" { "load":"lh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aget_wide.S b/runtime/interpreter/mterp/mips64/op_aget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..0945acae5a6d87b8c98f8cadd9bb984736da2fb8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aget_wide.S @@ -0,0 +1,21 @@ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + */ + /* aget-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + lw a3, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) + dinsu a2, a3, 32, 32 # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_and_int.S b/runtime/interpreter/mterp/mips64/op_and_int.S new file mode 100644 index 0000000000000000000000000000000000000000..f0792a8351653a2629a7b5dfc7e9df03db1b845b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_int_2addr.S b/runtime/interpreter/mterp/mips64/op_and_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..08dc615518989987eed96af039f6ed721df4ccb3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_int_lit16.S b/runtime/interpreter/mterp/mips64/op_and_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..65d28ad20cf6703dbb34c5de13f7645c19f6d97c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_int_lit8.S b/runtime/interpreter/mterp/mips64/op_and_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..ab84bb7ce25d121ffb8d40e142c5ddaf3b938ca4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_long.S b/runtime/interpreter/mterp/mips64/op_and_long.S new file mode 100644 index 0000000000000000000000000000000000000000..e383ba00caab2778453f603d9cd3bd0f03b575ba --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_and_long_2addr.S b/runtime/interpreter/mterp/mips64/op_and_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..f863bb9275d0e7f293ff49c52465c28b16f77150 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"and a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_aput.S b/runtime/interpreter/mterp/mips64/op_aput.S new file mode 100644 index 0000000000000000000000000000000000000000..9bfda97d05967530fcdfa7d2129685852375e23c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput.S @@ -0,0 +1,29 @@ +%default { "store":"sw", "shift":"2", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET" } + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if $shift + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, $shift # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + $store a2, $data_offset(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aput_boolean.S b/runtime/interpreter/mterp/mips64/op_aput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..6707a1f11da9c8c0e57755723ef63d1a8abc10eb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_byte.S b/runtime/interpreter/mterp/mips64/op_aput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..7b9ce483799fb5d7268eb9bda5046b425c6da279 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_byte.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sb", "shift":"0", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_char.S b/runtime/interpreter/mterp/mips64/op_aput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..82bc8f7818504603ad356b9f4ed2b6238562ab01 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_char.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_object.S b/runtime/interpreter/mterp/mips64/op_aput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..b132456a182fc183324e4d605a2ab83e7fe3609a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_object.S @@ -0,0 +1,14 @@ + /* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + .extern MterpAputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpAputObject + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_aput_short.S b/runtime/interpreter/mterp/mips64/op_aput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..a7af2945b1ac21866e8d913904b95737979734be --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_short.S @@ -0,0 +1 @@ +%include "mips64/op_aput.S" { "store":"sh", "shift":"1", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/mips64/op_aput_wide.S b/runtime/interpreter/mterp/mips64/op_aput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..a1d7a3b51ea333857defaa2caedf983546d28b18 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_aput_wide.S @@ -0,0 +1,21 @@ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + */ + /* aput-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + GET_VREG_WIDE a2, a4 # a2 <- vAA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + sw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + dsrl32 a2, a2, 0 + sw a2, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_array_length.S b/runtime/interpreter/mterp/mips64/op_array_length.S new file mode 100644 index 0000000000000000000000000000000000000000..2d9e172d1863f8eb38d1be287b71531007fdcdcf --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_array_length.S @@ -0,0 +1,12 @@ + /* + * Return the length of an array. + */ + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a0, a1 # a0 <- vB (object ref) + ext a2, rINST, 8, 4 # a2 <- A + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- array length + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a3, a2 # vB <- length + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_check_cast.S b/runtime/interpreter/mterp/mips64/op_check_cast.S new file mode 100644 index 0000000000000000000000000000000000000000..472595d8242f9f6948ee87c43c188174650546b0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_check_cast.S @@ -0,0 +1,17 @@ + /* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class//BBBB */ + .extern MterpCheckCast + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpCheckCast # (index, &obj, method, self) + PREFETCH_INST 2 + bnez v0, MterpPossibleException + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_cmp_long.S b/runtime/interpreter/mterp/mips64/op_cmp_long.S new file mode 100644 index 0000000000000000000000000000000000000000..6e9376cfabe84b4fde6a29d2bb5592094b283252 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmp_long.S @@ -0,0 +1,13 @@ + /* cmp-long vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + slt a2, a0, a1 + slt a0, a1, a0 + subu a0, a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_cmpg_double.S b/runtime/interpreter/mterp/mips64/op_cmpg_double.S new file mode 100644 index 0000000000000000000000000000000000000000..a8e2ef9867cdb3f8cd95454a52f9d7089246e343 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpg_double.S @@ -0,0 +1 @@ +%include "mips64/fcmpWide.S" {"gt_bias":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_cmpg_float.S b/runtime/interpreter/mterp/mips64/op_cmpg_float.S new file mode 100644 index 0000000000000000000000000000000000000000..0c93eac7de11abe92dc221443e9f4bedf99d95b0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpg_float.S @@ -0,0 +1 @@ +%include "mips64/fcmp.S" {"gt_bias":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_cmpl_double.S b/runtime/interpreter/mterp/mips64/op_cmpl_double.S new file mode 100644 index 0000000000000000000000000000000000000000..9111b067f63e8b98b34ff7d6896592672d76cd0e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpl_double.S @@ -0,0 +1 @@ +%include "mips64/fcmpWide.S" {"gt_bias":"0"} diff --git a/runtime/interpreter/mterp/mips64/op_cmpl_float.S b/runtime/interpreter/mterp/mips64/op_cmpl_float.S new file mode 100644 index 0000000000000000000000000000000000000000..b047451842536f1ed97227e366ac12058bd0eaac --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_cmpl_float.S @@ -0,0 +1 @@ +%include "mips64/fcmp.S" {"gt_bias":"0"} diff --git a/runtime/interpreter/mterp/mips64/op_const.S b/runtime/interpreter/mterp/mips64/op_const.S new file mode 100644 index 0000000000000000000000000000000000000000..4b0d69b763ba4ce8249d3da814cddc1f5f4c4ed3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const.S @@ -0,0 +1,9 @@ + /* const vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_16.S b/runtime/interpreter/mterp/mips64/op_const_16.S new file mode 100644 index 0000000000000000000000000000000000000000..51e68a7df77d7f58be781af3e144abf79373a6dc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_16.S @@ -0,0 +1,7 @@ + /* const/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_4.S b/runtime/interpreter/mterp/mips64/op_const_4.S new file mode 100644 index 0000000000000000000000000000000000000000..0a58bff7b7d65ac3866cc6e3ea5c8f93360f7509 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_4.S @@ -0,0 +1,8 @@ + /* const/4 vA, #+B */ + ext a2, rINST, 8, 4 # a2 <- A + seh a0, rINST # sign extend B in rINST + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + sra a0, a0, 12 # shift B into its final position + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- +B + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_class.S b/runtime/interpreter/mterp/mips64/op_const_class.S new file mode 100644 index 0000000000000000000000000000000000000000..adf79df38e93cd843d3dd62b5826bca97fd67606 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_class.S @@ -0,0 +1,13 @@ + /* const/class vAA, Class//BBBB */ + .extern MterpConstClass + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstClass # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_high16.S b/runtime/interpreter/mterp/mips64/op_const_high16.S new file mode 100644 index 0000000000000000000000000000000000000000..43effb6f60a2f9b074f022330144e120fc104d7a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_high16.S @@ -0,0 +1,8 @@ + /* const/high16 vAA, #+BBBB0000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + sll a0, a0, 16 # a0 <- BBBB0000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB0000 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_string.S b/runtime/interpreter/mterp/mips64/op_const_string.S new file mode 100644 index 0000000000000000000000000000000000000000..4684c11854fe7e96edad56843e24a77cf97728c6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_string.S @@ -0,0 +1,13 @@ + /* const/string vAA, String//BBBB */ + .extern MterpConstString + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_string_jumbo.S b/runtime/interpreter/mterp/mips64/op_const_string_jumbo.S new file mode 100644 index 0000000000000000000000000000000000000000..47f2101c881e128157a58bba36ff3ab597baf6f5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_string_jumbo.S @@ -0,0 +1,15 @@ + /* const/string vAA, String//BBBBBBBB */ + .extern MterpConstString + EXPORT_PC + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a4, 4(rPC) # a4 <- BBBB (high) + srl a1, rINST, 8 # a1 <- AA + ins a0, a4, 16, 16 # a0 <- BBBBbbbb + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 3 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 3 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide.S b/runtime/interpreter/mterp/mips64/op_const_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..f7eaf7c23110fe5ed61328807b77c59dbcddf6df --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide.S @@ -0,0 +1,13 @@ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + srl a4, rINST, 8 # a4 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (low middle) + lh a2, 6(rPC) # a2 <- hhhh (high middle) + lh a3, 8(rPC) # a3 <- HHHH (high) + FETCH_ADVANCE_INST 5 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + ins a2, a3, 16, 16 # a2 = HHHHhhhh + dinsu a0, a2, 32, 32 # a0 = HHHHhhhhBBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- +HHHHhhhhBBBBbbbb + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide_16.S b/runtime/interpreter/mterp/mips64/op_const_wide_16.S new file mode 100644 index 0000000000000000000000000000000000000000..3a70937973202d341c05b304b276c41eab5b154d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide_16.S @@ -0,0 +1,7 @@ + /* const-wide/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide_32.S b/runtime/interpreter/mterp/mips64/op_const_wide_32.S new file mode 100644 index 0000000000000000000000000000000000000000..867197ce139f90c0122ec8690763a7b055581c35 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide_32.S @@ -0,0 +1,9 @@ + /* const-wide/32 vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_const_wide_high16.S b/runtime/interpreter/mterp/mips64/op_const_wide_high16.S new file mode 100644 index 0000000000000000000000000000000000000000..d741631bcbfc4025079a725ec3dd11edb2c9df82 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_const_wide_high16.S @@ -0,0 +1,8 @@ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + dsll32 a0, a0, 16 # a0 <- BBBB000000000000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB000000000000 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_div_double.S b/runtime/interpreter/mterp/mips64/op_div_double.S new file mode 100644 index 0000000000000000000000000000000000000000..44998f0c29838742f1e1e74f25bfc67592c8e4ee --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"div.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_double_2addr.S b/runtime/interpreter/mterp/mips64/op_div_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..396af798f6c2d849860804a0a71684a74059f118 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"div.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_float.S b/runtime/interpreter/mterp/mips64/op_div_float.S new file mode 100644 index 0000000000000000000000000000000000000000..7b09d52f02e8832d2bc864e14713f0b422e3f592 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"div.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_float_2addr.S b/runtime/interpreter/mterp/mips64/op_div_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..e74fddae6dc56678da44dc5debadf11ef386ddf4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"div.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int.S b/runtime/interpreter/mterp/mips64/op_div_int.S new file mode 100644 index 0000000000000000000000000000000000000000..fb04acbff8a86ffb25f0304634e2f70a99aafdd1 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int_2addr.S b/runtime/interpreter/mterp/mips64/op_div_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..db29b844fb23b4e412fffab95aaa9643b5990a52 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int_lit16.S b/runtime/interpreter/mterp/mips64/op_div_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..e903ddee2c726874ad46bf6ab2546b9271d80983 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_int_lit8.S b/runtime/interpreter/mterp/mips64/op_div_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..055960546fdb1bc71845a045d7b9069d55ae61d5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"div a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_long.S b/runtime/interpreter/mterp/mips64/op_div_long.S new file mode 100644 index 0000000000000000000000000000000000000000..01fc2b281a50e2a9140afaa9caba469fc0a0625a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"ddiv a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_div_long_2addr.S b/runtime/interpreter/mterp/mips64/op_div_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..9627ab8a2487a9b5e8a3d4a4a8e9c9ee5a6dfa2e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_div_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"ddiv a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_double_to_float.S b/runtime/interpreter/mterp/mips64/op_double_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..2b2acee59147f8b8c6eb8482ebfa90dce3bf272f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_double_to_float.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + cvt.s.d f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_double_to_int.S b/runtime/interpreter/mterp/mips64/op_double_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..aa2cbcad38588249180421385a75e54e5d4d5859 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_double_to_int.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, INT_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.d f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.w.d f0, f0 + mfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_double_to_long.S b/runtime/interpreter/mterp/mips64/op_double_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..777cfeb6c803573e68c94c9ce672c97c11e777ab --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_double_to_long.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, LONG_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.d f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.l.d f0, f0 + dmfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_fill_array_data.S b/runtime/interpreter/mterp/mips64/op_fill_array_data.S new file mode 100644 index 0000000000000000000000000000000000000000..c90f0b90ada33d3e6c269c8e1db221542d7570d9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_fill_array_data.S @@ -0,0 +1,14 @@ + /* fill-array-data vAA, +BBBBBBBB */ + .extern MterpFillArrayData + EXPORT_PC + lh a1, 2(rPC) # a1 <- bbbb (lo) + lh a0, 4(rPC) # a0 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a1, a0, 16, 16 # a1 <- BBBBbbbb + GET_VREG_U a0, a3 # a0 <- vAA (array object) + dlsa a1, a1, rPC, 1 # a1 <- PC + BBBBbbbb*2 (array data off.) + jal MterpFillArrayData # (obj, payload) + beqzc v0, MterpPossibleException # exception? + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_filled_new_array.S b/runtime/interpreter/mterp/mips64/op_filled_new_array.S new file mode 100644 index 0000000000000000000000000000000000000000..35f55c27a6d6a02b72aacac56e2c22d7c345122b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_filled_new_array.S @@ -0,0 +1,18 @@ +%default { "helper":"MterpFilledNewArray" } + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class//CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type//BBBB */ + .extern $helper + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rSELF + jal $helper + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_filled_new_array_range.S b/runtime/interpreter/mterp/mips64/op_filled_new_array_range.S new file mode 100644 index 0000000000000000000000000000000000000000..a4e18f68d6d70fe8c9d71fb45865144a6f7eef73 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "mips64/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/mips64/op_float_to_double.S b/runtime/interpreter/mterp/mips64/op_float_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..6accfeeff6e8d76900f271f641f5afd04d523f50 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_float_to_double.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + cvt.d.s f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_float_to_int.S b/runtime/interpreter/mterp/mips64/op_float_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..d957540a7b0b5e1efee5e5f4cda934720e7d64e9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_float_to_int.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, INT_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.s f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.w.s f0, f0 + mfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_float_to_long.S b/runtime/interpreter/mterp/mips64/op_float_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..5d036c8455d3dd06b8699657523838fccd1a0306 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_float_to_long.S @@ -0,0 +1,23 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, LONG_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .L${opcode}_trunc + cmp.eq.s f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .L${opcode}_done +%break +.L${opcode}_trunc: + trunc.l.s f0, f0 + dmfc1 t0, f0 +.L${opcode}_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto.S b/runtime/interpreter/mterp/mips64/op_goto.S new file mode 100644 index 0000000000000000000000000000000000000000..7c7d0ecf5a94a9c156b5b71daeb7d734fb39f1bd --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_goto.S @@ -0,0 +1,25 @@ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + .extern MterpProfileBranch + srl rINST, rINST, 8 + seb rINST, rINST # rINST <- offset (sign-extended AA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_16.S b/runtime/interpreter/mterp/mips64/op_goto_16.S new file mode 100644 index 0000000000000000000000000000000000000000..566e3a78f01660061fb2c61f9a149445f86f9c13 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_goto_16.S @@ -0,0 +1,24 @@ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_goto_32.S b/runtime/interpreter/mterp/mips64/op_goto_32.S new file mode 100644 index 0000000000000000000000000000000000000000..b260083ae81cdbed6d93b2227d0df9af2143ed42 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_goto_32.S @@ -0,0 +1,29 @@ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- aaaa (low) + lh a1, 4(rPC) # a1 <- AAAA (high) + ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_if_eq.S b/runtime/interpreter/mterp/mips64/op_if_eq.S new file mode 100644 index 0000000000000000000000000000000000000000..aa35cadf17dbd00f1a0ff8862e345788ad0f0893 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_eq.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"eq" } diff --git a/runtime/interpreter/mterp/mips64/op_if_eqz.S b/runtime/interpreter/mterp/mips64/op_if_eqz.S new file mode 100644 index 0000000000000000000000000000000000000000..0fe34187a0fb51c86c06b3436b7d36c47ecd7a09 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_eqz.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"eq" } diff --git a/runtime/interpreter/mterp/mips64/op_if_ge.S b/runtime/interpreter/mterp/mips64/op_if_ge.S new file mode 100644 index 0000000000000000000000000000000000000000..59fdcc5b3394f9ed51ddae2ef9d8e44fc4d0b153 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_ge.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"ge" } diff --git a/runtime/interpreter/mterp/mips64/op_if_gez.S b/runtime/interpreter/mterp/mips64/op_if_gez.S new file mode 100644 index 0000000000000000000000000000000000000000..57f1f66ecdfaebe3f00fd54d4d3e3b340586566e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_gez.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"ge" } diff --git a/runtime/interpreter/mterp/mips64/op_if_gt.S b/runtime/interpreter/mterp/mips64/op_if_gt.S new file mode 100644 index 0000000000000000000000000000000000000000..26cc1195b5b35b721517e31ef5ad2ca27e9d0bfc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_gt.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"gt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_gtz.S b/runtime/interpreter/mterp/mips64/op_if_gtz.S new file mode 100644 index 0000000000000000000000000000000000000000..69fcacb82dc07cebfb054b71f87a73e7f4e711f4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_gtz.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"gt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_le.S b/runtime/interpreter/mterp/mips64/op_if_le.S new file mode 100644 index 0000000000000000000000000000000000000000..a7fce17c40750c2a5e1c9374e3dfaa4f7babb530 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_le.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"le" } diff --git a/runtime/interpreter/mterp/mips64/op_if_lez.S b/runtime/interpreter/mterp/mips64/op_if_lez.S new file mode 100644 index 0000000000000000000000000000000000000000..f3edcc6d994efc6b00bb7d6f0f94326badfa7aea --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_lez.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"le" } diff --git a/runtime/interpreter/mterp/mips64/op_if_lt.S b/runtime/interpreter/mterp/mips64/op_if_lt.S new file mode 100644 index 0000000000000000000000000000000000000000..a975a31b57b597b4d3026f160b24c1a52ffffba8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_lt.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"lt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_ltz.S b/runtime/interpreter/mterp/mips64/op_if_ltz.S new file mode 100644 index 0000000000000000000000000000000000000000..c1d730d43f8a504099b1979abb0dd154f6175908 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_ltz.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"lt" } diff --git a/runtime/interpreter/mterp/mips64/op_if_ne.S b/runtime/interpreter/mterp/mips64/op_if_ne.S new file mode 100644 index 0000000000000000000000000000000000000000..f143ee917e2e8a76593191c42ad88b6d24572c2c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_ne.S @@ -0,0 +1 @@ +%include "mips64/bincmp.S" { "condition":"ne" } diff --git a/runtime/interpreter/mterp/mips64/op_if_nez.S b/runtime/interpreter/mterp/mips64/op_if_nez.S new file mode 100644 index 0000000000000000000000000000000000000000..1856b96dbcd8460b71fdb1116b320470ad29fa45 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_if_nez.S @@ -0,0 +1 @@ +%include "mips64/zcmp.S" { "condition":"ne" } diff --git a/runtime/interpreter/mterp/mips64/op_iget.S b/runtime/interpreter/mterp/mips64/op_iget.S new file mode 100644 index 0000000000000000000000000000000000000000..ade4b31b807de7c1a953e8820ad265870ed88135 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget.S @@ -0,0 +1,26 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode"} + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal $helper + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if $is_object + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_boolean.S b/runtime/interpreter/mterp/mips64/op_iget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..cb2c8bef0701123aeba7b41c07a9ea4bc818ed15 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_boolean_quick.S b/runtime/interpreter/mterp/mips64/op_iget_boolean_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..979dc7079e09a9bda6e2ad0699b4e9a75db0d313 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lbu" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_byte.S b/runtime/interpreter/mterp/mips64/op_iget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..099d8d03623f648e2a0cdb804cf9a630c7f51c94 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_byte.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_byte_quick.S b/runtime/interpreter/mterp/mips64/op_iget_byte_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..cb355567216eea69e1a840fc89d40a2ce783a873 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lb" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_char.S b/runtime/interpreter/mterp/mips64/op_iget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..927b7affa68991bd0083a1285c301118c0d0387d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_char.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_char_quick.S b/runtime/interpreter/mterp/mips64/op_iget_char_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..603456775b89e17d81816c24914cc01a0681e62a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lhu" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_object.S b/runtime/interpreter/mterp/mips64/op_iget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..c65855699294f837e09fa6bd01a71bb9f546a792 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_object.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_object_quick.S b/runtime/interpreter/mterp/mips64/op_iget_object_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..171d54301b26f04e704a9662bfe08d97d8dc6d38 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_object_quick.S @@ -0,0 +1,16 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset//CCCC */ + .extern artIGetObjectFromMterp + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- object we're operating on + jal artIGetObjectFromMterp # (obj, offset) + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_quick.S b/runtime/interpreter/mterp/mips64/op_iget_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..fee6ab738c3d4c0775c8af962be4d65082c6d755 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_quick.S @@ -0,0 +1,14 @@ +%default { "load":"lw" } + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + $load a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_short.S b/runtime/interpreter/mterp/mips64/op_iget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..28b5093b6d7cefbef7688e5b67655945af5a51be --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_short.S @@ -0,0 +1 @@ +%include "mips64/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_short_quick.S b/runtime/interpreter/mterp/mips64/op_iget_short_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..6e152dbf48b539bd93040ad73488a1a2a5626910 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iget_quick.S" { "load":"lh" } diff --git a/runtime/interpreter/mterp/mips64/op_iget_wide.S b/runtime/interpreter/mterp/mips64/op_iget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..85cf6705a760f6e802f49ba157ed1a0b69f12235 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_wide.S @@ -0,0 +1,21 @@ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + .extern artGet64InstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGet64InstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_WIDE v0, a2 # fp[A] <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iget_wide_quick.S b/runtime/interpreter/mterp/mips64/op_iget_wide_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..2adc6adf15bfadea294a2946f4a7492a149f8d5a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iget_wide_quick.S @@ -0,0 +1,14 @@ + /* iget-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a4, 2(rPC) # a4 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + daddu a4, a3, a4 # create direct pointer + lw a0, 0(a4) + lw a1, 4(a4) + dinsu a0, a1, 32, 32 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_instance_of.S b/runtime/interpreter/mterp/mips64/op_instance_of.S new file mode 100644 index 0000000000000000000000000000000000000000..39a5dc7c265977e398b85a40ca59dcbb245a6ff7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_instance_of.S @@ -0,0 +1,23 @@ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class//CCCC */ + .extern MterpInstanceOf + EXPORT_PC + lhu a0, 2(rPC) # a0 <- CCCC + srl a1, rINST, 12 # a1 <- B + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpInstanceOf # (index, &obj, method, self) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a1, MterpException + ADVANCE 2 # advance rPC + SET_VREG v0, a2 # vA <- v0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_int_to_byte.S b/runtime/interpreter/mterp/mips64/op_int_to_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..1993e076a6cc2bd5ce4ff16f6fb29d7d647a0ad7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_byte.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"seb a0, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_int_to_char.S b/runtime/interpreter/mterp/mips64/op_int_to_char.S new file mode 100644 index 0000000000000000000000000000000000000000..8f03acd3f60100d225d6d803a1f566c02bd9afde --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_char.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"and a0, a0, 0xffff"} diff --git a/runtime/interpreter/mterp/mips64/op_int_to_double.S b/runtime/interpreter/mterp/mips64/op_int_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..6df71be39486ac707da30852b9df717cf6218361 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_double.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + cvt.d.w f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_int_to_float.S b/runtime/interpreter/mterp/mips64/op_int_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..77e9eba53ac0fd5e6141e9955787b1c9e1f2d4b4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_float.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + cvt.s.w f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_int_to_long.S b/runtime/interpreter/mterp/mips64/op_int_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..7b9ad86fdc8f9b2f427fc0ecf2d2ee03d8d603a3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_long.S @@ -0,0 +1,8 @@ + /* int-to-long vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB (sign-extended to 64 bits) + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_int_to_short.S b/runtime/interpreter/mterp/mips64/op_int_to_short.S new file mode 100644 index 0000000000000000000000000000000000000000..4a3f2346cf8ee49967e6e99eb8f1893ee9994e22 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_int_to_short.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"seh a0, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_invoke_direct.S b/runtime/interpreter/mterp/mips64/op_invoke_direct.S new file mode 100644 index 0000000000000000000000000000000000000000..5047118e48aa7b3a51bb09eefbd1d1a6ba1f0f69 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_direct.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_direct_range.S b/runtime/interpreter/mterp/mips64/op_invoke_direct_range.S new file mode 100644 index 0000000000000000000000000000000000000000..5c9b95f5be6c47cfaa9b64d430d4b1c9438b6d8c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_interface.S b/runtime/interpreter/mterp/mips64/op_invoke_interface.S new file mode 100644 index 0000000000000000000000000000000000000000..ed148adcbb613d34db8001a22547e686e9553f6f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_interface.S @@ -0,0 +1,8 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeInterface" } + /* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/mips64/op_invoke_interface_range.S b/runtime/interpreter/mterp/mips64/op_invoke_interface_range.S new file mode 100644 index 0000000000000000000000000000000000000000..91c231e0f4d0138e1b5a754834f93c7c986d1e0d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_static.S b/runtime/interpreter/mterp/mips64/op_invoke_static.S new file mode 100644 index 0000000000000000000000000000000000000000..44f5cb7a78a6407efd2d1fdf4394c1f0c5f171fb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_static.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeStatic" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_static_range.S b/runtime/interpreter/mterp/mips64/op_invoke_static_range.S new file mode 100644 index 0000000000000000000000000000000000000000..289e5aa97750945bc5d032f50bf05d2bbfe51073 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_super.S b/runtime/interpreter/mterp/mips64/op_invoke_super.S new file mode 100644 index 0000000000000000000000000000000000000000..b13fffe7144ff3893b4b9b9202ffb5decea5786d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_super.S @@ -0,0 +1,8 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeSuper" } + /* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/mips64/op_invoke_super_range.S b/runtime/interpreter/mterp/mips64/op_invoke_super_range.S new file mode 100644 index 0000000000000000000000000000000000000000..350b9757ba9a59cc2ecf9cabf6393ccd535321d2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual.S new file mode 100644 index 0000000000000000000000000000000000000000..0d26cda812a369e7607dc9b4a0744466b5a5ac5b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual.S @@ -0,0 +1,8 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtual" } + /* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..f39562c199cfb937df89466a7139ef3f77b8b02c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual_range.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range.S new file mode 100644 index 0000000000000000000000000000000000000000..0bb43f8fccf68066290bcaf0b844949079646968 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/mips64/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..c4488513bd9e12c4b7d0baf767e7d83c0797f3af --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "mips64/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/mips64/op_iput.S b/runtime/interpreter/mterp/mips64/op_iput.S new file mode 100644 index 0000000000000000000000000000000000000000..a906a0fc82ec4f27c5ee3ccc4dc2a14a318e7edb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput.S @@ -0,0 +1,21 @@ +%default { "helper":"artSet32InstanceFromMterp" } + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal $helper + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_boolean.S b/runtime/interpreter/mterp/mips64/op_iput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..3034fa59d590b8392f39ba25b80f6917127e0b1b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_boolean_quick.S b/runtime/interpreter/mterp/mips64/op_iput_boolean_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..df99948e4032f95628421366f3d1298ac31a2de9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_byte.S b/runtime/interpreter/mterp/mips64/op_iput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..3034fa59d590b8392f39ba25b80f6917127e0b1b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_byte.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_byte_quick.S b/runtime/interpreter/mterp/mips64/op_iput_byte_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..df99948e4032f95628421366f3d1298ac31a2de9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sb" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_char.S b/runtime/interpreter/mterp/mips64/op_iput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..4c2fa2877681a14a5ab6cab60b133684b7c65ce4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_char.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_char_quick.S b/runtime/interpreter/mterp/mips64/op_iput_char_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..a6286b7b970fe268cfb8c78cc4dbb70e22f54582 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_object.S b/runtime/interpreter/mterp/mips64/op_iput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..9a42f5466946b3d3b2ff498bb348550747d0d509 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_object.S @@ -0,0 +1,11 @@ + .extern MterpIputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpIputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_object_quick.S b/runtime/interpreter/mterp/mips64/op_iput_object_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..658ef42a190c466715c968eacbd86baf788b1079 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_object_quick.S @@ -0,0 +1,10 @@ + .extern MterpIputObjectQuick + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpIputObjectQuick + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_quick.S b/runtime/interpreter/mterp/mips64/op_iput_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..b95adfcd4fe8f14d1a7de9981fab3d5b9e2e1ae8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_quick.S @@ -0,0 +1,14 @@ +%default { "store":"sw" } + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + $store a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_short.S b/runtime/interpreter/mterp/mips64/op_iput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..4c2fa2877681a14a5ab6cab60b133684b7c65ce4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_short.S @@ -0,0 +1 @@ +%include "mips64/op_iput.S" { "helper":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_short_quick.S b/runtime/interpreter/mterp/mips64/op_iput_short_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..a6286b7b970fe268cfb8c78cc4dbb70e22f54582 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "mips64/op_iput_quick.S" { "store":"sh" } diff --git a/runtime/interpreter/mterp/mips64/op_iput_wide.S b/runtime/interpreter/mterp/mips64/op_iput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..9b790f812ab87d62be5c48f0527d65b379060c9c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_wide.S @@ -0,0 +1,15 @@ + /* iput-wide vA, vB, field//CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + dlsa a2, a2, rFP, 2 # a2 <- &fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet64InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_iput_wide_quick.S b/runtime/interpreter/mterp/mips64/op_iput_wide_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..95a8ad8f9c14178aee611081f6a27bacf69f99c0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_iput_wide_quick.S @@ -0,0 +1,14 @@ + /* iput-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a3, 2(rPC) # a3 <- field byte offset + GET_VREG_U a2, a2 # a2 <- fp[B], the object pointer + ext a0, rINST, 8, 4 # a0 <- A + beqz a2, common_errNullObject # object was null + GET_VREG_WIDE a0, a0 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a2, a3 # create a direct pointer + sw a0, 0(a1) + dsrl32 a0, a0, 0 + sw a0, 4(a1) + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_long_to_double.S b/runtime/interpreter/mterp/mips64/op_long_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..8503e769b9b6b20a7435203fee0556f178cf56cb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_long_to_double.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + cvt.d.l f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_long_to_float.S b/runtime/interpreter/mterp/mips64/op_long_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..31f5c0e9b02653a342cc090ff6245edac42c747a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_long_to_float.S @@ -0,0 +1,8 @@ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + cvt.s.l f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_long_to_int.S b/runtime/interpreter/mterp/mips64/op_long_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..4ef4b512dce23f3433e0b65eda0bebbe6d8883d9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_long_to_int.S @@ -0,0 +1,2 @@ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +%include "mips64/op_move.S" diff --git a/runtime/interpreter/mterp/mips64/op_monitor_enter.S b/runtime/interpreter/mterp/mips64/op_monitor_enter.S new file mode 100644 index 0000000000000000000000000000000000000000..36ae50346e611bf6297b858763f9d6864feef1b5 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_monitor_enter.S @@ -0,0 +1,14 @@ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + .extern artLockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artLockObjectFromCode + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_monitor_exit.S b/runtime/interpreter/mterp/mips64/op_monitor_exit.S new file mode 100644 index 0000000000000000000000000000000000000000..99459520179587f907eeb0fdd2b24eff28028c85 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_monitor_exit.S @@ -0,0 +1,18 @@ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + .extern artUnlockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artUnlockObjectFromCode # v0 <- success for unlock(self, obj) + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 # before throw: advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move.S b/runtime/interpreter/mterp/mips64/op_move.S new file mode 100644 index 0000000000000000000000000000000000000000..c79f6cde8d4ccc632861a745bb3371fb2b047351 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_16.S b/runtime/interpreter/mterp/mips64/op_move_16.S new file mode 100644 index 0000000000000000000000000000000000000000..9d5c4dce8cf2a60af2980f21c2858cc1617610da --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vAAAA <- vBBBB + .else + SET_VREG a0, a2 # vAAAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_exception.S b/runtime/interpreter/mterp/mips64/op_move_exception.S new file mode 100644 index 0000000000000000000000000000000000000000..d226718c8f67cf80f40cc370a086d6615a239fe8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_exception.S @@ -0,0 +1,8 @@ + /* move-exception vAA */ + srl a2, rINST, 8 # a2 <- AA + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) # load exception obj + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + SET_VREG_OBJECT a0, a2 # vAA <- exception obj + GET_INST_OPCODE v0 # extract opcode from rINST + sd zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_from16.S b/runtime/interpreter/mterp/mips64/op_move_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..6d6bde007fd6a36dd0a64c68f92ab119abe87f5e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_from16.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vAA <- vBBBB + .else + SET_VREG a0, a2 # vAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_object.S b/runtime/interpreter/mterp/mips64/op_move_object.S new file mode 100644 index 0000000000000000000000000000000000000000..47e0272a6c4c22f7c5d47705e7bb23cb8f0dc19e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_object.S @@ -0,0 +1 @@ +%include "mips64/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_object_16.S b/runtime/interpreter/mterp/mips64/op_move_object_16.S new file mode 100644 index 0000000000000000000000000000000000000000..a777dcdaf863b807388dcc3dcc7c28bee1c976f3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_object_16.S @@ -0,0 +1 @@ +%include "mips64/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_object_from16.S b/runtime/interpreter/mterp/mips64/op_move_object_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..ab55ebd646f5b916ff150d5785c095923297017a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_object_from16.S @@ -0,0 +1 @@ +%include "mips64/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_result.S b/runtime/interpreter/mterp/mips64/op_move_result.S new file mode 100644 index 0000000000000000000000000000000000000000..1ec28cb6d8820be81703e18cf90cfcc4a2815118 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_result.S @@ -0,0 +1,14 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE v0 # extract opcode from rINST + .if $is_object + SET_VREG_OBJECT a0, a2 # vAA <- result + .else + SET_VREG a0, a2 # vAA <- result + .endif + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_result_object.S b/runtime/interpreter/mterp/mips64/op_move_result_object.S new file mode 100644 index 0000000000000000000000000000000000000000..e76bc22c1124d42304dd88fa69b256ca6e2b55c3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_result_object.S @@ -0,0 +1 @@ +%include "mips64/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_move_result_wide.S b/runtime/interpreter/mterp/mips64/op_move_result_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..3ba0d7288b588cfe2f6a320f23989671e19942c4 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_result_wide.S @@ -0,0 +1,9 @@ + /* for: move-result-wide */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + ld a0, 0(a0) # a0 <- result.j + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_wide.S b/runtime/interpreter/mterp/mips64/op_move_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..ea23f87ff025daee4e3c3434bf16e0e10290a09f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_wide.S @@ -0,0 +1,9 @@ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + ext a3, rINST, 12, 4 # a3 <- B + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG_WIDE a0, a3 # a0 <- vB + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_wide_16.S b/runtime/interpreter/mterp/mips64/op_move_wide_16.S new file mode 100644 index 0000000000000000000000000000000000000000..8ec606834b9d0f5d6efb2cc2502ea13115e1bbb6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_wide_16.S @@ -0,0 +1,9 @@ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAAAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_move_wide_from16.S b/runtime/interpreter/mterp/mips64/op_move_wide_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..11d5603fe135b6cb33637ead73d3910fb8e7d8f3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_move_wide_from16.S @@ -0,0 +1,9 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_mul_double.S b/runtime/interpreter/mterp/mips64/op_mul_double.S new file mode 100644 index 0000000000000000000000000000000000000000..e7e17f7ece518e0fb02652b3c4bcb0f798365fe2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"mul.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_double_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..f404d4688d3fbcc2ea79fc128bda136e7c5c42ed --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"mul.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_float.S b/runtime/interpreter/mterp/mips64/op_mul_float.S new file mode 100644 index 0000000000000000000000000000000000000000..9a695fca1682dd0a211cfee501e584e61240e70b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"mul.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_float_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..a134a34253be231c0e1e58885611297035fc2bf7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"mul.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int.S b/runtime/interpreter/mterp/mips64/op_mul_int.S new file mode 100644 index 0000000000000000000000000000000000000000..e1b90ff4e920429e85f1c35f0fe33fb7a57cc830 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..c0c4063d548ed1c9ea90e54a3369125db8df17d6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int_lit16.S b/runtime/interpreter/mterp/mips64/op_mul_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..bb4fff874703c0832692252e10e203007a197e57 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_int_lit8.S b/runtime/interpreter/mterp/mips64/op_mul_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..da11ea9295738537ecf2ce06d8d0602fdd506f5b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"mul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_long.S b/runtime/interpreter/mterp/mips64/op_mul_long.S new file mode 100644 index 0000000000000000000000000000000000000000..ec3285060632eb4a9e899f6df113cded816ea898 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dmul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_mul_long_2addr.S b/runtime/interpreter/mterp/mips64/op_mul_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..eb50cda03ce18ee3e856063642f0e95e6914b4c3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_mul_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dmul a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_neg_double.S b/runtime/interpreter/mterp/mips64/op_neg_double.S new file mode 100644 index 0000000000000000000000000000000000000000..a135d611730fe62fb785af5d59f18f6aa2a61a77 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_double.S @@ -0,0 +1,3 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_DOUBLE", "valreg":"f0" } + neg.d f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_DOUBLE", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_neg_float.S b/runtime/interpreter/mterp/mips64/op_neg_float.S new file mode 100644 index 0000000000000000000000000000000000000000..78019f03d8ef872724a2d98ba896e29adab6ca4b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_float.S @@ -0,0 +1,3 @@ +%include "mips64/fcvtHeader.S" { "suffix":"_FLOAT", "valreg":"f0" } + neg.s f0, f0 +%include "mips64/fcvtFooter.S" { "suffix":"_FLOAT", "valreg":"f0" } diff --git a/runtime/interpreter/mterp/mips64/op_neg_int.S b/runtime/interpreter/mterp/mips64/op_neg_int.S new file mode 100644 index 0000000000000000000000000000000000000000..31538c0caa2cf5ef1c4fc76be261f3be1af1160a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_int.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"subu a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_neg_long.S b/runtime/interpreter/mterp/mips64/op_neg_long.S new file mode 100644 index 0000000000000000000000000000000000000000..bc80d0623f19576d16c7419d7750e8dae9d0ec1d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_neg_long.S @@ -0,0 +1 @@ +%include "mips64/unopWide.S" {"instr":"dsubu a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_new_array.S b/runtime/interpreter/mterp/mips64/op_new_array.S new file mode 100644 index 0000000000000000000000000000000000000000..d78b4ac32ef4a0024cba32559fb6f1cf55641250 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_new_array.S @@ -0,0 +1,19 @@ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class//CCCC */ + .extern MterpNewArray + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpNewArray + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_new_instance.S b/runtime/interpreter/mterp/mips64/op_new_instance.S new file mode 100644 index 0000000000000000000000000000000000000000..cc5e13e00d201aeefc2c5f17b42f5b628ed8707d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_new_instance.S @@ -0,0 +1,14 @@ + /* + * Create a new instance of a class. + */ + /* new-instance vAA, class//BBBB */ + .extern MterpNewInstance + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + jal MterpNewInstance # (shadow_frame, self, inst_data) + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_nop.S b/runtime/interpreter/mterp/mips64/op_nop.S new file mode 100644 index 0000000000000000000000000000000000000000..cc803a791a6e3100800aca2bbe461c2c78aa5f0c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_nop.S @@ -0,0 +1,3 @@ + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_not_int.S b/runtime/interpreter/mterp/mips64/op_not_int.S new file mode 100644 index 0000000000000000000000000000000000000000..59540950cdb0efc5008007ef5775e7e9d2a038f6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_not_int.S @@ -0,0 +1 @@ +%include "mips64/unop.S" {"instr":"nor a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_not_long.S b/runtime/interpreter/mterp/mips64/op_not_long.S new file mode 100644 index 0000000000000000000000000000000000000000..c8f5da7e82aff11647de579c125e7a9d11e1c773 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_not_long.S @@ -0,0 +1 @@ +%include "mips64/unopWide.S" {"instr":"nor a0, zero, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int.S b/runtime/interpreter/mterp/mips64/op_or_int.S new file mode 100644 index 0000000000000000000000000000000000000000..0102355c55d137d4b69f46e289f247056fa4d368 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int_2addr.S b/runtime/interpreter/mterp/mips64/op_or_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..eed89008c048dc2e084b201d6720c868b507431e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int_lit16.S b/runtime/interpreter/mterp/mips64/op_or_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..16a0f3e1a22f7847bf963ed74b108fee4fade095 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_int_lit8.S b/runtime/interpreter/mterp/mips64/op_or_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..dbbf7904c6a4a8b424d8cb557e931ecc78bd0a3f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_long.S b/runtime/interpreter/mterp/mips64/op_or_long.S new file mode 100644 index 0000000000000000000000000000000000000000..e6f8639e5258e71488d41cf57e70152124031557 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_or_long_2addr.S b/runtime/interpreter/mterp/mips64/op_or_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ad5e6c8e993d1625606df57ec4306a1a6a5017d0 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"or a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_packed_switch.S b/runtime/interpreter/mterp/mips64/op_packed_switch.S new file mode 100644 index 0000000000000000000000000000000000000000..2c6eb2f3caf78726b8dd33ada92e36d352254892 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_packed_switch.S @@ -0,0 +1,36 @@ +%default { "func":"MterpDoPackedSwitch" } + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBBBBBB */ + .extern $func + .extern MterpProfileBranch + lh a0, 2(rPC) # a0 <- bbbb (lo) + lh a1, 4(rPC) # a1 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a0, a1, 16, 16 # a0 <- BBBBbbbb + GET_VREG a1, a3 # a1 <- vAA + dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 + jal $func # v0 <- code-unit branch offset + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_double.S b/runtime/interpreter/mterp/mips64/op_rem_double.S new file mode 100644 index 0000000000000000000000000000000000000000..ba61cfdc71a12078072b52ea13108e8aeda41086 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_double.S @@ -0,0 +1,12 @@ + /* rem-double vAA, vBB, vCC */ + .extern fmod + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f12, a2 # f12 <- vBB + GET_VREG_DOUBLE f13, a3 # f13 <- vCC + jal fmod # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_double_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..c649f0d62cef2139ed0c7a4a8ad7210658088f26 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_double_2addr.S @@ -0,0 +1,12 @@ + /* rem-double/2addr vA, vB */ + .extern fmod + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f12, a2 # f12 <- vA + GET_VREG_DOUBLE f13, a3 # f13 <- vB + jal fmod # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_float.S b/runtime/interpreter/mterp/mips64/op_rem_float.S new file mode 100644 index 0000000000000000000000000000000000000000..3967b0b02c2cfa73dee3e2290efad02419b58542 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_float.S @@ -0,0 +1,12 @@ + /* rem-float vAA, vBB, vCC */ + .extern fmodf + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f12, a2 # f12 <- vBB + GET_VREG_FLOAT f13, a3 # f13 <- vCC + jal fmodf # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_float_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..3fed41e8510985d1fbd2c4b9959a3977ae81f266 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_float_2addr.S @@ -0,0 +1,12 @@ + /* rem-float/2addr vA, vB */ + .extern fmodf + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f12, a2 # f12 <- vA + GET_VREG_FLOAT f13, a3 # f13 <- vB + jal fmodf # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_rem_int.S b/runtime/interpreter/mterp/mips64/op_rem_int.S new file mode 100644 index 0000000000000000000000000000000000000000..c05e9c49fcf0b3ccbfc03743640e244f013a6207 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_int_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..a4e162d3fa0d9a130b18bc959a4b1c4859584121 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_int_lit16.S b/runtime/interpreter/mterp/mips64/op_rem_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..3284f1473c7de5e54c0a3ca04546bcbe9684973b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_int_lit8.S b/runtime/interpreter/mterp/mips64/op_rem_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..1e6a584be52af5da3fb60b7c8144c5eed2fa82f9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"mod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_long.S b/runtime/interpreter/mterp/mips64/op_rem_long.S new file mode 100644 index 0000000000000000000000000000000000000000..32b2d1916dcde7cb454c4db4de4891c39b92210d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dmod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_rem_long_2addr.S b/runtime/interpreter/mterp/mips64/op_rem_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ad658e1fde096e23b714004dd9517a02c63d2497 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dmod a0, a0, a1", "chkzero":"1"} diff --git a/runtime/interpreter/mterp/mips64/op_return.S b/runtime/interpreter/mterp/mips64/op_return.S new file mode 100644 index 0000000000000000000000000000000000000000..ec986b80667b1f75bd2650fc49af818718f11b3f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return.S @@ -0,0 +1,18 @@ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_return_object.S b/runtime/interpreter/mterp/mips64/op_return_object.S new file mode 100644 index 0000000000000000000000000000000000000000..67f1871e3d503015e4e92e69e72978c2005be525 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_object.S @@ -0,0 +1 @@ +%include "mips64/op_return.S" diff --git a/runtime/interpreter/mterp/mips64/op_return_void.S b/runtime/interpreter/mterp/mips64/op_return_void.S new file mode 100644 index 0000000000000000000000000000000000000000..05253aea05f7d7ab6664972ac8d8a961e01b23c7 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_void.S @@ -0,0 +1,11 @@ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S new file mode 100644 index 0000000000000000000000000000000000000000..f67e811c70eeacc36d6f47544c12e6adacc8b8f8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_void_no_barrier.S @@ -0,0 +1,9 @@ + .extern MterpSuspendCheck + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_return_wide.S b/runtime/interpreter/mterp/mips64/op_return_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..544e02794ddb7242df38295127edc8e6131c367c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_return_wide.S @@ -0,0 +1,17 @@ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vAA + b MterpReturn diff --git a/runtime/interpreter/mterp/mips64/op_rsub_int.S b/runtime/interpreter/mterp/mips64/op_rsub_int.S new file mode 100644 index 0000000000000000000000000000000000000000..fa31a0af5fa14c92ca6b4277d04c05e2412050f3 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rsub_int.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_rsub_int_lit8.S b/runtime/interpreter/mterp/mips64/op_rsub_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..c31ff32060ebf13c62b787a5f88ade40c00f7914 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"subu a0, a1, a0"} diff --git a/runtime/interpreter/mterp/mips64/op_sget.S b/runtime/interpreter/mterp/mips64/op_sget.S new file mode 100644 index 0000000000000000000000000000000000000000..bd2cfe377824644bd25b49fe34dcf7a0207842f9 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget.S @@ -0,0 +1,26 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode", "extend":"" } + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal $helper + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + $extend + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if $is_object + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 diff --git a/runtime/interpreter/mterp/mips64/op_sget_boolean.S b/runtime/interpreter/mterp/mips64/op_sget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..e7b1844d869beb84b75cd1528b38f408b066e1c8 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetBooleanStaticFromCode", "extend":"and v0, v0, 0xff"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_byte.S b/runtime/interpreter/mterp/mips64/op_sget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..52a2e4a5d5a2039abee3451a6e1eb48f2e598b17 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_byte.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetByteStaticFromCode", "extend":"seb v0, v0"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_char.S b/runtime/interpreter/mterp/mips64/op_sget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..873d82a0d6a701c48af91ad5bc6c89f94c5235ae --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_char.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetCharStaticFromCode", "extend":"and v0, v0, 0xffff"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_object.S b/runtime/interpreter/mterp/mips64/op_sget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..3108417e00aebb86016b51f8ef57e4310a97a895 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_object.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_short.S b/runtime/interpreter/mterp/mips64/op_sget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..fed4e76baa2460f97c0dc5fa0e4ca05176369d34 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_short.S @@ -0,0 +1 @@ +%include "mips64/op_sget.S" {"helper":"artGetShortStaticFromCode", "extend":"seh v0, v0"} diff --git a/runtime/interpreter/mterp/mips64/op_sget_wide.S b/runtime/interpreter/mterp/mips64/op_sget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..77124d1d8de600722e3e830a1e89552b13014eeb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sget_wide.S @@ -0,0 +1,18 @@ + /* + * SGET_WIDE handler wrapper. + * + */ + /* sget-wide vAA, field//BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGet64StaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + bnez a3, MterpException # bail out + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE v0, a4 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_shl_int.S b/runtime/interpreter/mterp/mips64/op_shl_int.S new file mode 100644 index 0000000000000000000000000000000000000000..784481f335df310a6e90760bb53d9b52690c7130 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_int_2addr.S b/runtime/interpreter/mterp/mips64/op_shl_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..a6c8a78ff683eccdd6bc9a43f05a912528bd8176 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_int_lit8.S b/runtime/interpreter/mterp/mips64/op_shl_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..36ef207edf94e456a97c540c6b3715ffd5dea981 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"sll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_long.S b/runtime/interpreter/mterp/mips64/op_shl_long.S new file mode 100644 index 0000000000000000000000000000000000000000..225a2cbc2a945895c9cce3d7302afb48a6c9e1dc --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shl_long_2addr.S b/runtime/interpreter/mterp/mips64/op_shl_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..c04d8823f4fb4a352f9b9270f9ae47a5d2279f0a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shl_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsll a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_int.S b/runtime/interpreter/mterp/mips64/op_shr_int.S new file mode 100644 index 0000000000000000000000000000000000000000..eded0373b14c04100c0e50f6d6fdf3d0e55bcc11 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_int_2addr.S b/runtime/interpreter/mterp/mips64/op_shr_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..5b4d96f187bb64837999bb68874c22bcf44e1ec2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_int_lit8.S b/runtime/interpreter/mterp/mips64/op_shr_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..175eb8633af6e62b02f0a9df00a19abb3b502456 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"sra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_long.S b/runtime/interpreter/mterp/mips64/op_shr_long.S new file mode 100644 index 0000000000000000000000000000000000000000..0db38c8510f4cf90ed4d8cc5052e81aa433f9e6b --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_shr_long_2addr.S b/runtime/interpreter/mterp/mips64/op_shr_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..48131ad7e47b7c93f3d7e7a5b32a098d2ae26c93 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_shr_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsra a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sparse_switch.S b/runtime/interpreter/mterp/mips64/op_sparse_switch.S new file mode 100644 index 0000000000000000000000000000000000000000..b065aaa95b3f915f2ecdf30b5ae52e47b2bce475 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sparse_switch.S @@ -0,0 +1 @@ +%include "mips64/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/mips64/op_sput.S b/runtime/interpreter/mterp/mips64/op_sput.S new file mode 100644 index 0000000000000000000000000000000000000000..142f18f3ba27973a458a4b820b22a29d944f2e69 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput.S @@ -0,0 +1,20 @@ +%default { "helper":"artSet32StaticFromCode" } + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern $helper + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal $helper + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_sput_boolean.S b/runtime/interpreter/mterp/mips64/op_sput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..f5b8dbf4336f21c1a0097323635c77c3e44761e2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_boolean.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_byte.S b/runtime/interpreter/mterp/mips64/op_sput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..f5b8dbf4336f21c1a0097323635c77c3e44761e2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_byte.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_char.S b/runtime/interpreter/mterp/mips64/op_sput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..c4d195c82f05201697fa469ab965775c536725aa --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_char.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_object.S b/runtime/interpreter/mterp/mips64/op_sput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..ef4c685116eef19affd378669b15b622979c2b66 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_object.S @@ -0,0 +1,11 @@ + .extern MterpSputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpSputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_sput_short.S b/runtime/interpreter/mterp/mips64/op_sput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..c4d195c82f05201697fa469ab965775c536725aa --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_short.S @@ -0,0 +1 @@ +%include "mips64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/mips64/op_sput_wide.S b/runtime/interpreter/mterp/mips64/op_sput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..828ddc15e7bafe6b5ed0b03aab1f9fd59f0fcbc6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sput_wide.S @@ -0,0 +1,18 @@ + /* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field//BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + srl a2, rINST, 8 # a2 <- AA + dlsa a2, a2, rFP, 2 + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet64IndirectStaticFromMterp + bnezc v0, MterpException # 0 on success, -1 on failure + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/op_sub_double.S b/runtime/interpreter/mterp/mips64/op_sub_double.S new file mode 100644 index 0000000000000000000000000000000000000000..40a6c89a1002f8f61a418bb6ef4a830d666275a6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_double.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide.S" {"instr":"sub.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_double_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..984737e553a81fc67f8e6edd0754fe5bf91f79fb --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinopWide2addr.S" {"instr":"sub.d f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_float.S b/runtime/interpreter/mterp/mips64/op_sub_float.S new file mode 100644 index 0000000000000000000000000000000000000000..9010592116aba7e6afefccc0eab60654eb45fb90 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_float.S @@ -0,0 +1 @@ +%include "mips64/fbinop.S" {"instr":"sub.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_float_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..e7d4ffe1ae957bbede8d44d875039464f276928e --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "mips64/fbinop2addr.S" {"instr":"sub.s f0, f0, f1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_int.S b/runtime/interpreter/mterp/mips64/op_sub_int.S new file mode 100644 index 0000000000000000000000000000000000000000..609ea0575dad3ec04beb83ab1f6f542dfa8adb0d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_int_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ba2f1e875b6f10724015491e85d6d80a2d72923d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"subu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_long.S b/runtime/interpreter/mterp/mips64/op_sub_long.S new file mode 100644 index 0000000000000000000000000000000000000000..09a6afd26ec9756b29aaf30191d29fb125220aa6 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsubu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_sub_long_2addr.S b/runtime/interpreter/mterp/mips64/op_sub_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..b9ec82a19b782e631cc97bdc460208e48e11f914 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_sub_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsubu a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_throw.S b/runtime/interpreter/mterp/mips64/op_throw.S new file mode 100644 index 0000000000000000000000000000000000000000..6418d57ecc1e17840e3d7effe9b893f9b14f62b2 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_throw.S @@ -0,0 +1,10 @@ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (exception object) + beqzc a0, common_errNullObject + sd a0, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException diff --git a/runtime/interpreter/mterp/mips64/op_unused_3e.S b/runtime/interpreter/mterp/mips64/op_unused_3e.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_3e.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_3f.S b/runtime/interpreter/mterp/mips64/op_unused_3f.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_3f.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_40.S b/runtime/interpreter/mterp/mips64/op_unused_40.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_40.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_41.S b/runtime/interpreter/mterp/mips64/op_unused_41.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_41.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_42.S b/runtime/interpreter/mterp/mips64/op_unused_42.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_42.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_43.S b/runtime/interpreter/mterp/mips64/op_unused_43.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_43.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_79.S b/runtime/interpreter/mterp/mips64/op_unused_79.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_79.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_7a.S b/runtime/interpreter/mterp/mips64/op_unused_7a.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_7a.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_f4.S b/runtime/interpreter/mterp/mips64/op_unused_f4.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_f4.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fa.S b/runtime/interpreter/mterp/mips64/op_unused_fa.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fa.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fb.S b/runtime/interpreter/mterp/mips64/op_unused_fb.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fb.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fc.S b/runtime/interpreter/mterp/mips64/op_unused_fc.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fc.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fd.S b/runtime/interpreter/mterp/mips64/op_unused_fd.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fd.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_fe.S b/runtime/interpreter/mterp/mips64/op_unused_fe.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_fe.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_unused_ff.S b/runtime/interpreter/mterp/mips64/op_unused_ff.S new file mode 100644 index 0000000000000000000000000000000000000000..29463d73fc3694e352e27c79541e24c6ea1c4e1c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_unused_ff.S @@ -0,0 +1 @@ +%include "mips64/unused.S" diff --git a/runtime/interpreter/mterp/mips64/op_ushr_int.S b/runtime/interpreter/mterp/mips64/op_ushr_int.S new file mode 100644 index 0000000000000000000000000000000000000000..37c90cb7ec4db6c72c6eafc5fb0a6c2b5747252c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_int_2addr.S b/runtime/interpreter/mterp/mips64/op_ushr_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..d6bf4135dc5e6f4d8e55ad4051c2f3c8d6b22b4f --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_int_lit8.S b/runtime/interpreter/mterp/mips64/op_ushr_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..2a2d843c8aadf3d149abe56368ee652c39325c14 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"srl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_long.S b/runtime/interpreter/mterp/mips64/op_ushr_long.S new file mode 100644 index 0000000000000000000000000000000000000000..e724405f1f9bf9251021429f9c69bb9fac95d233 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"dsrl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_ushr_long_2addr.S b/runtime/interpreter/mterp/mips64/op_ushr_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..d2cf1355666e81758e5e784e926258fa75d2ef21 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_ushr_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"dsrl a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int.S b/runtime/interpreter/mterp/mips64/op_xor_int.S new file mode 100644 index 0000000000000000000000000000000000000000..ee25ebc925375dd34097b592989c1394625de66d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int.S @@ -0,0 +1 @@ +%include "mips64/binop.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int_2addr.S b/runtime/interpreter/mterp/mips64/op_xor_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..0f0496729aae7f18c3fbc2fbc2fa128f4583323c --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "mips64/binop2addr.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int_lit16.S b/runtime/interpreter/mterp/mips64/op_xor_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..ecb21aee07e1c5e56cc18831b298f7a5ba244c88 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "mips64/binopLit16.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_int_lit8.S b/runtime/interpreter/mterp/mips64/op_xor_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..115ae99917c06474806c37262f4521f92242a62d --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "mips64/binopLit8.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_long.S b/runtime/interpreter/mterp/mips64/op_xor_long.S new file mode 100644 index 0000000000000000000000000000000000000000..7ebabc2710bc131e83e0105a6a6da32b6733ac30 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_long.S @@ -0,0 +1 @@ +%include "mips64/binopWide.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/op_xor_long_2addr.S b/runtime/interpreter/mterp/mips64/op_xor_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..0f1919a21efcb34c641c8418f9ac59d14fc940af --- /dev/null +++ b/runtime/interpreter/mterp/mips64/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "mips64/binopWide2addr.S" {"instr":"xor a0, a0, a1"} diff --git a/runtime/interpreter/mterp/mips64/unop.S b/runtime/interpreter/mterp/mips64/unop.S new file mode 100644 index 0000000000000000000000000000000000000000..e3f7ea0eda286915f2ba2ad5f3565f6082f1cb6a --- /dev/null +++ b/runtime/interpreter/mterp/mips64/unop.S @@ -0,0 +1,18 @@ +%default {"preinstr":""} + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + $preinstr # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $instr # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/unopWide.S b/runtime/interpreter/mterp/mips64/unopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..c0dd1aa1d3e2c23722293974222ac23ff1d827ed --- /dev/null +++ b/runtime/interpreter/mterp/mips64/unopWide.S @@ -0,0 +1,17 @@ +%default {"preinstr":""} + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * For: not-long, neg-long + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + $preinstr # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + $instr # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mips64/unused.S b/runtime/interpreter/mterp/mips64/unused.S new file mode 100644 index 0000000000000000000000000000000000000000..30d38bd6cdcdde3096d36fbddfb6760de2ea9781 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback diff --git a/runtime/interpreter/mterp/mips64/zcmp.S b/runtime/interpreter/mterp/mips64/zcmp.S new file mode 100644 index 0000000000000000000000000000000000000000..0e0477fadf8cb0bef1be618edfca8a23bb52e758 --- /dev/null +++ b/runtime/interpreter/mterp/mips64/zcmp.S @@ -0,0 +1,30 @@ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + .extern MterpProfileBranch + srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) + GET_VREG a0, a2 # a0 <- vAA + b${condition}zc a0, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 8f4741c3ef539bddf0488456eb63c44b73899269..10b19c5f4f37d1ae5653a32af3570b2c1cc6af38 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -147,14 +147,7 @@ extern "C" bool MterpShouldSwitchInterpreters() SHARED_REQUIRES(Locks::mutator_lock_) { const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation(); - bool unhandled_instrumentation; - // TODO: enable for other targets after more extensive testing. - if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm)) { - unhandled_instrumentation = instrumentation->NonJitProfilingActive(); - } else { - unhandled_instrumentation = instrumentation->IsActive(); - } - return unhandled_instrumentation || Dbg::IsDebuggerActive(); + return instrumentation->NonJitProfilingActive() || Dbg::IsDebuggerActive(); } diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 94cbd2d10eaff999524f3f51b5914a91fc2d33d9..092474d5442d886c32106014e40a57dd0770b215 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -343,8 +343,8 @@ ExecuteMterpImpl: /* set up "named" registers */ mov rSELF, r0 ldr r0, [r2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to insns[] (i.e. - the dalivk byte code). - add rREFS, rFP, r0, lsl #2 @ point to reference array in shadow frame + add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to vregs. + VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, #CODEITEM_INSNS_OFFSET @ Point to base of insns[] add rPC, rPC, r0, lsl #1 @ Create direct pointer to 1st dex opcode @@ -435,8 +435,8 @@ artMterpAsmInstructionStart = .L_op_nop /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- fp[B] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -452,8 +452,8 @@ artMterpAsmInstructionStart = .L_op_nop /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 1 @ r3<- BBBB mov rINST, rINST, lsr #8 @ rINST<- AA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 2 @ advance rPC, load rINST @@ -469,8 +469,8 @@ artMterpAsmInstructionStart = .L_op_nop /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ FETCH r3, 2 @ r3<- BBBB FETCH r2, 1 @ r2<- AAAA - add r3, rFP, r3, lsl #2 @ r3<- &fp[BBBB] - add lr, rFP, r2, lsl #2 @ r2<- &fp[AAAA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[BBBB] + VREG_INDEX_TO_ADDR lr, r2 @ r2<- &fp[AAAA] ldmia r3, {r0-r1} @ r0/r1<- fp[BBBB] FETCH_ADVANCE_INST 3 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r2, r3, ip @ Zero out the shadow regs @@ -563,7 +563,7 @@ artMterpAsmInstructionStart = .L_op_nop /* move-result-wide vAA */ mov rINST, rINST, lsr #8 @ rINST<- AA ldr r3, [rFP, #OFF_FP_RESULT_REGISTER] - add r2, rFP, rINST, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, rINST @ r2<- &fp[AA] ldmia r3, {r0-r1} @ r0/r1<- retval.j CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero out the shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -655,7 +655,7 @@ artMterpAsmInstructionStart = .L_op_nop ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA - add r2, rFP, r2, lsl #2 @ r2<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[AA] ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1 b MterpReturn @@ -687,10 +687,9 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_4: /* 0x12 */ /* File: arm/op_const_4.S */ /* const/4 vA, #+B */ - mov r1, rINST, lsl #16 @ r1<- Bxxx0000 + sbfx r1, rINST, #12, #4 @ r1<- sssssssB (sign-extended) ubfx r0, rINST, #8, #4 @ r0<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - mov r1, r1, asr #28 @ r1<- sssssssB (sign-extended) GET_INST_OPCODE ip @ ip<- opcode from rINST SET_VREG r1, r0 @ fp[A]<- r1 GOTO_OPCODE ip @ execute next instruction @@ -700,7 +699,7 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_16: /* 0x13 */ /* File: arm/op_const_16.S */ /* const/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA FETCH_ADVANCE_INST 2 @ advance rPC, load rINST SET_VREG r0, r3 @ vAA<- r0 @@ -713,8 +712,8 @@ artMterpAsmInstructionStart = .L_op_nop /* File: arm/op_const.S */ /* const vAA, #+BBBBbbbb */ mov r3, rINST, lsr #8 @ r3<- AA - FETCH r0, 1 @ r0<- bbbb (low - FETCH r1, 2 @ r1<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r1, 2 @ r1<- BBBB (high) FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r1, lsl #16 @ r0<- BBBBbbbb GET_INST_OPCODE ip @ extract opcode from rINST @@ -726,7 +725,7 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_high16: /* 0x15 */ /* File: arm/op_const_high16.S */ /* const/high16 vAA, #+BBBB0000 */ - FETCH r0, 1 @ r0<- 0000BBBB (zero-extended + FETCH r0, 1 @ r0<- 0000BBBB (zero-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r0, r0, lsl #16 @ r0<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST @@ -739,12 +738,12 @@ artMterpAsmInstructionStart = .L_op_nop .L_op_const_wide_16: /* 0x16 */ /* File: arm/op_const_wide_16.S */ /* const-wide/16 vAA, #+BBBB */ - FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended + FETCH_S r0, 1 @ r0<- ssssBBBB (sign-extended) mov r3, rINST, lsr #8 @ r3<- AA mov r1, r0, asr #31 @ r1<- ssssssss FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -760,7 +759,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH_ADVANCE_INST 3 @ advance rPC, load rINST orr r0, r0, r2, lsl #16 @ r0<- BBBBbbbb CLEAR_SHADOW_PAIR r3, r2, lr @ Zero out the shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] mov r1, r0, asr #31 @ r1<- ssssssss GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 @@ -780,7 +779,7 @@ artMterpAsmInstructionStart = .L_op_nop orr r1, r2, r3, lsl #16 @ r1<- HHHHhhhh (high word) CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs FETCH_ADVANCE_INST 5 @ advance rPC, load rINST - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -796,7 +795,7 @@ artMterpAsmInstructionStart = .L_op_nop mov r1, r1, lsl #16 @ r1<- BBBB0000 FETCH_ADVANCE_INST 2 @ advance rPC, load rINST CLEAR_SHADOW_PAIR r3, r0, r2 @ Zero shadow regs - add r3, rFP, r3, lsl #2 @ r3<- &fp[AA] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ vAA<- r0/r1 GOTO_OPCODE ip @ jump to next instruction @@ -825,8 +824,8 @@ artMterpAsmInstructionStart = .L_op_nop /* File: arm/op_const_string_jumbo.S */ /* const/string vAA, String@BBBBBBBB */ EXPORT_PC - FETCH r0, 1 @ r0<- bbbb (low - FETCH r2, 2 @ r2<- BBBB (high + FETCH r0, 1 @ r0<- bbbb (low) + FETCH r2, 2 @ r2<- BBBB (high) mov r1, rINST, lsr #8 @ r1<- AA orr r0, r0, r2, lsl #16 @ r1<- BBBBbbbb add r2, rFP, #OFF_FP_SHADOWFRAME @@ -938,10 +937,9 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r1, r1 @ r1<- &object ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- method mov r3, rSELF @ r3<- self - mov r9, rINST, lsr #8 @ r9<- A+ - and r9, r9, #15 @ r9<- A bl MterpInstanceOf @ (index, &obj, method, self) ldr r1, [rSELF, #THREAD_EXCEPTION_OFFSET] + ubfx r9, rINST, #8, #4 @ r9<- A PREFETCH_INST 2 cmp r1, #0 @ exception pending? bne MterpException @@ -1353,7 +1351,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -1392,7 +1390,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC flds s0, [r2] @ s0<- vBB flds s1, [r3] @ s1<- vCC - fcmpes s0, s1 @ compare (vBB, vCC) + vcmpe.f32 s0, s1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -1431,7 +1429,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mvn r0, #0 @ r0<- -1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -1470,7 +1468,7 @@ artMterpAsmInstructionStart = .L_op_nop VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vCC fldd d0, [r2] @ d0<- vBB fldd d1, [r3] @ d1<- vCC - fcmped d0, d1 @ compare (vBB, vCC) + vcmpe.f64 d0, d1 @ compare (vBB, vCC) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST mov r0, #1 @ r0<- 1 (default) GET_INST_OPCODE ip @ extract opcode from rINST @@ -1509,8 +1507,8 @@ artMterpAsmInstructionStart = .L_op_nop mov r9, rINST, lsr #8 @ r9<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 cmp r1, r3 @ compare (vBB+1, vCC+1) @@ -1534,14 +1532,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bne .L_op_if_eq_not_taken + movne rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1549,31 +1548,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST 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 - 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 - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1589,14 +1570,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - beq .L_op_if_ne_not_taken + moveq rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1604,31 +1586,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST 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 - 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 - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1644,14 +1608,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bge .L_op_if_lt_not_taken + movge rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1659,31 +1624,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST 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 - 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 - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1699,14 +1646,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - blt .L_op_if_ge_not_taken + movlt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1714,31 +1662,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST 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 - 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 - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1754,14 +1684,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - ble .L_op_if_gt_not_taken + movle rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1769,31 +1700,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST 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 - 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 - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1809,14 +1722,15 @@ artMterpAsmInstructionStart = .L_op_nop * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le */ /* if-cmp vA, vB, +CCCC */ -#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 rINST, 1 @ rINST<- branch offset, in code units cmp r2, r3 @ compare (vA, vB) - bgt .L_op_if_le_not_taken + movgt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1824,31 +1738,13 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif adds r2, rINST, rINST @ convert to bytes, check sign ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST 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 - 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 - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - FETCH_S rINST, 1 @ rINST<- branch offset, in code units - cmp r2, r3 @ compare (vA, vB) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1864,13 +1760,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bne .L_op_if_eqz_not_taken + movne rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1878,28 +1775,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif 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 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 rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1915,13 +1796,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - beq .L_op_if_nez_not_taken + moveq rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1929,28 +1811,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif 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 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 rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -1966,13 +1832,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bge .L_op_if_ltz_not_taken + movge rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -1980,28 +1847,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif 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 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 rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2017,13 +1868,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - blt .L_op_if_gez_not_taken + movlt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2031,28 +1883,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif 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 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 rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2068,13 +1904,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - ble .L_op_if_gtz_not_taken + movle rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2082,28 +1919,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif 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 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 rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2119,13 +1940,14 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES mov r0, rINST, lsr #8 @ r0<- AA GET_VREG r2, r0 @ r2<- vAA FETCH_S rINST, 1 @ rINST<- branch offset, in code units ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] cmp r2, #0 @ compare (vA, 0) - bgt .L_op_if_lez_not_taken + movgt rINST, #2 +#if MTERP_PROFILE_BRANCHES + @ TUNING: once measurements are complete, remove #if and hand-schedule. EXPORT_PC mov r0, rSELF add r1, rFP, #OFF_FP_SHADOWFRAME @@ -2133,28 +1955,12 @@ artMterpAsmInstructionStart = .L_op_nop bl MterpProfileBranch @ (self, shadow_frame, offset) cmp r0, #0 bne MterpOnStackReplacement @ Note: offset must be in rINST +#endif 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 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 rINST, 1 @ rINST<- branch offset, in code units - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] - cmp r2, #0 @ compare (vA, 0) - 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 - GOTO_OPCODE ip @ jump to next instruction -#endif /* ------------------------------ */ @@ -2281,7 +2087,7 @@ artMterpAsmInstructionStart = .L_op_nop bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldrd r2, [r0, #MIRROR_WIDE_ARRAY_DATA_OFFSET] @ r2/r3<- vBB[vCC] - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] GET_INST_OPCODE ip @ extract opcode from rINST stmia r9, {r2-r3} @ vAA/vAA+1<- r2/r3 GOTO_OPCODE ip @ jump to next instruction @@ -2506,7 +2312,7 @@ artMterpAsmInstructionStart = .L_op_nop ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length add r0, r0, r1, lsl #3 @ r0<- arrayObj + index*width cmp r1, r3 @ compare unsigned index, length - add r9, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA] bcs common_errArrayIndex @ index >= length, bail FETCH_ADVANCE_INST 2 @ advance rPC, load rINST ldmia r9, {r2-r3} @ r2/r3<- vAA/vAA+1 @@ -2725,7 +2531,7 @@ artMterpAsmInstructionStart = .L_op_nop cmp r3, #0 bne MterpException @ bail out CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] stmia r3, {r0-r1} @ fp[A]<- r0/r1 ADVANCE 2 GET_INST_OPCODE ip @ extract opcode from rINST @@ -2928,7 +2734,7 @@ artMterpAsmInstructionStart = .L_op_nop mov r1, rINST, lsr #12 @ r1<- B GET_VREG r1, r1 @ r1<- fp[B], the object pointer ubfx r2, rINST, #8, #4 @ r2<- A - add r2, rFP, r2, lsl #2 @ r2<- &fp[A] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[A] ldr r3, [rFP, #OFF_FP_METHOD] @ r3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp @@ -3115,7 +2921,7 @@ artMterpAsmInstructionStart = .L_op_nop bl artGet64StaticFromCode ldr r3, [rSELF, #THREAD_EXCEPTION_OFFSET] mov r9, rINST, lsr #8 @ r9<- AA - add lr, rFP, r9, lsl #2 @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR lr, r9 @ r9<- &fp[AA] cmp r3, #0 @ Fail to resolve? bne MterpException @ bail out FETCH_ADVANCE_INST 2 @ advance rPC, load rINST @@ -3327,7 +3133,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH r0, 1 @ r0<- field ref BBBB ldr r1, [rFP, #OFF_FP_METHOD] mov r2, rINST, lsr #8 @ r3<- AA - add r2, rFP, r2, lsl #2 + VREG_INDEX_TO_ADDR r2, r2 mov r3, rSELF PREFETCH_INST 2 @ Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp @@ -3860,8 +3666,8 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3888,8 +3694,8 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3942,8 +3748,8 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3971,7 +3777,7 @@ artMterpAsmInstructionStart = .L_op_nop mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A GET_VREG r0, r3 @ r0<- vB - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] @ optional op; may set condition codes CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -3995,11 +3801,10 @@ artMterpAsmInstructionStart = .L_op_nop */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A fsitos s1, s0 @ s1<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -4020,11 +3825,10 @@ artMterpAsmInstructionStart = .L_op_nop */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A fsitod d0, s0 @ d0<- op CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs GET_INST_OPCODE ip @ extract opcode from rINST @@ -4072,7 +3876,7 @@ artMterpAsmInstructionStart = .L_op_nop /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] ldmia r3, {r0-r1} @ r0/r1<- vB/vB+1 FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @ optional op; may set condition codes @@ -4096,8 +3900,8 @@ artMterpAsmInstructionStart = .L_op_nop */ mov r3, rINST, lsr #12 @ r3<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, r9, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[A] vldr d0, [r3] @ d0<- vAA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -4127,11 +3931,10 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A ftosizs s1, s0 @ s1<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -4156,7 +3959,7 @@ constvalop_long_to_double: mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A GET_VREG r0, r3 @ r0<- vB - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] @ optional op; may set condition codes CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -4181,12 +3984,11 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB flds s0, [r3] @ s0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A - fcvtds d0, s0 @ d0<- op + vcvt.f64.f32 d0, s0 @ d0<- op CLEAR_SHADOW_PAIR r9, ip, lr @ Zero shadow regs GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -4207,11 +4009,10 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB fldd d0, [r3] @ d0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A ftosizd s0, d0 @ s0<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA @@ -4235,8 +4036,8 @@ constvalop_long_to_double: /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r3, rFP, r3, lsl #2 @ r3<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r3, {r0-r1} @ r0/r1<- vAA CLEAR_SHADOW_PAIR rINST, ip, lr @ Zero shadow regs FETCH_ADVANCE_INST 1 @ advance rPC, load rINST @@ -4262,12 +4063,11 @@ constvalop_long_to_double: */ /* unop vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB fldd d0, [r3] @ d0<- vB + ubfx r9, rINST, #8, #4 @ r9<- A FETCH_ADVANCE_INST 1 @ advance rPC, load rINST - and r9, r9, #15 @ r9<- A - fcvtsd s0, d0 @ s0<- op + vcvt.f32.f64 s0, d0 @ s0<- op GET_INST_OPCODE ip @ extract opcode from rINST VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA fsts s0, [r9] @ vA<- s0 @@ -4818,9 +4618,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -4862,9 +4662,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -4907,8 +4707,8 @@ constvalop_long_to_double: FETCH r0, 1 @ r0<- CCBB and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 mul ip, r2, r1 @ ip<- ZxW @@ -4916,7 +4716,7 @@ constvalop_long_to_double: mla r2, r0, r3, ip @ r2<- YxX + (ZxW) mov r0, rINST, lsr #8 @ r0<- AA add r10, r2, r10 @ r10<- r10 + low(ZxW + (YxX)) - add r0, rFP, r0, lsl #2 @ r0<- &fp[AA] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[AA] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST GET_INST_OPCODE ip @ extract opcode from rINST stmia r0, {r9-r10} @ vAA/vAA+1<- r9/r10 @@ -4947,9 +4747,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 1 @@ -4992,9 +4792,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 1 @@ -5036,9 +4836,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -5080,9 +4880,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -5124,9 +4924,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -5158,12 +4958,12 @@ constvalop_long_to_double: mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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)) @@ -5190,12 +4990,12 @@ constvalop_long_to_double: mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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)) @@ -5222,12 +5022,12 @@ constvalop_long_to_double: mov r9, rINST, lsr #8 @ r9<- AA and r3, r0, #255 @ r3<- BB mov r0, r0, lsr #8 @ r0<- CC - add r3, rFP, r3, lsl #2 @ r3<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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)) @@ -5547,9 +5347,9 @@ constvalop_long_to_double: mov rINST, rINST, lsr #8 @ rINST<- AA and r2, r0, #255 @ r2<- BB mov r3, r0, lsr #8 @ r3<- CC - add r9, rFP, rINST, lsl #2 @ r9<- &fp[AA] - add r2, rFP, r2, lsl #2 @ r2<- &fp[BB] - add r3, rFP, r3, lsl #2 @ r3<- &fp[CC] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[AA] + VREG_INDEX_TO_ADDR r2, r2 @ r2<- &fp[BB] + VREG_INDEX_TO_ADDR r3, r3 @ r3<- &fp[CC] ldmia r2, {r0-r1} @ r0/r1<- vBB/vBB+1 ldmia r3, {r2-r3} @ r2/r3<- vCC/vCC+1 .if 0 @@ -6000,8 +5800,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6040,8 +5840,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6073,8 +5873,8 @@ constvalop_long_to_double: /* mul-long/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx r9, rINST, #8, #4 @ r9<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add rINST, rFP, r9, lsl #2 @ rINST<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR rINST, r9 @ rINST<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia rINST, {r0-r1} @ r0/r1<- vAA/vAA+1 mul ip, r2, r1 @ ip<- ZxW @@ -6109,8 +5909,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 1 @@ -6150,8 +5950,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 1 @@ -6190,8 +5990,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6230,8 +6030,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6270,8 +6070,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6301,7 +6101,7 @@ constvalop_long_to_double: 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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 @@ -6328,7 +6128,7 @@ constvalop_long_to_double: 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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 @@ -6355,7 +6155,7 @@ constvalop_long_to_double: 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] + VREG_INDEX_TO_ADDR r9, r9 @ 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 @@ -6383,14 +6183,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fadds s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6411,14 +6209,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fsubs s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6439,14 +6235,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fmuls s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6467,14 +6261,12 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - flds s1, [r3] @ s1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA + flds s1, [r3] @ s1<- vB FETCH_ADVANCE_INST 1 @ advance rPC, load rINST flds s0, [r9] @ s0<- vA - fdivs s2, s0, s1 @ s2<- op GET_INST_OPCODE ip @ extract opcode from rINST fsts s2, [r9] @ vAA<- s2 @@ -6535,11 +6327,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6564,11 +6355,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6593,11 +6383,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6622,11 +6411,10 @@ constvalop_long_to_double: */ /* binop/2addr vA, vB */ mov r3, rINST, lsr #12 @ r3<- B - mov r9, rINST, lsr #8 @ r9<- A+ + ubfx r9, rINST, #8, #4 @ r9<- A VREG_INDEX_TO_ADDR r3, r3 @ r3<- &vB - and r9, r9, #15 @ r9<- A - fldd d1, [r3] @ d1<- vB CLEAR_SHADOW_PAIR r9, ip, r0 @ Zero out shadow regs + fldd d1, [r3] @ d1<- vB VREG_INDEX_TO_ADDR r9, r9 @ r9<- &vA FETCH_ADVANCE_INST 1 @ advance rPC, load rINST fldd d0, [r9] @ d0<- vA @@ -6659,8 +6447,8 @@ constvalop_long_to_double: /* binop/2addr vA, vB */ mov r1, rINST, lsr #12 @ r1<- B ubfx rINST, rINST, #8, #4 @ rINST<- A - add r1, rFP, r1, lsl #2 @ r1<- &fp[B] - add r9, rFP, rINST, lsl #2 @ r9<- &fp[A] + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &fp[B] + VREG_INDEX_TO_ADDR r9, rINST @ r9<- &fp[A] ldmia r1, {r2-r3} @ r2/r3<- vBB/vBB+1 ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1 .if 0 @@ -6975,7 +6763,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7013,7 +6801,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7052,7 +6840,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7159,7 +6947,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7197,7 +6985,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7235,7 +7023,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7273,7 +7061,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7311,7 +7099,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7349,7 +7137,7 @@ constvalop_long_to_double: * shl-int/lit8, shr-int/lit8, ushr-int/lit8 */ /* binop/lit8 vAA, vBB, #+CC */ - FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC + FETCH_S r3, 1 @ r3<- ssssCCBB (sign-extended for CC) mov r9, rINST, lsr #8 @ r9<- AA and r2, r3, #255 @ r2<- BB GET_VREG r0, r2 @ r0<- vBB @@ -7399,7 +7187,7 @@ constvalop_long_to_double: beq common_errNullObject @ object was null ldrd r0, [r3, ip] @ r0<- obj.field (64 bits, aligned) FETCH_ADVANCE_INST 2 @ advance rPC, load rINST - add r3, rFP, r2, lsl #2 @ r3<- &fp[A] + VREG_INDEX_TO_ADDR r3, r2 @ r3<- &fp[A] CLEAR_SHADOW_PAIR r2, ip, lr @ Zero out the shadow regs GET_INST_OPCODE ip @ extract opcode from rINST stmia r3, {r0-r1} @ fp[A]<- r0/r1 @@ -7455,7 +7243,7 @@ constvalop_long_to_double: ubfx r0, rINST, #8, #4 @ r0<- A cmp r2, #0 @ check object for null beq common_errNullObject @ object was null - add r0, rFP, r0, lsl #2 @ r0<- &fp[A] + VREG_INDEX_TO_ADDR r0, r0 @ r0<- &fp[A] ldmia r0, {r0-r1} @ r0/r1<- fp[A]/fp[A+1] FETCH_ADVANCE_INST 2 @ advance rPC, load rINST strd r0, [r2, r3] @ obj.field<- r0/r1 @@ -7865,7 +7653,7 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes mvnne r0, #0 @ return maxlong (7fffffff) mvnne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, #0xdf000000 @ (float)minlong @@ -7873,14 +7661,14 @@ f2l_doconv: cmp r0, #0 @ nonzero == yes movne r0, #0 @ return minlong (80000000) movne r1, #0x80000000 - ldmnefd sp!, {r4, pc} + popne {r4, pc} mov r0, r4 @ recover arg mov r1, r4 bl __aeabi_fcmpeq @ is arg == self? cmp r0, #0 @ zero == no moveq r1, #0 @ return zero for NaN - ldmeqfd sp!, {r4, pc} + popeq {r4, pc} mov r0, r4 @ recover arg bl __aeabi_f2lz @ convert float to long diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index e4825f0489a9feec4ae4a986c21c4a052830508c..cdb27e89e64a14753cdd5f023896fb7539274949 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -279,7 +279,7 @@ codes. * Convert a virtual register index into an address. */ .macro VREG_INDEX_TO_ADDR reg, vreg - add \reg, xFP, \vreg, lsl #2 /* WARNING/FIXME: handle shadow frame vreg zero if store */ + add \reg, xFP, \vreg, lsl #2 /* WARNING: handle shadow frame vreg zero if store */ .endm /* @@ -338,7 +338,7 @@ ExecuteMterpImpl: /* set up "named" registers */ mov xSELF, x0 ldr w0, [x2, #SHADOWFRAME_NUMBER_OF_VREGS_OFFSET] - add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to insns[] (i.e. - the dalivk byte code). + add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to vregs. add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, #CODEITEM_INSNS_OFFSET // Point to base of insns[] @@ -1372,46 +1372,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_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 wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - 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: + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. 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 +#endif 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 GOTO_OPCODE ip // jump to next instruction -#else - 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 - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1427,46 +1410,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_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 wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - 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: + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. 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 +#endif 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 GOTO_OPCODE ip // jump to next instruction -#else - 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 - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1482,46 +1448,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_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 wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - 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: + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. 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 +#endif 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 GOTO_OPCODE ip // jump to next instruction -#else - 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 - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1537,46 +1486,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_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 wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - 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: + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. 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 +#endif 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 GOTO_OPCODE ip // jump to next instruction -#else - 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 - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1592,46 +1524,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_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 wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - 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: + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. 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 +#endif 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 GOTO_OPCODE ip // jump to next instruction -#else - 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 - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1647,46 +1562,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_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 wINST, 1 // wINST<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Offset if branch not taken cmp w2, w3 // compare (vA, vB) - 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: + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg. +#if MTERP_PROFILE_BRANCHES + // TUINING: once measurements are complete, remove #if and hand-schedule. 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 +#endif 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 GOTO_OPCODE ip // jump to next instruction -#else - 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 - mov w0, #2 // Offset if branch not taken - cmp w2, w3 // compare (vA, vB) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1702,42 +1600,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - 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: + csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. 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] 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 - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1753,42 +1636,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - 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: + csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. 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] 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 - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1804,42 +1672,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - 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: + csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. 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] 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 - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1855,42 +1708,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - 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: + csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. 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] 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 - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1906,42 +1744,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - 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: + csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. 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] 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 - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -1957,42 +1780,27 @@ artMterpAsmInstructionStart = .L_op_nop * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez */ /* if-cmp vAA, +BBBB */ -#if MTERP_PROFILE_BRANCHES lsr w0, wINST, #8 // w0<- AA GET_VREG w2, w0 // w2<- vAA - FETCH_S wINST, 1 // w1<- branch offset, in code units + FETCH_S w1, 1 // w1<- branch offset, in code units + mov w0, #2 // Branch offset if not taken cmp w2, #0 // compare (vA, 0) - 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: + csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg +#if MTERP_PROFILE_BRANCHES + // TUNING: once measurements are complete, remove #if and hand-schedule. 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] 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 - mov w0, #2 // Branch offset if not taken - cmp w2, #0 // compare (vA, 0) - 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 - GOTO_OPCODE ip // jump to next instruction -#endif /* ------------------------------ */ @@ -2744,7 +2552,7 @@ artMterpAsmInstructionStart = .L_op_nop lsr w1, wINST, #12 // w1<- B GET_VREG w1, w1 // w1<- fp[B], the object pointer ubfx w2, wINST, #8, #4 // w2<- A - add x2, xFP, x2, lsl #2 // w2<- &fp[A] + VREG_INDEX_TO_ADDR x2, x2 // w2<- &fp[A] ldr x3, [xFP, #OFF_FP_METHOD] // w3<- referrer PREFETCH_INST 2 bl artSet64InstanceFromMterp @@ -3133,7 +2941,7 @@ artMterpAsmInstructionStart = .L_op_nop FETCH w0, 1 // w0<- field ref BBBB ldr x1, [xFP, #OFF_FP_METHOD] lsr w2, wINST, #8 // w3<- AA - add x2, xFP, w2, lsl #2 + VREG_INDEX_TO_ADDR x2, w2 mov x3, xSELF PREFETCH_INST 2 // Get next inst, but don't advance rPC bl artSet64IndirectStaticFromMterp diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S new file mode 100644 index 0000000000000000000000000000000000000000..b134129a5aeec767f72f485a3256b700b0998944 --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -0,0 +1,13157 @@ +/* + * This file was generated automatically by gen-mterp.py for 'mips'. + * + * --> DO NOT EDIT <-- + */ + +/* File: mips/header.S */ +/* + * 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. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +#include "asm_support.h" + +#if (__mips==32) && (__mips_isa_rev>=2) +#define MIPS32REVGE2 /* mips32r2 and greater */ +#if (__mips==32) && (__mips_isa_rev>=5) +#define FPU64 /* 64 bit FPU */ +#if (__mips==32) && (__mips_isa_rev>=6) +#define MIPS32REVGE6 /* mips32r6 and greater */ +#endif +#endif +#endif + +/* MIPS definitions and declarations + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rIBASE interpreted instruction base pointer, used for computed goto + s4 rINST first 16-bit code unit of current instruction + s6 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rIBASE s3 +#define rINST s4 +#define rOBJ s5 +#define rREFS s6 +#define rTEMP s7 + +#define rARG0 a0 +#define rARG1 a1 +#define rARG2 a2 +#define rARG3 a3 +#define rRESULT0 v0 +#define rRESULT1 v1 + +/* GP register definitions */ +#define zero $0 /* always zero */ +#define AT $at /* assembler temp */ +#define v0 $2 /* return value */ +#define v1 $3 +#define a0 $4 /* argument registers */ +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define t0 $8 /* temp registers (not saved across subroutine calls) */ +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define ta0 $12 /* alias */ +#define ta1 $13 +#define ta2 $14 +#define ta3 $15 +#define s0 $16 /* saved across subroutine calls (callee saved) */ +#define s1 $17 +#define s2 $18 +#define s3 $19 +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 /* two more temp registers */ +#define t9 $25 +#define k0 $26 /* kernel temporary */ +#define k1 $27 +#define gp $28 /* global pointer */ +#define sp $29 /* stack pointer */ +#define s8 $30 /* one more callee saved */ +#define ra $31 /* return address */ + +/* FP register definitions */ +#define fv0 $f0 +#define fv0f $f1 +#define fv1 $f2 +#define fv1f $f3 +#define fa0 $f12 +#define fa0f $f13 +#define fa1 $f14 +#define fa1f $f15 +#define ft0 $f4 +#define ft0f $f5 +#define ft1 $f6 +#define ft1f $f7 +#define ft2 $f8 +#define ft2f $f9 +#define ft3 $f10 +#define ft3f $f11 +#define ft4 $f16 +#define ft4f $f17 +#define ft5 $f18 +#define ft5f $f19 +#define fs0 $f20 +#define fs0f $f21 +#define fs1 $f22 +#define fs1f $f23 +#define fs2 $f24 +#define fs2f $f25 +#define fs3 $f26 +#define fs3f $f27 +#define fs4 $f28 +#define fs4f $f29 +#define fs5 $f30 +#define fs5f $f31 + +#ifndef MIPS32REVGE6 +#define fcc0 $fcc0 +#define fcc1 $fcc1 +#endif + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#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 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +#define EXPORT_PC() \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) + +#define EXPORT_DEX_PC(tmp) \ + lw tmp, OFF_FP_CODE_ITEM(rFP) \ + sw rPC, OFF_FP_DEX_PC_PTR(rFP) \ + addu tmp, CODEITEM_INSNS_OFFSET \ + subu tmp, rPC, tmp \ + sra tmp, tmp, 1 \ + sw tmp, OFF_FP_DEX_PC(rFP) + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +#define FETCH_INST() lhu rINST, (rPC) + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. "_count" is in 16-bit code units. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC().) + */ +#define FETCH_ADVANCE_INST(_count) lhu rINST, ((_count)*2)(rPC); \ + addu rPC, rPC, ((_count) * 2) + +/* + * The operation performed here is similar to FETCH_ADVANCE_INST, except the + * src and dest registers are parameterized (not hard-wired to rPC and rINST). + */ +#define PREFETCH_ADVANCE_INST(_dreg, _sreg, _count) \ + lhu _dreg, ((_count)*2)(_sreg) ; \ + addu _sreg, _sreg, (_count)*2 + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +#define PREFETCH_INST(_count) lhu rINST, ((_count)*2)(rPC) + +/* Advance rPC by some number of code units. */ +#define ADVANCE(_count) addu rPC, rPC, ((_count) * 2) + +/* + * Fetch the next instruction from an offset specified by rd. Updates + * rPC to point to the next instruction. "rd" must specify the distance + * in bytes, *not* 16-bit code units, and may be a signed value. + */ +#define FETCH_ADVANCE_INST_RB(rd) addu rPC, rPC, rd; \ + lhu rINST, (rPC) + +/* + * Fetch a half-word code unit from an offset past the current PC. The + * "_count" value is in 16-bit code units. Does not advance rPC. + * + * The "_S" variant works the same but treats the value as signed. + */ +#define FETCH(rd, _count) lhu rd, ((_count) * 2)(rPC) +#define FETCH_S(rd, _count) lh rd, ((_count) * 2)(rPC) + +/* + * Fetch one byte from an offset past the current PC. Pass in the same + * "_count" as you would for FETCH, and an additional 0/1 indicating which + * byte of the halfword you want (lo/hi). + */ +#define FETCH_B(rd, _count, _byte) lbu rd, ((_count) * 2 + _byte)(rPC) + +/* + * Put the instruction's opcode field into the specified register. + */ +#define GET_INST_OPCODE(rd) and rd, rINST, 0xFF + +/* + * Put the prefetched instruction's opcode field into the specified register. + */ +#define GET_PREFETCHED_OPCODE(dreg, sreg) andi dreg, sreg, 255 + +/* + * Begin executing the opcode in rd. + */ +#define GOTO_OPCODE(rd) sll rd, rd, 7; \ + addu rd, rIBASE, rd; \ + jalr zero, rd + +#define GOTO_OPCODE_BASE(_base, rd) sll rd, rd, 7; \ + addu rd, _base, rd; \ + jalr zero, rd + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define GET_VREG(rd, rix) LOAD_eas2(rd, rFP, rix) + +#define GET_VREG_F(rd, rix) EAS2(AT, rFP, rix); \ + .set noat; l.s rd, (AT); .set at + +#define SET_VREG(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define SET_VREG64(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) + +#ifdef FPU64 +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rREFS, AT; \ + sw zero, 0(t8); \ + sw zero, 4(t8); \ + addu t8, rFP, AT; \ + mfhc1 AT, rlo; \ + sw AT, 4(t8); \ + .set at; \ + s.s rlo, 0(t8) +#else +#define SET_VREG64_F(rlo, rhi, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rlo, 0(t8); \ + s.s rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + sw zero, 4(t8) +#endif + +#define SET_VREG_OBJECT(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw rd, 0(t8) + +/* Combination of the SET_VREG and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG_GOTO(rd, rix, dst) .set noreorder; \ + sll dst, dst, 7; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + jalr zero, dst; \ + sw zero, 0(t8); \ + .set reorder + +/* Combination of the SET_VREG64 and GOTO_OPCODE functions to save 1 instruction */ +#define SET_VREG64_GOTO(rlo, rhi, rix, dst) .set noreorder; \ + sll dst, dst, 7; \ + addu dst, rIBASE, dst; \ + .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + sw rlo, 0(t8); \ + sw rhi, 4(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8); \ + jalr zero, dst; \ + sw zero, 4(t8); \ + .set reorder + +#define SET_VREG_F(rd, rix) .set noat; \ + sll AT, rix, 2; \ + addu t8, rFP, AT; \ + s.s rd, 0(t8); \ + addu t8, rREFS, AT; \ + .set at; \ + sw zero, 0(t8) + +#define GET_OPA(rd) srl rd, rINST, 8 +#ifdef MIPS32REVGE2 +#define GET_OPA4(rd) ext rd, rINST, 8, 4 +#else +#define GET_OPA4(rd) GET_OPA(rd); and rd, 0xf +#endif +#define GET_OPB(rd) srl rd, rINST, 12 + +/* + * Form an Effective Address rd = rbase + roff<>n; + * Uses reg AT + */ +#define ESRN(rd, rbase, roff, rshift) .set noat; \ + srl AT, roff, rshift; \ + addu rd, rbase, AT; \ + .set at + +#define LOAD_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; lw rd, 0(AT); .set at + +#define STORE_eas2(rd, rbase, roff) EAS2(AT, rbase, roff); \ + .set noat; sw rd, 0(AT); .set at + +#define LOAD_RB_OFF(rd, rbase, off) lw rd, off(rbase) +#define STORE_RB_OFF(rd, rbase, off) sw rd, off(rbase) + +#define STORE64_off(rlo, rhi, rbase, off) sw rlo, off(rbase); \ + sw rhi, (off+4)(rbase) +#define LOAD64_off(rlo, rhi, rbase, off) lw rlo, off(rbase); \ + lw rhi, (off+4)(rbase) + +#define STORE64(rlo, rhi, rbase) STORE64_off(rlo, rhi, rbase, 0) +#define LOAD64(rlo, rhi, rbase) LOAD64_off(rlo, rhi, rbase, 0) + +#ifdef FPU64 +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + .set noat; \ + mfhc1 AT, rlo; \ + sw AT, (off+4)(rbase); \ + .set at +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + .set noat; \ + lw AT, (off+4)(rbase); \ + mthc1 AT, rlo; \ + .set at +#else +#define STORE64_off_F(rlo, rhi, rbase, off) s.s rlo, off(rbase); \ + s.s rhi, (off+4)(rbase) +#define LOAD64_off_F(rlo, rhi, rbase, off) l.s rlo, off(rbase); \ + l.s rhi, (off+4)(rbase) +#endif + +#define STORE64_F(rlo, rhi, rbase) STORE64_off_F(rlo, rhi, rbase, 0) +#define LOAD64_F(rlo, rhi, rbase) LOAD64_off_F(rlo, rhi, rbase, 0) + + +#define LOAD_base_offMirrorArray_length(rd, rbase) LOAD_RB_OFF(rd, rbase, MIRROR_ARRAY_LENGTH_OFFSET) + +#define STACK_STORE(rd, off) sw rd, off(sp) +#define STACK_LOAD(rd, off) lw rd, off(sp) +#define CREATE_STACK(n) subu sp, sp, n +#define DELETE_STACK(n) addu sp, sp, n + +#define LOAD_ADDR(dest, addr) la dest, addr +#define LOAD_IMM(dest, imm) li dest, imm +#define MOVE_REG(dest, src) move dest, src +#define STACK_SIZE 128 + +#define STACK_OFFSET_ARG04 16 +#define STACK_OFFSET_ARG05 20 +#define STACK_OFFSET_ARG06 24 +#define STACK_OFFSET_ARG07 28 +#define STACK_OFFSET_GP 84 + +#define JAL(n) jal n +#define BAL(n) bal n + +/* + * FP register usage restrictions: + * 1) We don't use the callee save FP registers so we don't have to save them. + * 2) We don't use the odd FP registers so we can share code with mips32r6. + */ +#define STACK_STORE_FULL() CREATE_STACK(STACK_SIZE); \ + STACK_STORE(ra, 124); \ + STACK_STORE(s8, 120); \ + STACK_STORE(s0, 116); \ + STACK_STORE(s1, 112); \ + STACK_STORE(s2, 108); \ + STACK_STORE(s3, 104); \ + STACK_STORE(s4, 100); \ + STACK_STORE(s5, 96); \ + STACK_STORE(s6, 92); \ + STACK_STORE(s7, 88); + +#define STACK_LOAD_FULL() STACK_LOAD(gp, STACK_OFFSET_GP); \ + STACK_LOAD(s7, 88); \ + STACK_LOAD(s6, 92); \ + STACK_LOAD(s5, 96); \ + STACK_LOAD(s4, 100); \ + STACK_LOAD(s3, 104); \ + STACK_LOAD(s2, 108); \ + STACK_LOAD(s1, 112); \ + STACK_LOAD(s0, 116); \ + STACK_LOAD(s8, 120); \ + STACK_LOAD(ra, 124); \ + DELETE_STACK(STACK_SIZE) + +/* File: mips/entry.S */ +/* + * 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. + */ +/* + * Interpreter entry point. + */ + + .text + .align 2 + .global ExecuteMterpImpl + .ent ExecuteMterpImpl + .frame sp, STACK_SIZE, ra +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ + +ExecuteMterpImpl: + .set noreorder + .cpload t9 + .set reorder +/* Save to the stack. Frame size = STACK_SIZE */ + STACK_STORE_FULL() +/* This directive will make sure all subsequent jal restore gp at a known offset */ + .cprestore STACK_OFFSET_GP + + /* Remember the return register */ + sw a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sw a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + lw a0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + addu rFP, a2, SHADOWFRAME_VREGS_OFFSET # point to vregs. + EAS2(rREFS, rFP, a0) # point to reference array in shadow frame + lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc + addu rPC, a1, CODEITEM_INSNS_OFFSET # Point to base of insns[] + EAS1(rPC, rPC, a0) # Create direct pointer to 1st dex opcode + + EXPORT_PC() + + /* Starting ibase */ + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + + /* start executing the instruction at rPC */ + FETCH_INST() # load rINST from rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* NOTE: no fallthrough */ + + + .global artMterpAsmInstructionStart + .type artMterpAsmInstructionStart, %function +artMterpAsmInstructionStart = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: mips/op_nop.S */ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: mips/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: mips/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + FETCH(a1, 1) # a1 <- BBBB + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[AA] <- a2 + .else + SET_VREG(a2, a0) # fp[AA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: mips/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + FETCH(a1, 2) # a1 <- BBBB + FETCH(a0, 1) # a0 <- AAAA + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[AAAA] <- a2 + .else + SET_VREG(a2, a0) # fp[AAAA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: mips/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + GET_OPA4(a2) # a2 <- A(+) + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- fp[B] + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: mips/op_move_wide_from16.S */ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 1) # a3 <- BBBB + GET_OPA(a2) # a2 <- AA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: mips/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6, v7" or "move v7, v6" */ + FETCH(a3, 2) # a3 <- BBBB + FETCH(a2, 1) # a2 <- AAAA + EAS2(a3, rFP, a3) # a3 <- &fp[BBBB] + LOAD64(a0, a1, a3) # a0/a1 <- fp[BBBB] + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AAAA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: mips/op_move_object.S */ +/* File: mips/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if 1 + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: mips/op_move_object_from16.S */ +/* File: mips/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + FETCH(a1, 1) # a1 <- BBBB + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 1 + SET_VREG_OBJECT(a2, a0) # fp[AA] <- a2 + .else + SET_VREG(a2, a0) # fp[AA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: mips/op_move_object_16.S */ +/* File: mips/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + FETCH(a1, 2) # a1 <- BBBB + FETCH(a0, 1) # a0 <- AAAA + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[BBBB] + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 1 + SET_VREG_OBJECT(a2, a0) # fp[AAAA] <- a2 + .else + SET_VREG(a2, a0) # fp[AAAA] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: mips/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + GET_OPA(a2) # a2 <- AA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + lw a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 0 + SET_VREG_OBJECT(a0, a2) # fp[AA] <- a0 + .else + SET_VREG(a0, a2) # fp[AA] <- a0 + .endif + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: mips/op_move_result_wide.S */ + /* move-result-wide vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + LOAD64(a0, a1, a3) # a0/a1 <- retval.j + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG64(a0, a1, a2) # fp[AA] <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: mips/op_move_result_object.S */ +/* File: mips/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + GET_OPA(a2) # a2 <- AA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + lw a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE(t0) # extract opcode from rINST + .if 1 + SET_VREG_OBJECT(a0, a2) # fp[AA] <- a0 + .else + SET_VREG(a0, a2) # fp[AA] <- a0 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: mips/op_move_exception.S */ + /* move-exception vAA */ + GET_OPA(a2) # a2 <- AA + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) # get exception obj + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + SET_VREG_OBJECT(a3, a2) # fp[AA] <- exception obj + GET_INST_OPCODE(t0) # extract opcode from rINST + sw zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: mips/op_return_void.S */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: mips/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + GET_VREG(v0, a2) # v0 <- vAA + move v1, zero + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: mips/op_return_wide.S */ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + LOAD64(v0, v1, a2) # v0/v1 <- vAA/vAA+1 + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: mips/op_return_object.S */ +/* File: mips/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + JAL(MterpThreadFenceForConstructor) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + GET_OPA(a2) # a2 <- AA + GET_VREG(v0, a2) # v0 <- vAA + move v1, zero + b MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: mips/op_const_4.S */ + # const/4 vA, /* +B */ + sll a1, rINST, 16 # a1 <- Bxxx0000 + GET_OPA(a0) # a0 <- A+ + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sra a1, a1, 28 # a1 <- sssssssB (sign-extended) + and a0, a0, 15 + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a1, a0, t0) # fp[A] <- a1 + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: mips/op_const_16.S */ + # const/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: mips/op_const.S */ + # const vAA, /* +BBBBbbbb */ + GET_OPA(a3) # a3 <- AA + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a1, a1, 16 + or a0, a1, a0 # a0 <- BBBBbbbb + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: mips/op_const_high16.S */ + # const/high16 vAA, /* +BBBB0000 */ + FETCH(a0, 1) # a0 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + sll a0, a0, 16 # a0 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a3, t0) # vAA <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: mips/op_const_wide_16.S */ + # const-wide/16 vAA, /* +BBBB */ + FETCH_S(a0, 1) # a0 <- ssssBBBB (sign-extended) + GET_OPA(a3) # a3 <- AA + sra a1, a0, 31 # a1 <- ssssssss + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: mips/op_const_wide_32.S */ + # const-wide/32 vAA, /* +BBBBbbbb */ + FETCH(a0, 1) # a0 <- 0000bbbb (low) + GET_OPA(a3) # a3 <- AA + FETCH_S(a2, 2) # a2 <- ssssBBBB (high) + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + sra a1, a0, 31 # a1 <- ssssssss + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: mips/op_const_wide.S */ + # const-wide vAA, /* +HHHHhhhhBBBBbbbb */ + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a1, 2) # a1 <- BBBB (low middle) + FETCH(a2, 3) # a2 <- hhhh (high middle) + sll a1, 16 # + or a0, a1 # a0 <- BBBBbbbb (low word) + FETCH(a3, 4) # a3 <- HHHH (high) + GET_OPA(t1) # t1 <- AA + sll a3, 16 + or a1, a3, a2 # a1 <- HHHHhhhh (high word) + FETCH_ADVANCE_INST(5) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, t1) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: mips/op_const_wide_high16.S */ + # const-wide/high16 vAA, /* +BBBB000000000000 */ + FETCH(a1, 1) # a1 <- 0000BBBB (zero-extended) + GET_OPA(a3) # a3 <- AA + li a0, 0 # a0 <- 00000000 + sll a1, 16 # a1 <- BBBB0000 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a3) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: mips/op_const_string.S */ + # const/string vAA, String /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: mips/op_const_string_jumbo.S */ + # const/string vAA, String /* BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (low) + FETCH(a2, 2) # a2 <- BBBB (high) + GET_OPA(a1) # a1 <- AA + sll a2, a2, 16 + or a0, a0, a2 # a0 <- BBBBbbbb + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstString) # v0 <- Mterp(index, tgt_reg, shadow_frame, self) + PREFETCH_INST(3) # load rINST + bnez v0, MterpPossibleException + ADVANCE(3) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: mips/op_const_class.S */ + # const/class vAA, Class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + addu a2, rFP, OFF_FP_SHADOWFRAME # a2 <- shadow frame + move a3, rSELF + JAL(MterpConstClass) + PREFETCH_INST(2) # load rINST + bnez v0, MterpPossibleException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: mips/op_monitor_enter.S */ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artLockObjectFromCode) # v0 <- artLockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: mips/op_monitor_exit.S */ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC() + GET_OPA(a2) # a2 <- AA + GET_VREG(a0, a2) # a0 <- vAA (object) + move a1, rSELF # a1 <- self + JAL(artUnlockObjectFromCode) # v0 <- artUnlockObject(obj, self) + bnez v0, MterpException + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: mips/op_check_cast.S */ + /* + * Check to see if a cast from one class to another is allowed. + */ + # check-cast vAA, class /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- BBBB + GET_OPA(a1) # a1 <- AA + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + JAL(MterpCheckCast) # v0 <- CheckCast(index, &obj, method, self) + PREFETCH_INST(2) + bnez v0, MterpPossibleException + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: mips/op_instance_of.S */ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + # instance-of vA, vB, class /* CCCC */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- CCCC + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &object + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + GET_OPA4(rOBJ) # rOBJ <- A+ + JAL(MterpInstanceOf) # v0 <- Mterp(index, &obj, method, self) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vA <- v0 + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: mips/op_array_length.S */ + /* + * Return the length of an array. + */ + GET_OPB(a1) # a1 <- B + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a0, a1) # a0 <- vB (object ref) + # is object null? + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- array length + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a3, a2, t0) # vA <- length + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: mips/op_new_instance.S */ + /* + * Create a new instance of a class. + */ + # new-instance vAA, class /* BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + JAL(MterpNewInstance) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: mips/op_new_array.S */ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpNewArray) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array: /* 0x24 */ +/* File: mips/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, type /* BBBB */ + .extern MterpFilledNewArray + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME # a0 <- shadow frame + move a1, rPC + move a2, rSELF + JAL(MterpFilledNewArray) # v0 <- helper(shadow_frame, pc, self) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array_range: /* 0x25 */ +/* File: mips/op_filled_new_array_range.S */ +/* File: mips/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, type /* BBBB */ + .extern MterpFilledNewArrayRange + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME # a0 <- shadow frame + move a1, rPC + move a2, rSELF + JAL(MterpFilledNewArrayRange) # v0 <- helper(shadow_frame, pc, self) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_fill_array_data: /* 0x26 */ +/* File: mips/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll a1, a1, 16 # a1 <- BBBBbbbb + or a1, a0, a1 # a1 <- BBBBbbbb + GET_VREG(a0, a3) # a0 <- vAA (array object) + EAS1(a1, rPC, a1) # a1 <- PC + BBBBbbbb*2 (array data off.) + JAL(MterpFillArrayData) # v0 <- Mterp(obj, payload) + beqz v0, MterpPossibleException # has exception + FETCH_ADVANCE_INST(3) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: mips/op_throw.S */ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC() # exception handler can throw + GET_OPA(a2) # a2 <- AA + GET_VREG(a1, a2) # a1 <- vAA (exception object) + # null object? + beqz a1, common_errNullObject # yes, throw an NPE instead + sw a1, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: mips/op_goto.S */ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ +#if MTERP_PROFILE_BRANCHES + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a2, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + sll a0, rINST, 16 # a0 <- AAxx0000 + sra rINST, a0, 24 # rINST <- ssssssAA (sign-extended) + addu a2, rINST, rINST # a2 <- byte offset + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + /* If backwards branch refresh rIBASE */ + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: mips/op_goto_16.S */ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH_S(rINST, 1) # rINST <- ssssAAAA (sign-extended) + addu a1, rINST, rINST # a1 <- byte offset, flags set + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: mips/op_goto_32.S */ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#else + FETCH(a0, 1) # a0 <- aaaa (lo) + FETCH(a1, 2) # a1 <- AAAA (hi) + sll a1, a1, 16 + or rINST, a0, a1 # rINST <- AAAAaaaa + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: mips/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoPackedSwitch) # a0 <- code-unit branch offset + move rINST, v0 + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, .Lop_packed_switch_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +#else + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoPackedSwitch) # a0 <- code-unit branch offset + move rINST, v0 + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: mips/op_sparse_switch.S */ +/* File: mips/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ +#if MTERP_PROFILE_BRANCHES + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoSparseSwitch) # a0 <- code-unit branch offset + move rINST, v0 + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, .Lop_sparse_switch_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +#else + FETCH(a0, 1) # a0 <- bbbb (lo) + FETCH(a1, 2) # a1 <- BBBB (hi) + GET_OPA(a3) # a3 <- AA + sll t0, a1, 16 + or a0, a0, t0 # a0 <- BBBBbbbb + GET_VREG(a1, a3) # a1 <- vAA + EAS1(a0, rPC, a0) # a0 <- PC + BBBBbbbb*2 + JAL(MterpDoSparseSwitch) # a0 <- code-unit branch offset + move rINST, v0 + addu a1, rINST, rINST # a1 <- byte offset + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgtz a1, 1f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +1: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +#endif + + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: mips/op_cmpl_float.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register rTEMP based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1 or 1}; // one or both operands was NaN + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + + /* "clasic" form */ + FETCH(a0, 1) # a0 <- CCBB + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 + GET_VREG_F(ft0, a2) + GET_VREG_F(ft1, a3) +#ifdef MIPS32REVGE6 + cmp.ult.s ft2, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpl_float_finish + cmp.ult.s ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpl_float_finish + cmp.eq.s ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpl_float_finish + b .Lop_cmpl_float_nan +#else + c.olt.s fcc0, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpl_float_finish + c.olt.s fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpl_float_finish + c.eq.s fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpl_float_finish + b .Lop_cmpl_float_nan +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: mips/op_cmpg_float.S */ +/* File: mips/op_cmpl_float.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register rTEMP based on the results of the comparison. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * The operation we're implementing is: + * if (x == y) + * return 0; + * else if (x < y) + * return -1; + * else if (x > y) + * return 1; + * else + * return {-1 or 1}; // one or both operands was NaN + * + * for: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + + /* "clasic" form */ + FETCH(a0, 1) # a0 <- CCBB + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 + GET_VREG_F(ft0, a2) + GET_VREG_F(ft1, a3) +#ifdef MIPS32REVGE6 + cmp.ult.s ft2, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpg_float_finish + cmp.ult.s ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpg_float_finish + cmp.eq.s ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpg_float_finish + b .Lop_cmpg_float_nan +#else + c.olt.s fcc0, ft0, ft1 # Is ft0 < ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpg_float_finish + c.olt.s fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpg_float_finish + c.eq.s fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpg_float_finish + b .Lop_cmpg_float_nan +#endif + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: mips/op_cmpl_double.S */ + /* + * Compare two floating-point values. Puts 0(==), 1(>), or -1(<) + * into the destination register (rTEMP) based on the comparison results. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * See op_cmpl_float for more details. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + + FETCH(a0, 1) # a0 <- CCBB + and rOBJ, a0, 255 # s5 <- BB + srl t0, a0, 8 # t0 <- CC + EAS2(rOBJ, rFP, rOBJ) # s5 <- &fp[BB] + EAS2(t0, rFP, t0) # t0 <- &fp[CC] + LOAD64_F(ft0, ft0f, rOBJ) + LOAD64_F(ft1, ft1f, t0) +#ifdef MIPS32REVGE6 + cmp.ult.d ft2, ft0, ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpl_double_finish + cmp.ult.d ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpl_double_finish + cmp.eq.d ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpl_double_finish + b .Lop_cmpl_double_nan +#else + c.olt.d fcc0, ft0, ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpl_double_finish + c.olt.d fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpl_double_finish + c.eq.d fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpl_double_finish + b .Lop_cmpl_double_nan +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: mips/op_cmpg_double.S */ +/* File: mips/op_cmpl_double.S */ + /* + * Compare two floating-point values. Puts 0(==), 1(>), or -1(<) + * into the destination register (rTEMP) based on the comparison results. + * + * Provide a "naninst" instruction that puts 1 or -1 into rTEMP depending + * on what value we'd like to return when one of the operands is NaN. + * + * See op_cmpl_float for more details. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + + FETCH(a0, 1) # a0 <- CCBB + and rOBJ, a0, 255 # s5 <- BB + srl t0, a0, 8 # t0 <- CC + EAS2(rOBJ, rFP, rOBJ) # s5 <- &fp[BB] + EAS2(t0, rFP, t0) # t0 <- &fp[CC] + LOAD64_F(ft0, ft0f, rOBJ) + LOAD64_F(ft1, ft1f, t0) +#ifdef MIPS32REVGE6 + cmp.ult.d ft2, ft0, ft1 + li rTEMP, -1 + bc1nez ft2, .Lop_cmpg_double_finish + cmp.ult.d ft2, ft1, ft0 + li rTEMP, 1 + bc1nez ft2, .Lop_cmpg_double_finish + cmp.eq.d ft2, ft0, ft1 + li rTEMP, 0 + bc1nez ft2, .Lop_cmpg_double_finish + b .Lop_cmpg_double_nan +#else + c.olt.d fcc0, ft0, ft1 + li rTEMP, -1 + bc1t fcc0, .Lop_cmpg_double_finish + c.olt.d fcc0, ft1, ft0 + li rTEMP, 1 + bc1t fcc0, .Lop_cmpg_double_finish + c.eq.d fcc0, ft0, ft1 + li rTEMP, 0 + bc1t fcc0, .Lop_cmpg_double_finish + b .Lop_cmpg_double_nan +#endif + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: mips/op_cmp_long.S */ + /* + * Compare two 64-bit values + * x = y return 0 + * x < y return -1 + * x > y return 1 + * + * I think I can improve on the ARM code by the following observation + * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 + * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 + * subu v0, t0, t1 # v0= -1:1:0 for [ < > = ] + */ + /* cmp-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(a3, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, a3) # a2/a3 <- vCC/vCC+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + slt t0, a1, a3 # compare hi + sgt t1, a1, a3 + subu v0, t1, t0 # v0 <- (-1, 1, 0) + bnez v0, .Lop_cmp_long_finish + # at this point x.hi==y.hi + sltu t0, a0, a2 # compare lo + sgtu t1, a0, a2 + subu v0, t1, t0 # v0 <- (-1, 1, 0) for [< > =] + +.Lop_cmp_long_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(v0, rOBJ, t0) # vAA <- v0 + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: mips/op_if_eq.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + bne a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_eq_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: mips/op_if_ne.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + beq a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_ne_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: mips/op_if_lt.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + bge a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_lt_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: mips/op_if_ge.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + blt a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_ge_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: mips/op_if_gt.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + ble a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_gt_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: mips/op_if_le.S */ +/* File: mips/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + GET_OPA4(a0) # a0 <- A+ + GET_OPB(a1) # a1 <- B + GET_VREG(a3, a1) # a3 <- vB + GET_VREG(a2, a0) # a2 <- vA + bgt a2, a3, 1f # branch to 1 if comparison failed + FETCH_S(rINST, 1) # rINST<- branch offset, in code units + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a2, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a2) # update rPC, load rINST + bgez a2, .L_op_if_le_finish + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue + + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: mips/op_if_eqz.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + bne a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: mips/op_if_nez.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + beq a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: mips/op_if_ltz.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + bge a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: mips/op_if_gez.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + blt a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: mips/op_if_gtz.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + ble a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: mips/op_if_lez.S */ +/* File: mips/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + GET_OPA(a0) # a0 <- AA + GET_VREG(a2, a0) # a2 <- vAA + FETCH_S(rINST, 1) # rINST <- branch offset, in code units + bgt a2, zero, 1f # branch to 1 if comparison failed + b 2f +1: + li rINST, 2 # rINST- BYTE branch dist for not-taken +2: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpProfileBranch) # (self, shadow_frame, offset) + bnez v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + addu a1, rINST, rINST # convert to bytes + FETCH_ADVANCE_INST_RB(a1) # update rPC, load rINST + bgez a1, 3f + lw ra, THREAD_FLAGS_OFFSET(rSELF) + b MterpCheckSuspendAndContinue +3: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: mips/op_unused_3e.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: mips/op_unused_3f.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: mips/op_unused_40.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: mips/op_unused_41.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: mips/op_unused_42.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: mips/op_unused_43.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 2 + EASN(a0, a0, a1, 2) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: mips/op_aget_wide.S */ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + * Arrays of long/double are 64-bit aligned. + */ + /* aget-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a2, a3, rOBJ, t0) # vAA/vAA+1 <- a2/a3 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: mips/op_aget_object.S */ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + JAL(artAGetObjectFromMterp) # v0 <- GetObj(array, index) + lw a1, THREAD_EXCEPTION_OFFSET(rSELF) + PREFETCH_INST(2) # load rINST + bnez a1, MterpException + SET_VREG_OBJECT(v0, rOBJ) # vAA <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: mips/op_aget_boolean.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lbu a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: mips/op_aget_byte.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: mips/op_aget_char.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lhu a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: mips/op_aget_short.S */ +/* File: mips/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17 + * instructions. We use a pair of FETCH_Bs instead. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + # a1 >= a3; compare unsigned index + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + lh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a2, rOBJ, t0) # vAA <- a2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 2 + EASN(a0, a0, a1, 2) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: mips/op_aput_wide.S */ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + * Arrays of long/double are 64-bit aligned, so it's okay to use STRD. + */ + /* aput-wide vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t0) # t0 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + EAS3(a0, a0, a1) # a0 <- arrayObj + index*width + EAS2(rOBJ, rFP, t0) # rOBJ <- &fp[AA] + # compare unsigned index, length + bgeu a1, a3, common_errArrayIndex # index >= length, bail + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + LOAD64(a2, a3, rOBJ) # a2/a3 <- vAA/vAA+1 + GET_INST_OPCODE(t0) # extract opcode from rINST + STORE64_off(a2, a3, a0, MIRROR_WIDE_ARRAY_DATA_OFFSET) # a2/a3 <- vBB[vCC] + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: mips/op_aput_object.S */ + /* + * Store an object into an array. vBB[vCC] <- vAA. + * + */ + /* op vAA, vBB, vCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpAputObject) + beqz v0, MterpPossibleException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: mips/op_aput_boolean.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sb a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: mips/op_aput_byte.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 0 + EASN(a0, a0, a1, 0) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: mips/op_aput_char.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sh a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: mips/op_aput_short.S */ +/* File: mips/op_aput.S */ + + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + FETCH_B(a2, 1, 0) # a2 <- BB + GET_OPA(rOBJ) # rOBJ <- AA + FETCH_B(a3, 1, 1) # a3 <- CC + GET_VREG(a0, a2) # a0 <- vBB (array object) + GET_VREG(a1, a3) # a1 <- vCC (requested index) + # null array object? + beqz a0, common_errNullObject # yes, bail + LOAD_base_offMirrorArray_length(a3, a0) # a3 <- arrayObj->length + .if 1 + EASN(a0, a0, a1, 1) # a0 <- arrayObj + index*width + .else + addu a0, a0, a1 + .endif + bgeu a1, a3, common_errArrayIndex # index >= length, bail + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_VREG(a2, rOBJ) # a2 <- vAA + GET_INST_OPCODE(t0) # extract opcode from rINST + sh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGet32InstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: mips/op_iget_wide.S */ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field byte offset + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGet64InstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpException # bail out + SET_VREG64(v0, v1, a2) # fp[A] <- v0/v1 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: mips/op_iget_object.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetObjInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 1 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: mips/op_iget_boolean.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetBooleanInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: mips/op_iget_byte.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetByteInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: mips/op_iget_char.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetCharInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: mips/op_iget_short.S */ +/* File: mips/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + JAL(artGetShortInstanceFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + .else + SET_VREG(v0, a2) # fp[A] <- v0 + .endif + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet32InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet32InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: mips/op_iput_wide.S */ + # iput-wide vA, vB, field /* CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + EAS2(a2, rFP, a2) # a2 <- &fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet64InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: mips/op_iput_object.S */ + /* + * 32-bit instance field put. + * + * for: iput-object, iput-object-volatile + */ + # op vA, vB, field /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpIputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: mips/op_iput_boolean.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet8InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: mips/op_iput_byte.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet8InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: mips/op_iput_char.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet16InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: mips/op_iput_short.S */ +/* File: mips/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + # op vA, vB, field /* CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + GET_OPB(a1) # a1 <- B + GET_VREG(a1, a1) # a1 <- fp[B], the object pointer + GET_OPA4(a2) # a2 <- A+ + GET_VREG(a2, a2) # a2 <- fp[A] + lw a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST(2) # load rINST + JAL(artSet16InstanceFromMterp) + bnez v0, MterpPossibleException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGet32StaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGet32StaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: mips/op_sget_wide.S */ + /* + * 64-bit SGET handler. + */ + # sget-wide vAA, field /* BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGet64StaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + bnez a3, MterpException + GET_OPA(a1) # a1 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + SET_VREG64(v0, v1, a1) # vAA/vAA+1 <- v0/v1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: mips/op_sget_object.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetObjStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetObjStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 1 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: mips/op_sget_boolean.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetBooleanStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetBooleanStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: mips/op_sget_byte.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetByteStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetByteStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: mips/op_sget_char.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetCharStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetCharStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: mips/op_sget_short.S */ +/* File: mips/op_sget.S */ + /* + * General SGET handler. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + # op vAA, field /* BBBB */ + .extern artGetShortStaticFromCode + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + move a2, rSELF # a2 <- self + JAL(artGetShortStaticFromCode) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA(a2) # a2 <- AA + PREFETCH_INST(2) + bnez a3, MterpException # bail out +.if 0 + SET_VREG_OBJECT(v0, a2) # fp[AA] <- v0 +.else + SET_VREG(v0, a2) # fp[AA] <- v0 +.endif + ADVANCE(2) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet32StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: mips/op_sput_wide.S */ + /* + * 64-bit SPUT handler. + */ + # sput-wide vAA, field /* BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref CCCC + lw a1, OFF_FP_METHOD(rFP) # a1 <- method + GET_OPA(a2) # a2 <- AA + EAS2(a2, rFP, a2) # a2 <- &fp[AA] + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet64IndirectStaticFromMterp) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: mips/op_sput_object.S */ + /* + * General 32-bit SPUT handler. + * + * for: sput-object, + */ + /* op vAA, field@BBBB */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + JAL(MterpSputObject) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: mips/op_sput_boolean.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet8StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: mips/op_sput_byte.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet8StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: mips/op_sput_char.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet16StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: mips/op_sput_short.S */ +/* File: mips/op_sput.S */ + /* + * General SPUT handler. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + # op vAA, field /* BBBB */ + EXPORT_PC() + FETCH(a0, 1) # a0 <- field ref BBBB + GET_OPA(a3) # a3 <- AA + GET_VREG(a1, a3) # a1 <- fp[AA], the object pointer + lw a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + PREFETCH_INST(2) # load rINST + JAL(artSet16StaticFromCode) + bnez v0, MterpException # bail out + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: mips/op_invoke_virtual.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtual + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtual) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super: /* 0x6f */ +/* File: mips/op_invoke_super.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeSuper + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeSuper) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct: /* 0x70 */ +/* File: mips/op_invoke_direct.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeDirect + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeDirect) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: mips/op_invoke_static.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeStatic + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeStatic) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: mips/op_invoke_interface.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeInterface + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeInterface) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_return_void_no_barrier: /* 0x73 */ +/* File: mips/op_return_void_no_barrier.S */ + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqz ra, 1f + JAL(MterpSuspendCheck) # (self) +1: + move v0, zero + move v1, zero + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: mips/op_invoke_virtual_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtualRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtualRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: mips/op_invoke_super_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeSuperRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeSuperRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: mips/op_invoke_direct_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeDirectRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeDirectRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: mips/op_invoke_static_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeStaticRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeStaticRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: mips/op_invoke_interface_range.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeInterfaceRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeInterfaceRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: mips/op_unused_79.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: mips/op_unused_7a.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: mips/op_neg_int.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + negu a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: mips/op_not_int.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + not a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: mips/op_neg_long.S */ +/* File: mips/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + negu v0, a0 # optional op + negu v1, a1; sltu a0, zero, v0; subu v1, v1, a0 # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: mips/op_not_long.S */ +/* File: mips/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + not a0, a0 # optional op + not a1, a1 # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: mips/op_neg_float.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + addu a0, a0, 0x80000000 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: mips/op_neg_double.S */ +/* File: mips/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be MIPS instruction or a function call. + * + * For: neg-long, not-long, neg-double, + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(a0, a1, a3) # a0/a1 <- vAA + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + addu a1, a1, 0x80000000 # a0/a1 <- op, a2-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: mips/op_int_to_long.S */ +/* File: mips/unopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + sra a1, a0, 31 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vA/vA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 10-11 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: mips/op_int_to_float.S */ +/* File: mips/funop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: int-to-float, float-to-int + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t0 <- A+ + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.s.w fv0, fa0 + +.Lop_int_to_float_set_vreg_f: + SET_VREG_F(fv0, rOBJ) + GET_INST_OPCODE(t1) # extract opcode from rINST + GOTO_OPCODE(t1) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: mips/op_int_to_double.S */ +/* File: mips/funopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.d.w fv0, fa0 + +.Lop_int_to_double_set_vreg: + SET_VREG64_F(fv0, fv0f, rOBJ) # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: mips/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: mips/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + GET_OPB(a1) # a1 <- B from 15:12 + GET_OPA4(a0) # a0 <- A from 11:8 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_VREG(a2, a1) # a2 <- fp[B] + GET_INST_OPCODE(t0) # t0 <- opcode from rINST + .if 0 + SET_VREG_OBJECT(a2, a0) # fp[A] <- a2 + .else + SET_VREG(a2, a0) # fp[A] <- a2 + .endif + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: mips/op_long_to_float.S */ +/* File: mips/unopNarrower.S */ + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(rARG0, rARG1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + JAL(__floatdisf) + +.Lop_long_to_float_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: mips/op_long_to_double.S */ +/* File: mips/funopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be a MIPS instruction or a function call. + * + * long-to-double, double-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64(rARG0, rARG1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + JAL(__floatdidf) # a0/a1 <- op, a2-a3 changed + +.Lop_long_to_double_set_vreg: + SET_VREG64_F(fv0, fv0f, rOBJ) # vAA <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: mips/op_float_to_int.S */ +/* File: mips/funop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: int-to-float, float-to-int + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t0 <- A+ + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + b f2i_doconv + +.Lop_float_to_int_set_vreg_f: + SET_VREG_F(fv0, rOBJ) + GET_INST_OPCODE(t1) # extract opcode from rINST + GOTO_OPCODE(t1) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: mips/op_float_to_long.S */ +/* File: mips/funopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + b f2l_doconv + +.Lop_float_to_long_set_vreg: + SET_VREG64(rRESULT0, rRESULT1, rOBJ) # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: mips/op_float_to_double.S */ +/* File: mips/funopWider.S */ + /* + * Generic 32bit-to-64bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0", where + * "result" is a 64-bit quantity in a0/a1. + * + * For: int-to-double, float-to-long, float-to-double + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.d.s fv0, fa0 + +.Lop_float_to_double_set_vreg: + SET_VREG64_F(fv0, fv0f, rOBJ) # vA/vA+1 <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: mips/op_double_to_int.S */ +/* File: mips/unopNarrower.S */ + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64_F(fa0, fa0f, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + b d2i_doconv + +.Lop_double_to_int_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* + * Convert the double in a0/a1 to an int in a0. + * + * We have to clip values to int min/max per the specification. The + * expected common case is a "reasonable" value that converts directly + * to modest integer. The EABI convert function isn't doing this for us. + */ + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: mips/op_double_to_long.S */ +/* File: mips/funopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0/a1". + * This could be a MIPS instruction or a function call. + * + * long-to-double, double-to-long + */ + /* unop vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64_F(fa0, fa0f, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + b d2l_doconv # a0/a1 <- op, a2-a3 changed + +.Lop_double_to_long_set_vreg: + SET_VREG64(rRESULT0, rRESULT1, rOBJ) # vAA <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + /* 12-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: mips/op_double_to_float.S */ +/* File: mips/unopNarrower.S */ + /* + * Generic 64bit-to-32bit unary operation. Provide an "instr" line + * that specifies an instruction that performs "result = op a0/a1", where + * "result" is a 32-bit quantity in a0. + * + * For: long-to-float, double-to-int, double-to-float + * If hard floating point support is available, use fa0 as the parameter, + * except for long-to-float opcode. + * (This would work for long-to-int, but that instruction is actually + * an exact match for OP_MOVE.) + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(rOBJ) # t1 <- A+ + EAS2(a3, rFP, a3) # a3 <- &fp[B] + LOAD64_F(fa0, fa0f, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + cvt.s.d fv0, fa0 + +.Lop_double_to_float_set_vreg_f: + SET_VREG_F(fv0, rOBJ) # vA <- result0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: mips/op_int_to_byte.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sll a0, a0, 24 # optional op + sra a0, a0, 24 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: mips/op_int_to_char.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + # optional op + and a0, 0xffff # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: mips/op_int_to_short.S */ +/* File: mips/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op a0". + * This could be a MIPS instruction or a function call. + * + * for: neg-int, not-int, neg-float, int-to-float, float-to-int, + * int-to-byte, int-to-char, int-to-short + */ + /* unop vA, vB */ + GET_OPB(a3) # a3 <- B + GET_OPA4(t0) # t0 <- A+ + GET_VREG(a0, a3) # a0 <- vB + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sll a0, 16 # optional op + sra a0, 16 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t1) # extract opcode from rINST + SET_VREG_GOTO(a0, t0, t1) # vAA <- result0 + /* 9-10 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: mips/op_add_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: mips/op_sub_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: mips/op_mul_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: mips/op_div_int.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#else +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: mips/op_rem_int.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#else +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: mips/op_and_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: mips/op_or_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: mips/op_xor_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: mips/op_shl_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: mips/op_shr_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: mips/op_ushr_int.S */ +/* File: mips/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG(a1, a3) # a1 <- vCC + GET_VREG(a0, a2) # a0 <- vBB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 11-14 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: mips/op_add_long.S */ +/* + * The compiler generates the following sequence for + * [v1 v0] = [a1 a0] + [a3 a2]; + * addu v0,a2,a0 + * addu a1,a3,a1 + * sltu v1,v0,a2 + * addu v1,v1,a1 + */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + addu v0, a2, a0 # optional op + addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: mips/op_sub_long.S */ +/* + * For little endian the code sequence looks as follows: + * subu v0,a0,a2 + * subu v1,a1,a3 + * sltu a0,a0,v0 + * subu v1,v1,a0 + */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + subu v0, a0, a2 # optional op + subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: mips/op_mul_long.S */ + /* + * Signed 64-bit integer multiply. + * a1 a0 + * x a3 a2 + * ------------- + * a2a1 a2a0 + * a3a0 + * a3a1 (<= unused) + * --------------- + * v1 v0 + */ + /* mul-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + and t0, a0, 255 # a2 <- BB + srl t1, a0, 8 # a3 <- CC + EAS2(t0, rFP, t0) # t0 <- &fp[BB] + LOAD64(a0, a1, t0) # a0/a1 <- vBB/vBB+1 + + EAS2(t1, rFP, t1) # t0 <- &fp[CC] + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 +#endif + mul t0, a2, a1 # t0= a2a1 + addu v1, v1, t1 # v1+= hi(a2a0) + addu v1, v1, t0 # v1= a3a0 + a2a1; + + GET_OPA(a0) # a0 <- AA + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + b .Lop_mul_long_finish + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: mips/op_div_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + JAL(__divdi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: mips/op_rem_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + JAL(__moddi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: mips/op_and_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + and a0, a0, a2 # optional op + and a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a0, a1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: mips/op_or_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + or a0, a0, a2 # optional op + or a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a0, a1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: mips/op_xor_long.S */ +/* File: mips/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * for: add-long, sub-long, div-long, rem-long, and-long, or-long, + * xor-long + * + * IMPORTANT: you may specify "chkzero" or "preinstr" but not both. + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64(a0, a1, a2) # a0/a1 <- vBB/vBB+1 + LOAD64(a2, a3, t1) # a2/a3 <- vCC/vCC+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + xor a0, a0, a2 # optional op + xor a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64_GOTO(a0, a1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + /* 14-17 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: mips/op_shl_long.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shl-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t2) # t2 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .Lop_shl_long_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: mips/op_shr_long.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* shr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(t3) # t3 <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_shr_long_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/VAA+1 <- v0/v0 + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: mips/op_ushr_long.S */ + /* + * Long integer shift. This is different from the generic 32/64-bit + * binary operations because vAA/vBB are 64-bit but vCC (the shift + * distance) is 32-bit. Also, Dalvik requires us to mask off the low + * 6 bits of the shift distance. + */ + /* ushr-long vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # rOBJ <- AA + and a3, a0, 255 # a3 <- BB + srl a0, a0, 8 # a0 <- CC + EAS2(a3, rFP, a3) # a3 <- &fp[BB] + GET_VREG(a2, a0) # a2 <- vCC + LOAD64(a0, a1, a3) # a0/a1 <- vBB/vBB+1 + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_ushr_long_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- v0/v1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: mips/op_add_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + add.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: mips/op_sub_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + sub.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: mips/op_mul_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + mul.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: mips/op_div_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div.s fv0, fa0, fa1 # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: mips/op_rem_float.S */ +/* File: mips/fbinop.S */ + /* + * Generic 32-bit binary float operation. + * + * For: add-fp, sub-fp, mul-fp, div-fp, rem-fp + */ + + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + srl a3, a0, 8 # a3 <- CC + and a2, a0, 255 # a2 <- BB + GET_VREG_F(fa1, a3) # a1 <- vCC + GET_VREG_F(fa0, a2) # a0 <- vBB + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + JAL(fmodf) # f0 = result + SET_VREG_F(fv0, rOBJ) # vAA <- fv0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: mips/op_add_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + add.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_add_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: mips/op_sub_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + sub.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_sub_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: mips/op_mul_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + mul.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_mul_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: mips/op_div_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + div.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_div_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: mips/op_rem_double.S */ +/* File: mips/fbinopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * for: add-double, sub-double, mul-double, div-double, + * rem-double + * + */ + /* binop vAA, vBB, vCC */ + FETCH(a0, 1) # a0 <- CCBB + GET_OPA(rOBJ) # s5 <- AA + and a2, a0, 255 # a2 <- BB + srl a3, a0, 8 # a3 <- CC + EAS2(a2, rFP, a2) # a2 <- &fp[BB] + EAS2(t1, rFP, a3) # a3 <- &fp[CC] + LOAD64_F(fa0, fa0f, a2) + LOAD64_F(fa1, fa1f, t1) + + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + JAL(fmod) + SET_VREG64_F(fv0, fv0f, rOBJ) + b .Lop_rem_double_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: mips/op_add_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: mips/op_sub_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: mips/op_mul_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: mips/op_div_int_2addr.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: mips/op_rem_int_2addr.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: mips/op_and_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: mips/op_or_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: mips/op_xor_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: mips/op_shl_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: mips/op_shr_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: mips/op_ushr_int_2addr.S */ +/* File: mips/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a0, rOBJ) # a0 <- vA + GET_VREG(a1, a3) # a1 <- vB + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: mips/op_add_long_2addr.S */ +/* + * See op_add_long.S for details + */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + addu v0, a2, a0 # optional op + addu a1, a3, a1; sltu v1, v0, a2; addu v1, v1, a1 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: mips/op_sub_long_2addr.S */ +/* + * See op_sub_long.S for more details + */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + subu v0, a0, a2 # optional op + subu v1, a1, a3; sltu a0, a0, v0; subu v1, v1, a0 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: mips/op_mul_long_2addr.S */ + /* + * See op_mul_long.S for more details + */ + /* mul-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # vAA.low / high + + GET_OPB(t1) # t1 <- B + EAS2(t1, rFP, t1) # t1 <- &fp[B] + LOAD64(a2, a3, t1) # vBB.low / high + + mul v1, a3, a0 # v1= a3a0 +#ifdef MIPS32REVGE6 + mulu v0, a2, a0 # v0= a2a0 + muhu t1, a2, a0 +#else + multu a2, a0 + mfhi t1 + mflo v0 # v0= a2a0 + #endif + mul t2, a2, a1 # t2= a2a1 + addu v1, v1, t1 # v1= a3a0 + hi(a2a0) + addu v1, v1, t2 # v1= v1 + a2a1; + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t1) # extract opcode from rINST + # vAA <- v0 (low) + SET_VREG64(v0, v1, rOBJ) # vAA+1 <- v1 (high) + GOTO_OPCODE(t1) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: mips/op_div_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + JAL(__divdi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: mips/op_rem_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 1 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + # optional op + JAL(__moddi3) # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, rOBJ) # vAA/vAA+1 <- v0/v1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: mips/op_and_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + and a0, a0, a2 # optional op + and a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA/vAA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: mips/op_or_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + or a0, a0, a2 # optional op + or a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA/vAA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: mips/op_xor_long_2addr.S */ +/* File: mips/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-long/2addr, sub-long/2addr, div-long/2addr, rem-long/2addr, + * and-long/2addr, or-long/2addr, xor-long/2addr + * rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64(a2, a3, a1) # a2/a3 <- vBB/vBB+1 + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + .if 0 + or t0, a2, a3 # second arg (a2-a3) is zero? + beqz t0, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + xor a0, a0, a2 # optional op + xor a1, a1, a3 # result <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, rOBJ) # vAA/vAA+1 <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + /* 12-15 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: mips/op_shl_long_2addr.S */ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shl-long/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t2, rFP, rOBJ) # t2 <- &fp[A] + LOAD64(a0, a1, t2) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v1, a2, 0x20 # shift< shift & 0x20 + sll v0, a0, a2 # rlo<- alo << (shift&31) + bnez v1, .Lop_shl_long_2addr_finish + not v1, a2 # rhi<- 31-shift (shift is 5b) + srl a0, 1 + srl a0, v1 # alo<- alo >> (32-(shift&31)) + sll v1, a1, a2 # rhi<- ahi << (shift&31) + or v1, a0 # rhi<- rhi | alo + SET_VREG64_GOTO(v0, v1, rOBJ, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: mips/op_shr_long_2addr.S */ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shr-long/2addr vA, vB */ + GET_OPA4(t2) # t2 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t2) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + sra v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_shr_long_2addr_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-shift (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t2, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips/op_ushr_long_2addr.S */ + /* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* ushr-long/2addr vA, vB */ + GET_OPA4(t3) # t3 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG(a2, a3) # a2 <- vB + EAS2(t0, rFP, t3) # t0 <- &fp[A] + LOAD64(a0, a1, t0) # a0/a1 <- vAA/vAA+1 + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + + andi v0, a2, 0x20 # shift & 0x20 + srl v1, a1, a2 # rhi<- ahi >> (shift&31) + bnez v0, .Lop_ushr_long_2addr_finish + srl v0, a0, a2 # rlo<- alo >> (shift&31) + not a0, a2 # alo<- 31-n (shift is 5b) + sll a1, 1 + sll a1, a0 # ahi<- ahi << (32-(shift&31)) + or v0, a1 # rlo<- rlo | ahi + SET_VREG64_GOTO(v0, v1, t3, t0) # vAA/vAA+1 <- a0/a1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: mips/op_add_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + add.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: mips/op_sub_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + sub.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: mips/op_mul_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + mul.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: mips/op_div_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + div.s fv0, fa0, fa1 + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: mips/op_rem_float_2addr.S */ +/* File: mips/fbinop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, + * div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # t1 <- A+ + GET_OPB(a3) # a3 <- B + GET_VREG_F(fa0, rOBJ) + GET_VREG_F(fa1, a3) + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + + JAL(fmodf) + SET_VREG_F(fv0, rOBJ) # vAA <- result + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: mips/op_add_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + add.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: mips/op_sub_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + sub.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: mips/op_mul_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + mul.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: mips/op_div_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + div.d fv0, fa0, fa1 + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: mips/op_rem_double_2addr.S */ +/* File: mips/fbinopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0-a1 op a2-a3". + * This could be an MIPS instruction or a function call. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, + * div-double/2addr, rem-double/2addr + */ + /* binop/2addr vA, vB */ + GET_OPA4(rOBJ) # rOBJ <- A+ + GET_OPB(a1) # a1 <- B + EAS2(a1, rFP, a1) # a1 <- &fp[B] + EAS2(t0, rFP, rOBJ) # t0 <- &fp[A] + LOAD64_F(fa0, fa0f, t0) + LOAD64_F(fa1, fa1f, a1) + + FETCH_ADVANCE_INST(1) # advance rPC, load rINST + JAL(fmod) + SET_VREG64_F(fv0, fv0f, rOBJ) + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: mips/op_add_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: mips/op_rsub_int.S */ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: mips/op_mul_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: mips/op_div_int_lit16.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: mips/op_rem_int_lit16.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#else +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 1 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: mips/op_and_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: mips/op_or_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: mips/op_xor_int_lit16.S */ +/* File: mips/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + # binop/lit16 vA, vB, /* +CCCC */ + FETCH_S(a1, 1) # a1 <- ssssCCCC (sign-extended) + GET_OPB(a2) # a2 <- B + GET_OPA(rOBJ) # rOBJ <- A+ + GET_VREG(a0, a2) # a0 <- vB + and rOBJ, rOBJ, 15 + .if 0 + # cmp a1, 0; is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-13 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: mips/op_add_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips/op_rsub_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: mips/op_mul_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: mips/op_div_int_lit8.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#else +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mflo a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: mips/op_rem_int_lit8.S */ +#ifdef MIPS32REVGE6 +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#else +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 1 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + div zero, a0, a1 # optional op + mfhi a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + +#endif + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: mips/op_and_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: mips/op_or_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: mips/op_xor_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: mips/op_shl_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: mips/op_shr_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips/op_ushr_int_lit8.S */ +/* File: mips/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + # binop/lit8 vAA, vBB, /* +CC */ + FETCH_S(a3, 1) # a3 <- ssssCCBB (sign-extended for CC) + GET_OPA(rOBJ) # rOBJ <- AA + and a2, a3, 255 # a2 <- BB + GET_VREG(a0, a2) # a0 <- vBB + sra a1, a3, 8 # a1 <- ssssssCC (sign extended) + .if 0 + # is second operand zero? + beqz a1, common_errDivideByZero + .endif + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, rOBJ, t0) # vAA <- a0 + /* 10-12 instructions */ + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lw a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: mips/op_iget_wide_quick.S */ + # iget-wide-quick vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 # t0 <- a3 + a1 + LOAD64(a0, a1, t0) # a0 <- obj.field (64 bits, aligned) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(a0, a1, a2) # fp[A] <- a0/a1 + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: mips/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + GET_OPB(a2) # a2 <- B + FETCH(a1, 1) # a1 <- field byte offset + EXPORT_PC() + GET_VREG(a0, a2) # a0 <- object we're operating on + JAL(artIGetObjectFromMterp) # v0 <- GetObj(obj, offset) + lw a3, THREAD_EXCEPTION_OFFSET(rSELF) + GET_OPA4(a2) # a2<- A+ + PREFETCH_INST(2) # load rINST + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT(v0, a2) # fp[A] <- v0 + ADVANCE(2) # advance rPC + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sw a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: mips/op_iput_wide_quick.S */ + # iput-wide-quick vA, vB, offset /* CCCC */ + GET_OPA4(a0) # a0 <- A(+) + GET_OPB(a1) # a1 <- B + GET_VREG(a2, a1) # a2 <- fp[B], the object pointer + # check object for null + beqz a2, common_errNullObject # object was null + EAS2(a3, rFP, a0) # a3 <- &fp[A] + LOAD64(a0, a1, a3) # a0/a1 <- fp[A] + FETCH(a3, 1) # a3 <- field byte offset + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu a2, a2, a3 # obj.field (64 bits, aligned) <- a0/a1 + STORE64(a0, a1, a2) # obj.field (64 bits, aligned) <- a0/a1 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: mips/op_iput_object_quick.S */ + /* For: iput-object-quick */ + # op vA, vB, offset /* CCCC */ + EXPORT_PC() + addu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + JAL(MterpIputObjectQuick) + beqz v0, MterpException + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips/op_invoke_virtual_quick.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtualQuick + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtualQuick) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips/op_invoke_virtual_range_quick.S */ +/* File: mips/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + # op vB, {vD, vE, vF, vG, vA}, class /* CCCC */ + # op {vCCCC..v(CCCC+AA-1)}, meth /* BBBB */ + .extern MterpInvokeVirtualQuickRange + EXPORT_PC() + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + JAL(MterpInvokeVirtualQuickRange) + beqz v0, MterpException + FETCH_ADVANCE_INST(3) + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: mips/op_iput_boolean_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sb a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: mips/op_iput_byte_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sb a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: mips/op_iput_char_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sh a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: mips/op_iput_short_quick.S */ +/* File: mips/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- fp[B], the object pointer + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + beqz a3, common_errNullObject # object was null + GET_VREG(a0, a2) # a0 <- fp[A] + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + addu t0, a3, a1 + sh a0, 0(t0) # obj.field (8/16/32 bits) <- a0 + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: mips/op_iget_boolean_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lbu a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: mips/op_iget_byte_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lb a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: mips/op_iget_char_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lhu a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: mips/op_iget_short_quick.S */ +/* File: mips/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + # op vA, vB, offset /* CCCC */ + GET_OPB(a2) # a2 <- B + GET_VREG(a3, a2) # a3 <- object we're operating on + FETCH(a1, 1) # a1 <- field byte offset + GET_OPA4(a2) # a2 <- A(+) + # check object for null + beqz a3, common_errNullObject # object was null + addu t0, a3, a1 + lh a0, 0(t0) # a0 <- obj.field (8/16/32 bits) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(a0, a2, t0) # fp[A] <- a0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_lambda: /* 0xf3 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_f4: /* 0xf4 */ +/* File: mips/op_unused_f4.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_capture_variable: /* 0xf5 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_create_lambda: /* 0xf6 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_liberate_variable: /* 0xf7 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_box_lambda: /* 0xf8 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unbox_lambda: /* 0xf9 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fa: /* 0xfa */ +/* File: mips/op_unused_fa.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: mips/op_unused_fb.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: mips/op_unused_fc.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: mips/op_unused_fd.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: mips/op_unused_fe.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: mips/op_unused_ff.S */ +/* File: mips/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + + .balign 128 + .size artMterpAsmInstructionStart, .-artMterpAsmInstructionStart + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global artMterpAsmSisterStart + .type artMterpAsmSisterStart, %function + .text + .balign 4 +artMterpAsmSisterStart: + +/* continuation for op_packed_switch */ + +.Lop_packed_switch_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_sparse_switch */ + +.Lop_sparse_switch_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_cmpl_float */ + +.Lop_cmpl_float_nan: + li rTEMP, -1 + +.Lop_cmpl_float_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_cmpg_float */ + +.Lop_cmpg_float_nan: + li rTEMP, 1 + +.Lop_cmpg_float_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_cmpl_double */ + +.Lop_cmpl_double_nan: + li rTEMP, -1 + +.Lop_cmpl_double_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_cmpg_double */ + +.Lop_cmpg_double_nan: + li rTEMP, 1 + +.Lop_cmpg_double_finish: + GET_OPA(rOBJ) + FETCH_ADVANCE_INST(2) # advance rPC, load rINST + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG_GOTO(rTEMP, rOBJ, t0) # vAA <- rTEMP + +/* continuation for op_if_eq */ + +.L_op_if_eq_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_ne */ + +.L_op_if_ne_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_lt */ + +.L_op_if_lt_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_ge */ + +.L_op_if_ge_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_gt */ + +.L_op_if_gt_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_if_le */ + +.L_op_if_le_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_float_to_int */ + +/* + * Not an entry point as it is used only once !! + */ +f2i_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LFLOAT_TO_INT_max + cmp.ule.s ft2, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1nez ft2, .Lop_float_to_int_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + cmp.ule.s ft2, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1nez ft2, .Lop_float_to_int_set_vreg_f + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .Lop_float_to_int_set_vreg_f +#else + l.s fa1, .LFLOAT_TO_INT_max + c.ole.s fcc0, fa1, fa0 + l.s fv0, .LFLOAT_TO_INT_ret_max + bc1t .Lop_float_to_int_set_vreg_f + + l.s fa1, .LFLOAT_TO_INT_min + c.ole.s fcc0, fa0, fa1 + l.s fv0, .LFLOAT_TO_INT_ret_min + bc1t .Lop_float_to_int_set_vreg_f + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .Lop_float_to_int_set_vreg_f +#endif + + trunc.w.s fv0, fa0 + b .Lop_float_to_int_set_vreg_f + +.LFLOAT_TO_INT_max: + .word 0x4f000000 +.LFLOAT_TO_INT_min: + .word 0xcf000000 +.LFLOAT_TO_INT_ret_max: + .word 0x7fffffff +.LFLOAT_TO_INT_ret_min: + .word 0x80000000 + +/* continuation for op_float_to_long */ + +f2l_doconv: +#ifdef MIPS32REVGE6 + l.s fa1, .LLONG_TO_max + cmp.ule.s ft2, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1nez ft2, .Lop_float_to_long_set_vreg + + l.s fa1, .LLONG_TO_min + cmp.ule.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1nez ft2, .Lop_float_to_long_set_vreg + + mov.s fa1, fa0 + cmp.un.s ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .Lop_float_to_long_set_vreg +#else + l.s fa1, .LLONG_TO_max + c.ole.s fcc0, fa1, fa0 + li rRESULT0, ~0 + li rRESULT1, ~0x80000000 + bc1t .Lop_float_to_long_set_vreg + + l.s fa1, .LLONG_TO_min + c.ole.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0x80000000 + bc1t .Lop_float_to_long_set_vreg + + mov.s fa1, fa0 + c.un.s fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .Lop_float_to_long_set_vreg +#endif + + JAL(__fixsfdi) + + b .Lop_float_to_long_set_vreg + +.LLONG_TO_max: + .word 0x5f000000 + +.LLONG_TO_min: + .word 0xdf000000 + +/* continuation for op_double_to_int */ + +d2i_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1nez ft2, .Lop_double_to_int_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1nez ft2, .Lop_double_to_int_set_vreg_f + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li.s fv0, 0 + bc1nez ft2, .Lop_double_to_int_set_vreg_f +#else + la t0, .LDOUBLE_TO_INT_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + l.s fv0, .LDOUBLE_TO_INT_maxret + bc1t .Lop_double_to_int_set_vreg_f + + la t0, .LDOUBLE_TO_INT_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + l.s fv0, .LDOUBLE_TO_INT_minret + bc1t .Lop_double_to_int_set_vreg_f + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li.s fv0, 0 + bc1t .Lop_double_to_int_set_vreg_f +#endif + + trunc.w.d fv0, fa0 + b .Lop_double_to_int_set_vreg_f + +.LDOUBLE_TO_INT_max: + .dword 0x41dfffffffc00000 +.LDOUBLE_TO_INT_min: + .dword 0xc1e0000000000000 # minint, as a double (high word) +.LDOUBLE_TO_INT_maxret: + .word 0x7fffffff +.LDOUBLE_TO_INT_minret: + .word 0x80000000 + +/* continuation for op_double_to_long */ + +d2l_doconv: +#ifdef MIPS32REVGE6 + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .Lop_double_to_long_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + cmp.ule.d ft2, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1nez ft2, .Lop_double_to_long_set_vreg + + mov.d fa1, fa0 + cmp.un.d ft2, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1nez ft2, .Lop_double_to_long_set_vreg +#else + la t0, .LDOUBLE_TO_LONG_max + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa1, fa0 + la t0, .LDOUBLE_TO_LONG_ret_max + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .Lop_double_to_long_set_vreg + + la t0, .LDOUBLE_TO_LONG_min + LOAD64_F(fa1, fa1f, t0) + c.ole.d fcc0, fa0, fa1 + la t0, .LDOUBLE_TO_LONG_ret_min + LOAD64(rRESULT0, rRESULT1, t0) + bc1t .Lop_double_to_long_set_vreg + + mov.d fa1, fa0 + c.un.d fcc0, fa0, fa1 + li rRESULT0, 0 + li rRESULT1, 0 + bc1t .Lop_double_to_long_set_vreg +#endif + JAL(__fixdfdi) + b .Lop_double_to_long_set_vreg + +.LDOUBLE_TO_LONG_max: + .dword 0x43e0000000000000 # maxlong, as a double (high word) +.LDOUBLE_TO_LONG_min: + .dword 0xc3e0000000000000 # minlong, as a double (high word) +.LDOUBLE_TO_LONG_ret_max: + .dword 0x7fffffffffffffff +.LDOUBLE_TO_LONG_ret_min: + .dword 0x8000000000000000 + +/* continuation for op_mul_long */ + +.Lop_mul_long_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + SET_VREG64(v0, v1, a0) # vAA::vAA+1 <- v0(low) :: v1(high) + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_shl_long */ + +.Lop_shl_long_finish: + SET_VREG64_GOTO(zero, v0, t2, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_shr_long */ + +.Lop_shr_long_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t3, t0) # vAA/VAA+1 <- rlo/rhi + +/* continuation for op_ushr_long */ + +.Lop_ushr_long_finish: + SET_VREG64_GOTO(v1, zero, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_add_double */ + +.Lop_add_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_sub_double */ + +.Lop_sub_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_mul_double */ + +.Lop_mul_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_div_double */ + +.Lop_div_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_rem_double */ + +.Lop_rem_double_finish: + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* continuation for op_shl_long_2addr */ + +.Lop_shl_long_2addr_finish: + SET_VREG64_GOTO(zero, v0, rOBJ, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_shr_long_2addr */ + +.Lop_shr_long_2addr_finish: + sra a3, a1, 31 # a3<- sign(ah) + SET_VREG64_GOTO(v1, a3, t2, t0) # vAA/vAA+1 <- rlo/rhi + +/* continuation for op_ushr_long_2addr */ + +.Lop_ushr_long_2addr_finish: + SET_VREG64_GOTO(v1, zero, t3, t0) # vAA/vAA+1 <- rlo/rhi + + .size artMterpAsmSisterStart, .-artMterpAsmSisterStart + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: + + + .global artMterpAsmAltInstructionStart + .type artMterpAsmAltInstructionStart, %function + .text + +artMterpAsmAltInstructionStart = .L_ALT_op_nop +/* ------------------------------ */ + .balign 128 +.L_ALT_op_nop: /* 0x00 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (0 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (1 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (2 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (3 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (4 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (5 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (6 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (7 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (8 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (9 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (10 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (11 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (12 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (13 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (14 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (15 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (16 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (17 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (18 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (19 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (20 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (21 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (22 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (23 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (24 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (25 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (26 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (27 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (28 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (29 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (30 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (31 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (32 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (33 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (34 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (35 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (36 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (37 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (38 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (39 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (40 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (41 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (42 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (43 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (44 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (45 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (46 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (47 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (48 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (49 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (50 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (51 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (52 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (53 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (54 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (55 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (56 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (57 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (58 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (59 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (60 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (61 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (62 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (63 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (64 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (65 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (66 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (67 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (68 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (69 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (70 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (71 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (72 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (73 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (74 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (75 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (76 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (77 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (78 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (79 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (80 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (81 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (82 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (83 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (84 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (85 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (86 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (87 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (88 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (89 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (90 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (91 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (92 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (93 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (94 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (95 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (96 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (97 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (98 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (99 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (100 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (101 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (102 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (103 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (104 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (105 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (106 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (107 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (108 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (109 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (110 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (111 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (112 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (113 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (114 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (115 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (116 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (117 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (118 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (119 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (120 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (121 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (122 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (123 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (124 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (125 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (126 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (127 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (128 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (129 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (130 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (131 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (132 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (133 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (134 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (135 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (136 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (137 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (138 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (139 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (140 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (141 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (142 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (143 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (144 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (145 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (146 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (147 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (148 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (149 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (150 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (151 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (152 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (153 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (154 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (155 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (156 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (157 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (158 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (159 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (160 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (161 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (162 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (163 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (164 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (165 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (166 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (167 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (168 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (169 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (170 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (171 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (172 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (173 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (174 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (175 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (176 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (177 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (178 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (179 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (180 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (181 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (182 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (183 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (184 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (185 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (186 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (187 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (188 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (189 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (190 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (191 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (192 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (193 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (194 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (195 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (196 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (197 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (198 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (199 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (200 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (201 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (202 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (203 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (204 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (205 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (206 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (207 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (208 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (209 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (210 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (211 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (212 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (213 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (214 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (215 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (216 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (217 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (218 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (219 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (220 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (221 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (222 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (223 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (224 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (225 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (226 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (227 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (228 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (229 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (230 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (231 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (232 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (233 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (234 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (235 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (236 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (237 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (238 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (239 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (240 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (241 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (242 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (243 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (244 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (245 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (246 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (247 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (248 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (249 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (250 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (251 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (252 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (253 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (254 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: mips/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC() + la ra, artMterpAsmInstructionStart + (255 * 128) # Addr of primary handler + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh IBASE + move a0, rSELF # arg0 + addu a1, rFP, OFF_FP_SHADOWFRAME # arg1 + la a2, MterpCheckBefore + jalr zero, a2 # Tail call to Mterp(self, shadow_frame) + + .balign 128 + .size artMterpAsmAltInstructionStart, .-artMterpAsmAltInstructionStart + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: +/* File: mips/footer.S */ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * 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. + */ +common_errDivideByZero: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogDivideByZeroException) +#endif + b MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogArrayIndexException) +#endif + b MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNegativeArraySizeException) +#endif + b MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNoSuchMethodException) +#endif + b MterpCommonFallback + +common_errNullObject: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogNullObjectException) +#endif + b MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogExceptionThrownException) +#endif + b MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + lw a2, THREAD_FLAGS_OFFSET(rSELF) + JAL(MterpLogSuspendFallback) +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + lw a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqz a0, MterpFallback # If exception, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpHandleException) # (self, shadow_frame) + beqz v0, MterpExceptionReturn # no local catch, back to caller. + lw a0, OFF_FP_CODE_ITEM(rFP) + lw a1, OFF_FP_DEX_PC(rFP) + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) + addu rPC, a0, CODEITEM_INSNS_OFFSET + sll a1, a1, 1 + addu rPC, rPC, a1 # generate new dex_pc_ptr + /* Do we need to switch interpreters? */ + JAL(MterpShouldSwitchInterpreters) + bnez v0, MterpFallback + /* resume execution at catch block */ + EXPORT_PC() + FETCH_INST() + GET_INST_OPCODE(t0) + GOTO_OPCODE(t0) + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + lw rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) # refresh rIBASE + and ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, 1f + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction +1: + EXPORT_PC() + move a0, rSELF + JAL(MterpSuspendCheck) # (self) + bnez v0, MterpFallback + GET_INST_OPCODE(t0) # extract opcode from rINST + GOTO_OPCODE(t0) # jump to next instruction + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + JAL(MterpLogOSR) +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC() +#if MTERP_LOGGING + move a0, rSELF + addu a1, rFP, OFF_FP_SHADOWFRAME + JAL(MterpLogFallback) +#endif +MterpCommonFallback: + move v0, zero # signal retry with reference interpreter. + b MterpDone +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and LR. Here we restore SP, restore the registers, and then restore + * LR to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +MterpReturn: + lw a2, OFF_FP_RESULT_REGISTER(rFP) + sw v0, 0(a2) + sw v1, 4(a2) + li v0, 1 # signal return to caller. +MterpDone: +/* Restore from the stack and return. Frame size = STACK_SIZE */ + STACK_LOAD_FULL() + jalr zero, ra + + .end ExecuteMterpImpl + diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S new file mode 100644 index 0000000000000000000000000000000000000000..a17252b2f80761c1d78897934353ca651359f97a --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -0,0 +1,12425 @@ +/* + * This file was generated automatically by gen-mterp.py for 'mips64'. + * + * --> DO NOT EDIT <-- + */ + +/* File: mips64/header.S */ +/* + * 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 + +/* TODO: add the missing file and use its FP register definitions. */ +/* #include */ +/* FP register definitions */ +#define f0 $f0 +#define f1 $f1 +#define f2 $f2 +#define f3 $f3 +#define f12 $f12 +#define f13 $f13 + +/* + * It looks like the GNU assembler currently does not support the blec and bgtc + * idioms, which should translate into bgec and bltc respectively with swapped + * left and right register operands. + * TODO: remove these macros when the assembler is fixed. + */ +.macro blec lreg, rreg, target + bgec \rreg, \lreg, \target +.endm +.macro bgtc lreg, rreg, target + bltc \rreg, \lreg, \target +.endm + +/* +Mterp and MIPS64 notes: + +The following registers have fixed assignments: + + reg nick purpose + s0 rPC interpreted program counter, used for fetching instructions + s1 rFP interpreted frame pointer, used for accessing locals and args + s2 rSELF self (Thread) pointer + s3 rINST first 16-bit code unit of current instruction + s4 rIBASE interpreted instruction base pointer, used for computed goto + s5 rREFS base of object references in shadow frame (ideally, we'll get rid of this later). +*/ + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rPC s0 +#define rFP s1 +#define rSELF s2 +#define rINST s3 +#define rIBASE s4 +#define rREFS s5 + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#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 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + sd rPC, OFF_FP_DEX_PC_PTR(rFP) +.endm + +/* + * Refresh handler table. + */ +.macro REFRESH_IBASE + ld rIBASE, THREAD_CURRENT_IBASE_OFFSET(rSELF) +.endm + +/* + * Fetch the next instruction from rPC into rINST. Does not advance rPC. + */ +.macro FETCH_INST + lhu rINST, 0(rPC) +.endm + +/* Advance rPC by some number of code units. */ +.macro ADVANCE count + daddu rPC, rPC, (\count) * 2 +.endm + +/* + * Fetch the next instruction from the specified offset. Advances rPC + * to point to the next instruction. + * + * This must come AFTER anything that can throw an exception, or the + * exception catch may miss. (This also implies that it must come after + * EXPORT_PC.) + */ +.macro FETCH_ADVANCE_INST count + ADVANCE \count + FETCH_INST +.endm + +/* + * Similar to FETCH_ADVANCE_INST, but does not update rPC. Used to load + * rINST ahead of possible exception point. Be sure to manually advance rPC + * later. + */ +.macro PREFETCH_INST count + lhu rINST, ((\count) * 2)(rPC) +.endm + +/* + * Put the instruction's opcode field into the specified register. + */ +.macro GET_INST_OPCODE reg + and \reg, rINST, 255 +.endm + +/* + * Begin executing the opcode in _reg. + */ +.macro GOTO_OPCODE reg + .set noat + sll AT, \reg, 7 + daddu AT, rIBASE, AT + jic AT, 0 + .set at +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + * Note, GET_VREG does sign extension to 64 bits while + * GET_VREG_U does zero extension to 64 bits. + * One is useful for arithmetic while the other is + * useful for storing the result value as 64-bit. + */ +.macro GET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + .set at +.endm +.macro GET_VREG_U reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwu \reg, 0(AT) + .set at +.endm +.macro GET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + .set at +.endm +.macro SET_VREG reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm +.macro SET_VREG_OBJECT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw \reg, 0(AT) + .set at +.endm +.macro SET_VREG_FLOAT reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + .set at +.endm + +/* + * Get/set the 64-bit value from a Dalvik register. + * Avoid unaligned memory accesses. + * Note, SET_VREG_WIDE clobbers the register containing the value being stored. + * Note, SET_VREG_DOUBLE clobbers the register containing the Dalvik register number. + */ +.macro GET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lw \reg, 0(AT) + lw AT, 4(AT) + dinsu \reg, AT, 32, 32 + .set at +.endm +.macro GET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + lwc1 \reg, 0(AT) + lw AT, 4(AT) + mthc1 AT, \reg + .set at +.endm +.macro SET_VREG_WIDE reg, vreg + .set noat + dlsa AT, \vreg, rFP, 2 + sw \reg, 0(AT) + drotr32 \reg, \reg, 0 + sw \reg, 4(AT) + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + .set at +.endm +.macro SET_VREG_DOUBLE reg, vreg + .set noat + dlsa AT, \vreg, rREFS, 2 + sw zero, 0(AT) + sw zero, 4(AT) + dlsa AT, \vreg, rFP, 2 + swc1 \reg, 0(AT) + mfhc1 \vreg, \reg + sw \vreg, 4(AT) + .set at +.endm + +/* + * On-stack offsets for spilling/unspilling callee-saved registers + * and the frame size. + */ +#define STACK_OFFSET_RA 0 +#define STACK_OFFSET_GP 8 +#define STACK_OFFSET_S0 16 +#define STACK_OFFSET_S1 24 +#define STACK_OFFSET_S2 32 +#define STACK_OFFSET_S3 40 +#define STACK_OFFSET_S4 48 +#define STACK_OFFSET_S5 56 +#define STACK_SIZE 64 + +/* Constants for float/double_to_int/long conversions */ +#define INT_MIN 0x80000000 +#define INT_MIN_AS_FLOAT 0xCF000000 +#define INT_MIN_AS_DOUBLE 0xC1E0000000000000 +#define LONG_MIN 0x8000000000000000 +#define LONG_MIN_AS_FLOAT 0xDF000000 +#define LONG_MIN_AS_DOUBLE 0xC3E0000000000000 + +/* File: mips64/entry.S */ +/* + * 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. + */ + +/* + * Interpreter entry point. + */ + + .set reorder + + .text + .global ExecuteMterpImpl + .type ExecuteMterpImpl, %function + .balign 16 +/* + * On entry: + * a0 Thread* self + * a1 code_item + * a2 ShadowFrame + * a3 JValue* result_register + * + */ +ExecuteMterpImpl: + .cfi_startproc + .cpsetup t9, t8, ExecuteMterpImpl + + .cfi_def_cfa sp, 0 + daddu sp, sp, -STACK_SIZE + .cfi_adjust_cfa_offset STACK_SIZE + + sd t8, STACK_OFFSET_GP(sp) + .cfi_rel_offset 28, STACK_OFFSET_GP + sd ra, STACK_OFFSET_RA(sp) + .cfi_rel_offset 31, STACK_OFFSET_RA + + sd s0, STACK_OFFSET_S0(sp) + .cfi_rel_offset 16, STACK_OFFSET_S0 + sd s1, STACK_OFFSET_S1(sp) + .cfi_rel_offset 17, STACK_OFFSET_S1 + sd s2, STACK_OFFSET_S2(sp) + .cfi_rel_offset 18, STACK_OFFSET_S2 + sd s3, STACK_OFFSET_S3(sp) + .cfi_rel_offset 19, STACK_OFFSET_S3 + sd s4, STACK_OFFSET_S4(sp) + .cfi_rel_offset 20, STACK_OFFSET_S4 + sd s5, STACK_OFFSET_S5(sp) + .cfi_rel_offset 21, STACK_OFFSET_S5 + + /* Remember the return register */ + sd a3, SHADOWFRAME_RESULT_REGISTER_OFFSET(a2) + + /* Remember the code_item */ + sd a1, SHADOWFRAME_CODE_ITEM_OFFSET(a2) + + /* set up "named" registers */ + move rSELF, a0 + daddu rFP, a2, SHADOWFRAME_VREGS_OFFSET + lw v0, SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(a2) + dlsa rREFS, v0, rFP, 2 + daddu rPC, a1, CODEITEM_INSNS_OFFSET + lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) + dlsa rPC, v0, rPC, 1 + EXPORT_PC + + /* Starting ibase */ + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* NOTE: no fallthrough */ + + + .global artMterpAsmInstructionStart + .type artMterpAsmInstructionStart, %function +artMterpAsmInstructionStart = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: mips64/op_nop.S */ + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: mips64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: mips64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vAA <- vBBBB + .else + SET_VREG a0, a2 # vAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: mips64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vAAAA <- vBBBB + .else + SET_VREG a0, a2 # vAAAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: mips64/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + ext a3, rINST, 12, 4 # a3 <- B + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG_WIDE a0, a3 # a0 <- vB + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: mips64/op_move_wide_from16.S */ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: mips64/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + GET_VREG_WIDE a0, a3 # a0 <- vBBBB + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAAAA <- vBBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: mips64/op_move_object.S */ +/* File: mips64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: mips64/op_move_object_from16.S */ +/* File: mips64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + lhu a3, 2(rPC) # a3 <- BBBB + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vAA <- vBBBB + .else + SET_VREG a0, a2 # vAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: mips64/op_move_object_16.S */ +/* File: mips64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + lhu a3, 4(rPC) # a3 <- BBBB + lhu a2, 2(rPC) # a2 <- AAAA + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vBBBB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vAAAA <- vBBBB + .else + SET_VREG a0, a2 # vAAAA <- vBBBB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: mips64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vAA <- result + .else + SET_VREG a0, a2 # vAA <- result + .endif + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: mips64/op_move_result_wide.S */ + /* for: move-result-wide */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + ld a0, 0(a0) # a0 <- result.j + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: mips64/op_move_result_object.S */ +/* File: mips64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + srl a2, rINST, 8 # a2 <- AA + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + ld a0, OFF_FP_RESULT_REGISTER(rFP) # get pointer to result JType + lw a0, 0(a0) # a0 <- result.i + GET_INST_OPCODE v0 # extract opcode from rINST + .if 1 + SET_VREG_OBJECT a0, a2 # vAA <- result + .else + SET_VREG a0, a2 # vAA <- result + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: mips64/op_move_exception.S */ + /* move-exception vAA */ + srl a2, rINST, 8 # a2 <- AA + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) # load exception obj + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + SET_VREG_OBJECT a0, a2 # vAA <- exception obj + GET_INST_OPCODE v0 # extract opcode from rINST + sd zero, THREAD_EXCEPTION_OFFSET(rSELF) # clear exception + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: mips64/op_return_void.S */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: mips64/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: mips64/op_return_wide.S */ + /* + * Return a 64-bit value. + */ + /* return-wide vAA */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vAA + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: mips64/op_return_object.S */ +/* File: mips64/op_return.S */ + /* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + .extern MterpSuspendCheck + jal MterpThreadFenceForConstructor + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA + b MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: mips64/op_const_4.S */ + /* const/4 vA, #+B */ + ext a2, rINST, 8, 4 # a2 <- A + seh a0, rINST # sign extend B in rINST + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + sra a0, a0, 12 # shift B into its final position + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- +B + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: mips64/op_const_16.S */ + /* const/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: mips64/op_const.S */ + /* const vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: mips64/op_const_high16.S */ + /* const/high16 vAA, #+BBBB0000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + sll a0, a0, 16 # a0 <- BBBB0000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- +BBBB0000 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: mips64/op_const_wide_16.S */ + /* const-wide/16 vAA, #+BBBB */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- sign-extended BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: mips64/op_const_wide_32.S */ + /* const-wide/32 vAA, #+BBBBbbbb */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (high) + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBBbbbb + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: mips64/op_const_wide.S */ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + srl a4, rINST, 8 # a4 <- AA + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a1, 4(rPC) # a1 <- BBBB (low middle) + lh a2, 6(rPC) # a2 <- hhhh (high middle) + lh a3, 8(rPC) # a3 <- HHHH (high) + FETCH_ADVANCE_INST 5 # advance rPC, load rINST + ins a0, a1, 16, 16 # a0 = BBBBbbbb + ins a2, a3, 16, 16 # a2 = HHHHhhhh + dinsu a0, a2, 32, 32 # a0 = HHHHhhhhBBBBbbbb + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- +HHHHhhhhBBBBbbbb + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: mips64/op_const_wide_high16.S */ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + srl a2, rINST, 8 # a2 <- AA + lh a0, 2(rPC) # a0 <- BBBB + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + dsll32 a0, a0, 16 # a0 <- BBBB000000000000 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vAA <- +BBBB000000000000 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: mips64/op_const_string.S */ + /* const/string vAA, String//BBBB */ + .extern MterpConstString + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: mips64/op_const_string_jumbo.S */ + /* const/string vAA, String//BBBBBBBB */ + .extern MterpConstString + EXPORT_PC + lh a0, 2(rPC) # a0 <- bbbb (low) + lh a4, 4(rPC) # a4 <- BBBB (high) + srl a1, rINST, 8 # a1 <- AA + ins a0, a4, 16, 16 # a0 <- BBBBbbbb + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstString # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 3 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 3 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: mips64/op_const_class.S */ + /* const/class vAA, Class//BBBB */ + .extern MterpConstClass + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + daddu a2, rFP, OFF_FP_SHADOWFRAME + move a3, rSELF + jal MterpConstClass # (index, tgt_reg, shadow_frame, self) + PREFETCH_INST 2 # load rINST + bnez v0, MterpPossibleException # let reference interpreter deal with it. + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: mips64/op_monitor_enter.S */ + /* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + .extern artLockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artLockObjectFromCode + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: mips64/op_monitor_exit.S */ + /* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + .extern artUnlockObjectFromCode + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (object) + move a1, rSELF # a1 <- self + jal artUnlockObjectFromCode # v0 <- success for unlock(self, obj) + bnezc v0, MterpException + FETCH_ADVANCE_INST 1 # before throw: advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: mips64/op_check_cast.S */ + /* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class//BBBB */ + .extern MterpCheckCast + EXPORT_PC + lhu a0, 2(rPC) # a0 <- BBBB + srl a1, rINST, 8 # a1 <- AA + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpCheckCast # (index, &obj, method, self) + PREFETCH_INST 2 + bnez v0, MterpPossibleException + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: mips64/op_instance_of.S */ + /* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class//CCCC */ + .extern MterpInstanceOf + EXPORT_PC + lhu a0, 2(rPC) # a0 <- CCCC + srl a1, rINST, 12 # a1 <- B + dlsa a1, a1, rFP, 2 # a1 <- &object + ld a2, OFF_FP_METHOD(rFP) # a2 <- method + move a3, rSELF # a3 <- self + jal MterpInstanceOf # (index, &obj, method, self) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a1, MterpException + ADVANCE 2 # advance rPC + SET_VREG v0, a2 # vA <- v0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: mips64/op_array_length.S */ + /* + * Return the length of an array. + */ + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a0, a1 # a0 <- vB (object ref) + ext a2, rINST, 8, 4 # a2 <- A + beqz a0, common_errNullObject # yup, fail + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- array length + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a3, a2 # vB <- length + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: mips64/op_new_instance.S */ + /* + * Create a new instance of a class. + */ + /* new-instance vAA, class//BBBB */ + .extern MterpNewInstance + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rSELF + move a2, rINST + jal MterpNewInstance # (shadow_frame, self, inst_data) + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: mips64/op_new_array.S */ + /* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class//CCCC */ + .extern MterpNewArray + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpNewArray + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array: /* 0x24 */ +/* File: mips64/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class//CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type//BBBB */ + .extern MterpFilledNewArray + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rSELF + jal MterpFilledNewArray + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array_range: /* 0x25 */ +/* File: mips64/op_filled_new_array_range.S */ +/* File: mips64/op_filled_new_array.S */ + /* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class//CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type//BBBB */ + .extern MterpFilledNewArrayRange + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rSELF + jal MterpFilledNewArrayRange + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_fill_array_data: /* 0x26 */ +/* File: mips64/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + .extern MterpFillArrayData + EXPORT_PC + lh a1, 2(rPC) # a1 <- bbbb (lo) + lh a0, 4(rPC) # a0 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a1, a0, 16, 16 # a1 <- BBBBbbbb + GET_VREG_U a0, a3 # a0 <- vAA (array object) + dlsa a1, a1, rPC, 1 # a1 <- PC + BBBBbbbb*2 (array data off.) + jal MterpFillArrayData # (obj, payload) + beqzc v0, MterpPossibleException # exception? + FETCH_ADVANCE_INST 3 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: mips64/op_throw.S */ + /* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + srl a2, rINST, 8 # a2 <- AA + GET_VREG_U a0, a2 # a0 <- vAA (exception object) + beqzc a0, common_errNullObject + sd a0, THREAD_EXCEPTION_OFFSET(rSELF) # thread->exception <- obj + b MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: mips64/op_goto.S */ + /* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + .extern MterpProfileBranch + srl rINST, rINST, 8 + seb rINST, rINST # rINST <- offset (sign-extended AA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: mips64/op_goto_16.S */ + /* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- offset (sign-extended AAAA) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: mips64/op_goto_32.S */ + /* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Unlike most opcodes, this one is allowed to branch to itself, so + * our "backward branch" test must be "<=0" instead of "<0". + */ + /* goto/32 +AAAAAAAA */ + .extern MterpProfileBranch + lh rINST, 2(rPC) # rINST <- aaaa (low) + lh a1, 4(rPC) # a1 <- AAAA (high) + ins rINST, a1, 16, 16 # rINST <- offset (sign-extended AAAAaaaa) +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: mips64/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBBBBBB */ + .extern MterpDoPackedSwitch + .extern MterpProfileBranch + lh a0, 2(rPC) # a0 <- bbbb (lo) + lh a1, 4(rPC) # a1 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a0, a1, 16, 16 # a0 <- BBBBbbbb + GET_VREG a1, a3 # a1 <- vAA + dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 + jal MterpDoPackedSwitch # v0 <- code-unit branch offset + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: mips64/op_sparse_switch.S */ +/* File: mips64/op_packed_switch.S */ + /* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBBBBBB */ + .extern MterpDoSparseSwitch + .extern MterpProfileBranch + lh a0, 2(rPC) # a0 <- bbbb (lo) + lh a1, 4(rPC) # a1 <- BBBB (hi) + srl a3, rINST, 8 # a3 <- AA + ins a0, a1, 16, 16 # a0 <- BBBBbbbb + GET_VREG a1, a3 # a1 <- vAA + dlsa a0, a0, rPC, 1 # a0 <- PC + BBBBbbbb*2 + jal MterpDoSparseSwitch # v0 <- code-unit branch offset + move rINST, v0 +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + blez a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: mips64/op_cmpl_float.S */ +/* File: mips64/fcmp.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + cmp.eq.s f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 0 + cmp.lt.s f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.s f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: mips64/op_cmpg_float.S */ +/* File: mips64/fcmp.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-float, cmpg-float + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + cmp.eq.s f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 1 + cmp.lt.s f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.s f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: mips64/op_cmpl_double.S */ +/* File: mips64/fcmpWide.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + cmp.eq.d f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 0 + cmp.lt.d f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.d f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: mips64/op_cmpg_double.S */ +/* File: mips64/fcmpWide.S */ + /* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * For: cmpl-double, cmpg-double + */ + /* op vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + cmp.eq.d f2, f0, f1 + li a0, 0 + bc1nez f2, 1f # done if vBB == vCC (ordered) + .if 1 + cmp.lt.d f2, f0, f1 + li a0, -1 + bc1nez f2, 1f # done if vBB < vCC (ordered) + li a0, 1 # vBB > vCC or unordered + .else + cmp.lt.d f2, f1, f0 + li a0, 1 + bc1nez f2, 1f # done if vBB > vCC (ordered) + li a0, -1 # vBB < vCC or unordered + .endif +1: + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: mips64/op_cmp_long.S */ + /* cmp-long vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + slt a2, a0, a1 + slt a0, a1, a0 + subu a0, a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- result + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: mips64/op_if_eq.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + .extern MterpProfileBranch + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + beqc a0, a1, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: mips64/op_if_ne.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + .extern MterpProfileBranch + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + bnec a0, a1, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: mips64/op_if_lt.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + .extern MterpProfileBranch + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + bltc a0, a1, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: mips64/op_if_ge.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + .extern MterpProfileBranch + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + bgec a0, a1, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: mips64/op_if_gt.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + .extern MterpProfileBranch + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + bgtc a0, a1, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: mips64/op_if_le.S */ +/* File: mips64/bincmp.S */ + /* + * Generic two-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-le" you would use "le". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + .extern MterpProfileBranch + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + lh rINST, 2(rPC) # rINST <- offset (sign-extended CCCC) + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + blec a0, a1, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: mips64/op_if_eqz.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + .extern MterpProfileBranch + srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) + GET_VREG a0, a2 # a0 <- vAA + beqzc a0, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: mips64/op_if_nez.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + .extern MterpProfileBranch + srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) + GET_VREG a0, a2 # a0 <- vAA + bnezc a0, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: mips64/op_if_ltz.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + .extern MterpProfileBranch + srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) + GET_VREG a0, a2 # a0 <- vAA + bltzc a0, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: mips64/op_if_gez.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + .extern MterpProfileBranch + srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) + GET_VREG a0, a2 # a0 <- vAA + bgezc a0, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: mips64/op_if_gtz.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + .extern MterpProfileBranch + srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) + GET_VREG a0, a2 # a0 <- vAA + bgtzc a0, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: mips64/op_if_lez.S */ +/* File: mips64/zcmp.S */ + /* + * Generic one-operand compare-and-branch operation. Provide a "condition" + * fragment that specifies the comparison to perform, e.g. for + * "if-lez" you would use "le". + * + * For: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + .extern MterpProfileBranch + srl a2, rINST, 8 # a2 <- AA + lh rINST, 2(rPC) # rINST <- offset (sign-extended BBBB) + GET_VREG a0, a2 # a0 <- vAA + blezc a0, 1f + li rINST, 2 # offset if branch not taken +1: +#if MTERP_PROFILE_BRANCHES + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST + jal MterpProfileBranch # (self, shadow_frame, offset) + bnezc v0, MterpOnStackReplacement # Note: offset must be in rINST +#endif + dlsa rPC, rINST, rPC, 1 # rPC <- rPC + offset * 2 + lw ra, THREAD_FLAGS_OFFSET(rSELF) # Preload flags for MterpCheckSuspendAndContinue + move a0, rINST # a0 <- offset + FETCH_INST # load rINST + bltz a0, MterpCheckSuspendAndContinue # suspend check if backwards branch + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: mips64/op_unused_3e.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: mips64/op_unused_3f.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: mips64/op_unused_40.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: mips64/op_unused_41.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: mips64/op_unused_42.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: mips64/op_unused_43.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 2 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 2 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: mips64/op_aget_wide.S */ + /* + * Array get, 64 bits. vAA <- vBB[vCC]. + * + */ + /* aget-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + lw a3, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) + dinsu a2, a3, 32, 32 # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: mips64/op_aget_object.S */ + /* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + .extern artAGetObjectFromMterp + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + jal artAGetObjectFromMterp # (array, index) + ld a1, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + PREFETCH_INST 2 + bnez a1, MterpException + SET_VREG_OBJECT v0, a4 # vAA <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: mips64/op_aget_boolean.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lbu a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: mips64/op_aget_byte.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: mips64/op_aget_char.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lhu a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: mips64/op_aget_short.S */ +/* File: mips64/op_aget.S */ + /* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + * NOTE: assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + lh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # a2 <- vBB[vCC] + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a2, a4 # vAA <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 2 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 2 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sw a2, MIRROR_INT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: mips64/op_aput_wide.S */ + /* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + */ + /* aput-wide vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + dlsa a0, a1, a0, 3 # a0 <- arrayObj + index*width + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + GET_VREG_WIDE a2, a4 # a2 <- vAA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + sw a2, MIRROR_WIDE_ARRAY_DATA_OFFSET(a0) + dsrl32 a2, a2, 0 + sw a2, (MIRROR_WIDE_ARRAY_DATA_OFFSET+4)(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: mips64/op_aput_object.S */ + /* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + .extern MterpAputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpAputObject + beqzc v0, MterpPossibleException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: mips64/op_aput_boolean.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sb a2, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: mips64/op_aput_byte.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 0 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 0 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sb a2, MIRROR_BYTE_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: mips64/op_aput_char.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sh a2, MIRROR_CHAR_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: mips64/op_aput_short.S */ +/* File: mips64/op_aput.S */ + /* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + * NOTE: this assumes data offset for arrays is the same for all non-wide types. + * If this changes, specialize. + */ + /* op vAA, vBB, vCC */ + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + srl a4, rINST, 8 # a4 <- AA + GET_VREG_U a0, a2 # a0 <- vBB (array object) + GET_VREG a1, a3 # a1 <- vCC (requested index) + beqz a0, common_errNullObject # bail if null array object + lw a3, MIRROR_ARRAY_LENGTH_OFFSET(a0) # a3 <- arrayObj->length + .if 1 + # [d]lsa does not support shift count of 0. + dlsa a0, a1, a0, 1 # a0 <- arrayObj + index*width + .else + daddu a0, a1, a0 # a0 <- arrayObj + index*width + .endif + bgeu a1, a3, common_errArrayIndex # unsigned compare: index >= length, bail + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_VREG a2, a4 # a2 <- vAA + GET_INST_OPCODE v0 # extract opcode from rINST + sh a2, MIRROR_SHORT_ARRAY_DATA_OFFSET(a0) # vBB[vCC] <- a2 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGet32InstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGet32InstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: mips64/op_iget_wide.S */ + /* + * 64-bit instance field get. + * + * for: iget-wide + */ + .extern artGet64InstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGet64InstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_WIDE v0, a2 # fp[A] <- v0 + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: mips64/op_iget_object.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetObjInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetObjInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 1 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: mips64/op_iget_boolean.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetBooleanInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetBooleanInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: mips64/op_iget_byte.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetByteInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetByteInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: mips64/op_iget_char.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetCharInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetCharInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: mips64/op_iget_short.S */ +/* File: mips64/op_iget.S */ + /* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + .extern artGetShortInstanceFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ld a2, OFF_FP_METHOD(rFP) # a2 <- referrer + move a3, rSELF # a3 <- self + jal artGetShortInstanceFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + .else + SET_VREG v0, a2 # fp[A] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet32InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet32InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: mips64/op_iput_wide.S */ + /* iput-wide vA, vB, field//CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + dlsa a2, a2, rFP, 2 # a2 <- &fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet64InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: mips64/op_iput_object.S */ + .extern MterpIputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpIputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: mips64/op_iput_boolean.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet8InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: mips64/op_iput_byte.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet8InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: mips64/op_iput_char.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet16InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: mips64/op_iput_short.S */ +/* File: mips64/op_iput.S */ + /* + * General 32-bit instance field put. + * + * for: iput, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field//CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref CCCC + srl a1, rINST, 12 # a1 <- B + GET_VREG_U a1, a1 # a1 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + GET_VREG a2, a2 # a2 <- fp[A] + ld a3, OFF_FP_METHOD(rFP) # a3 <- referrer + PREFETCH_INST 2 + jal artSet16InstanceFromMterp + bnez v0, MterpPossibleException # bail out + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGet32StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGet32StaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: mips64/op_sget_wide.S */ + /* + * SGET_WIDE handler wrapper. + * + */ + /* sget-wide vAA, field//BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGet64StaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a4, rINST, 8 # a4 <- AA + bnez a3, MterpException # bail out + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE v0, a4 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: mips64/op_sget_object.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetObjStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetObjStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 1 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: mips64/op_sget_boolean.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetBooleanStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetBooleanStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + and v0, v0, 0xff + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: mips64/op_sget_byte.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetByteStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetByteStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + seb v0, v0 + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: mips64/op_sget_char.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetCharStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetCharStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + and v0, v0, 0xffff + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: mips64/op_sget_short.S */ +/* File: mips64/op_sget.S */ + /* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field//BBBB */ + .extern artGetShortStaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + move a2, rSELF + jal artGetShortStaticFromCode + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + srl a2, rINST, 8 # a2 <- AA + seh v0, v0 + PREFETCH_INST 2 + bnez a3, MterpException # bail out + .if 0 + SET_VREG_OBJECT v0, a2 # fp[AA] <- v0 + .else + SET_VREG v0, a2 # fp[AA] <- v0 + .endif + ADVANCE 2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet32StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet32StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: mips64/op_sput_wide.S */ + /* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field//BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + ld a1, OFF_FP_METHOD(rFP) + srl a2, rINST, 8 # a2 <- AA + dlsa a2, a2, rFP, 2 + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet64IndirectStaticFromMterp + bnezc v0, MterpException # 0 on success, -1 on failure + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: mips64/op_sput_object.S */ + .extern MterpSputObject + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + move a3, rSELF + jal MterpSputObject + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: mips64/op_sput_boolean.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet8StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: mips64/op_sput_byte.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet8StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: mips64/op_sput_char.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet16StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: mips64/op_sput_short.S */ +/* File: mips64/op_sput.S */ + /* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field//BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + lhu a0, 2(rPC) # a0 <- field ref BBBB + srl a3, rINST, 8 # a3 <- AA + GET_VREG a1, a3 # a1 <- fp[AA] + ld a2, OFF_FP_METHOD(rFP) + move a3, rSELF + PREFETCH_INST 2 # Get next inst, but don't advance rPC + jal artSet16StaticFromCode + bnezc v0, MterpException # 0 on success + ADVANCE 2 # Past exception point - now advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: mips64/op_invoke_virtual.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtual + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtual + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super: /* 0x6f */ +/* File: mips64/op_invoke_super.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuper + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeSuper + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct: /* 0x70 */ +/* File: mips64/op_invoke_direct.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirect + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeDirect + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: mips64/op_invoke_static.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStatic + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeStatic + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: mips64/op_invoke_interface.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterface + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeInterface + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + /* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_return_void_no_barrier: /* 0x73 */ +/* File: mips64/op_return_void_no_barrier.S */ + .extern MterpSuspendCheck + lw ra, THREAD_FLAGS_OFFSET(rSELF) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, 1f + jal MterpSuspendCheck # (self) +1: + li a0, 0 + b MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: mips64/op_invoke_virtual_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualRange + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtualRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: mips64/op_invoke_super_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuperRange + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeSuperRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: mips64/op_invoke_direct_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirectRange + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeDirectRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: mips64/op_invoke_static_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStaticRange + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeStaticRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: mips64/op_invoke_interface_range.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterfaceRange + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeInterfaceRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: mips64/op_unused_79.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: mips64/op_unused_7a.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: mips64/op_neg_int.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + subu a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: mips64/op_not_int.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + nor a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: mips64/op_neg_long.S */ +/* File: mips64/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * For: not-long, neg-long + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + dsubu a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: mips64/op_not_long.S */ +/* File: mips64/unopWide.S */ + /* + * Generic 64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * For: not-long, neg-long + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + nor a0, zero, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: mips64/op_neg_float.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + neg.s f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: mips64/op_neg_double.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + neg.d f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: mips64/op_int_to_long.S */ + /* int-to-long vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB (sign-extended to 64 bits) + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- vB + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: mips64/op_int_to_float.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.s.w f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: mips64/op_int_to_double.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.d.w f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: mips64/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: mips64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_VREG a0, a3 # a0 <- vB + GET_INST_OPCODE v0 # extract opcode from rINST + .if 0 + SET_VREG_OBJECT a0, a2 # vA <- vB + .else + SET_VREG a0, a2 # vA <- vB + .endif + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: mips64/op_long_to_float.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.s.l f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: mips64/op_long_to_double.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.d.l f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: mips64/op_float_to_int.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, INT_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .Lop_float_to_int_trunc + cmp.eq.s f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_float_to_int_done + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: mips64/op_float_to_long.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + li t0, LONG_MIN_AS_FLOAT + mtc1 t0, f1 + cmp.le.s f1, f1, f0 + bc1nez f1, .Lop_float_to_long_trunc + cmp.eq.s f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_float_to_long_done + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: mips64/op_float_to_double.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_FLOAT f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.d.s f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: mips64/op_double_to_int.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, INT_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .Lop_double_to_int_trunc + cmp.eq.d f1, f0, f0 + li t0, INT_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_double_to_int_done + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: mips64/op_double_to_long.S */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + /* + * TODO: simplify this when the MIPS64R6 emulator + * supports NAN2008=1. + */ + dli t0, LONG_MIN_AS_DOUBLE + dmtc1 t0, f1 + cmp.le.d f1, f1, f0 + bc1nez f1, .Lop_double_to_long_trunc + cmp.eq.d f1, f0, f0 + dli t0, LONG_MIN + mfc1 t1, f1 + and t0, t0, t1 + b .Lop_double_to_long_done + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: mips64/op_double_to_float.S */ + /* + * Conversion from or to floating-point happens in a floating-point register. + * Therefore we load the input and store the output into or from a + * floating-point register irrespective of the type. + */ +/* File: mips64/fcvtHeader.S */ + /* + * Loads a specified register from vB. Used primarily for conversions + * from or to a floating-point type. + * + * Sets up a1 = A and a2 = B. a2 is later used by fcvtFooter.S to + * store the result in vA and jump to the next instruction. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + */ + ext a1, rINST, 8, 4 # a1 <- A + srl a2, rINST, 12 # a2 <- B + GET_VREG_DOUBLE f0, a2 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + + cvt.s.d f0, f0 +/* File: mips64/fcvtFooter.S */ + /* + * Stores a specified register containing the result of conversion + * from or to a floating-point type and jumps to the next instruction. + * + * Expects a1 to contain the destination Dalvik register number. + * a1 is set up by fcvtHeader.S. + * + * For: int-to-float, int-to-double, long-to-float, long-to-double, + * float-to-int, float-to-long, float-to-double, double-to-int, + * double-to-long, double-to-float, neg-float, neg-double. + * + * Note that this file can't be included after a break in other files + * and in those files its contents appear as a copy. + * See: float-to-int, float-to-long, double-to-int, double-to-long. + */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a1 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: mips64/op_int_to_byte.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + seb a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: mips64/op_int_to_char.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + and a0, a0, 0xffff # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: mips64/op_int_to_short.S */ +/* File: mips64/unop.S */ + /* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "a0 = op a0". + * + * for: int-to-byte, int-to-char, int-to-short, + * not-int, neg-int + */ + /* unop vA, vB */ + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + ext a2, rINST, 8, 4 # a2 <- A + # optional op + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + seh a0, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: mips64/op_add_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: mips64/op_sub_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: mips64/op_mul_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: mips64/op_div_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: mips64/op_rem_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: mips64/op_and_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: mips64/op_or_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: mips64/op_xor_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: mips64/op_shl_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: mips64/op_shr_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: mips64/op_ushr_int.S */ +/* File: mips64/binop.S */ + /* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int, sub-int, mul-int, div-int, rem-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG a0, a2 # a0 <- vBB + GET_VREG a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: mips64/op_add_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + daddu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: mips64/op_sub_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsubu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: mips64/op_mul_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dmul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: mips64/op_div_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + ddiv a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: mips64/op_rem_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dmod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: mips64/op_and_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: mips64/op_or_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: mips64/op_xor_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: mips64/op_shl_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: mips64/op_shr_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: mips64/op_ushr_long.S */ +/* File: mips64/binopWide.S */ + /* + * Generic 64-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vCC (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long, sub-long, mul-long, div-long, rem-long, and-long, or-long, + * xor-long, shl-long, shr-long, ushr-long + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_WIDE a0, a2 # a0 <- vBB + GET_VREG_WIDE a1, a3 # a1 <- vCC + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + dsrl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a4 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: mips64/op_add_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + add.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: mips64/op_sub_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + sub.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: mips64/op_mul_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + mul.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: mips64/op_div_float.S */ +/* File: mips64/fbinop.S */ + /*: + * Generic 32-bit floating-point operation. + * + * For: add-float, sub-float, mul-float, div-float. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f0, a2 # f0 <- vBB + GET_VREG_FLOAT f1, a3 # f1 <- vCC + div.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: mips64/op_rem_float.S */ + /* rem-float vAA, vBB, vCC */ + .extern fmodf + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_FLOAT f12, a2 # f12 <- vBB + GET_VREG_FLOAT f13, a3 # f13 <- vCC + jal fmodf # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: mips64/op_add_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + add.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: mips64/op_sub_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + sub.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: mips64/op_mul_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + mul.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: mips64/op_div_double.S */ +/* File: mips64/fbinopWide.S */ + /*: + * Generic 64-bit floating-point operation. + * + * For: add-double, sub-double, mul-double, div-double. + * form: f0, f0, f1 + */ + /* binop vAA, vBB, vCC */ + srl a4, rINST, 8 # a4 <- AA + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f0, a2 # f0 <- vBB + GET_VREG_DOUBLE f1, a3 # f1 <- vCC + div.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: mips64/op_rem_double.S */ + /* rem-double vAA, vBB, vCC */ + .extern fmod + lbu a2, 2(rPC) # a2 <- BB + lbu a3, 3(rPC) # a3 <- CC + GET_VREG_DOUBLE f12, a2 # f12 <- vBB + GET_VREG_DOUBLE f13, a3 # f13 <- vCC + jal fmod # f0 <- f12 op f13 + srl a4, rINST, 8 # a4 <- AA + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a4 # vAA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: mips64/op_add_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: mips64/op_sub_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + subu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: mips64/op_mul_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: mips64/op_div_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: mips64/op_rem_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: mips64/op_and_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: mips64/op_or_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: mips64/op_xor_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: mips64/op_shl_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: mips64/op_shr_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: mips64/op_ushr_int_2addr.S */ +/* File: mips64/binop2addr.S */ + /* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (INT_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a2 # a0 <- vA + GET_VREG a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: mips64/op_add_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + daddu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: mips64/op_sub_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsubu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: mips64/op_mul_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dmul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: mips64/op_div_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + ddiv a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: mips64/op_rem_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dmod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: mips64/op_and_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: mips64/op_or_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: mips64/op_xor_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: mips64/op_shl_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: mips64/op_shr_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips64/op_ushr_long_2addr.S */ +/* File: mips64/binopWide2addr.S */ + /* + * Generic 64-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be a MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * vB (a1). Useful for integer division and modulus. Note that we + * *don't* check for (LONG_MIN / -1) here, because the CPU handles it + * correctly. + * + * For: add-long/2addr, sub-long/2addr, mul-long/2addr, div-long/2addr, + * rem-long/2addr, and-long/2addr, or-long/2addr, xor-long/2addr, + * shl-long/2addr, shr-long/2addr, ushr-long/2addr + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_WIDE a0, a2 # a0 <- vA + GET_VREG_WIDE a1, a3 # a1 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + # optional op + dsrl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: mips64/op_add_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + add.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: mips64/op_sub_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + sub.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: mips64/op_mul_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + mul.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: mips64/op_div_float_2addr.S */ +/* File: mips64/fbinop2addr.S */ + /*: + * Generic 32-bit "/2addr" floating-point operation. + * + * For: add-float/2addr, sub-float/2addr, mul-float/2addr, div-float/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f0, a2 # f0 <- vA + GET_VREG_FLOAT f1, a3 # f1 <- vB + div.s f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: mips64/op_rem_float_2addr.S */ + /* rem-float/2addr vA, vB */ + .extern fmodf + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_FLOAT f12, a2 # f12 <- vA + GET_VREG_FLOAT f13, a3 # f13 <- vB + jal fmodf # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_FLOAT f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: mips64/op_add_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + add.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: mips64/op_sub_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + sub.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: mips64/op_mul_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + mul.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: mips64/op_div_double_2addr.S */ +/* File: mips64/fbinopWide2addr.S */ + /*: + * Generic 64-bit "/2addr" floating-point operation. + * + * For: add-double/2addr, sub-double/2addr, mul-double/2addr, div-double/2addr. + * form: f0, f0, f1 + */ + /* binop/2addr vA, vB */ + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f0, a2 # f0 <- vA + GET_VREG_DOUBLE f1, a3 # f1 <- vB + div.d f0, f0, f1 # f0 <- f0 op f1 + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: mips64/op_rem_double_2addr.S */ + /* rem-double/2addr vA, vB */ + .extern fmod + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG_DOUBLE f12, a2 # f12 <- vA + GET_VREG_DOUBLE f13, a3 # f13 <- vB + jal fmod # f0 <- f12 op f13 + ext a2, rINST, 8, 4 # a2 <- A + FETCH_ADVANCE_INST 1 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_DOUBLE f0, a2 # vA <- f0 + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: mips64/op_add_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: mips64/op_rsub_int.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: mips64/op_mul_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: mips64/op_div_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: mips64/op_rem_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: mips64/op_and_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: mips64/op_or_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: mips64/op_xor_int_lit16.S */ +/* File: mips64/binopLit16.S */ + /* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CCCC (a1). Useful for integer division and modulus. + * + * For: add-int/lit16, rsub-int, mul-int/lit16, div-int/lit16, + * rem-int/lit16, and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + lh a1, 2(rPC) # a1 <- sign-extended CCCC + ext a2, rINST, 8, 4 # a2 <- A + ext a3, rINST, 12, 4 # a3 <- B + GET_VREG a0, a3 # a0 <- vB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: mips64/op_add_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + addu a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips64/op_rsub_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + subu a0, a1, a0 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: mips64/op_mul_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mul a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: mips64/op_div_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + div a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: mips64/op_rem_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 1 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + mod a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: mips64/op_and_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + and a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: mips64/op_or_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + or a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: mips64/op_xor_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + xor a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: mips64/op_shl_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sll a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: mips64/op_shr_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + sra a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips64/op_ushr_int_lit8.S */ +/* File: mips64/binopLit8.S */ + /* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = a0 op a1". + * This could be an MIPS instruction or a function call. (If the result + * comes back in a register other than a0, you can override "result".) + * + * If "chkzero" is set to 1, we perform a divide-by-zero check on + * CC (a1). Useful for integer division and modulus. + * + * For: add-int/lit8, rsub-int/lit8, mul-int/lit8, div-int/lit8, + * rem-int/lit8, and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + lbu a3, 2(rPC) # a3 <- BB + lb a1, 3(rPC) # a1 <- sign-extended CC + srl a2, rINST, 8 # a2 <- AA + GET_VREG a0, a3 # a0 <- vBB + .if 0 + beqz a1, common_errDivideByZero # is second operand zero? + .endif + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + # optional op + srl a0, a0, a1 # a0 <- op, a0-a3 changed + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG a0, a2 # vAA <- a0 + GOTO_OPCODE v0 # jump to next instruction + + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lw a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: mips64/op_iget_wide_quick.S */ + /* iget-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a4, 2(rPC) # a4 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + daddu a4, a3, a4 # create direct pointer + lw a0, 0(a4) + lw a1, 4(a4) + dinsu a0, a1, 32, 32 + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG_WIDE a0, a2 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: mips64/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset//CCCC */ + .extern artIGetObjectFromMterp + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + EXPORT_PC + GET_VREG_U a0, a2 # a0 <- object we're operating on + jal artIGetObjectFromMterp # (obj, offset) + ld a3, THREAD_EXCEPTION_OFFSET(rSELF) + ext a2, rINST, 8, 4 # a2 <- A + PREFETCH_INST 2 + bnez a3, MterpPossibleException # bail out + SET_VREG_OBJECT v0, a2 # fp[A] <- v0 + ADVANCE 2 # advance rPC + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sw a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: mips64/op_iput_wide_quick.S */ + /* iput-wide-quick vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a3, 2(rPC) # a3 <- field byte offset + GET_VREG_U a2, a2 # a2 <- fp[B], the object pointer + ext a0, rINST, 8, 4 # a0 <- A + beqz a2, common_errNullObject # object was null + GET_VREG_WIDE a0, a0 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a2, a3 # create a direct pointer + sw a0, 0(a1) + dsrl32 a0, a0, 0 + sw a0, 4(a1) + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: mips64/op_iput_object_quick.S */ + .extern MterpIputObjectQuick + EXPORT_PC + daddu a0, rFP, OFF_FP_SHADOWFRAME + move a1, rPC + move a2, rINST + jal MterpIputObjectQuick + beqzc v0, MterpException + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips64/op_invoke_virtual_quick.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuick + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtualQuick + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips64/op_invoke_virtual_range_quick.S */ +/* File: mips64/invoke.S */ + /* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuickRange + .extern MterpShouldSwitchInterpreters + EXPORT_PC + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rPC + move a3, rINST + jal MterpInvokeVirtualQuickRange + beqzc v0, MterpException + FETCH_ADVANCE_INST 3 + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: mips64/op_iput_boolean_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sb a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: mips64/op_iput_byte_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sb a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: mips64/op_iput_char_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sh a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: mips64/op_iput_short_quick.S */ +/* File: mips64/op_iput_quick.S */ + /* For: iput-quick, iput-boolean-quick, iput-byte-quick, iput-char-quick, iput-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- fp[B], the object pointer + ext a2, rINST, 8, 4 # a2 <- A + beqz a3, common_errNullObject # object was null + GET_VREG a0, a2 # a0 <- fp[A] + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + daddu a1, a1, a3 + sh a0, 0(a1) # obj.field <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: mips64/op_iget_boolean_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lbu a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: mips64/op_iget_byte_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lb a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: mips64/op_iget_char_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lhu a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: mips64/op_iget_short_quick.S */ +/* File: mips64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset//CCCC */ + srl a2, rINST, 12 # a2 <- B + lhu a1, 2(rPC) # a1 <- field byte offset + GET_VREG_U a3, a2 # a3 <- object we're operating on + ext a4, rINST, 8, 4 # a4 <- A + daddu a1, a1, a3 + beqz a3, common_errNullObject # object was null + lh a0, 0(a1) # a0 <- obj.field + FETCH_ADVANCE_INST 2 # advance rPC, load rINST + SET_VREG a0, a4 # fp[A] <- a0 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_lambda: /* 0xf3 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_f4: /* 0xf4 */ +/* File: mips64/op_unused_f4.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_capture_variable: /* 0xf5 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_create_lambda: /* 0xf6 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_liberate_variable: /* 0xf7 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_box_lambda: /* 0xf8 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unbox_lambda: /* 0xf9 */ +/* Transfer stub to alternate interpreter */ + b MterpFallback + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fa: /* 0xfa */ +/* File: mips64/op_unused_fa.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: mips64/op_unused_fb.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: mips64/op_unused_fc.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: mips64/op_unused_fd.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: mips64/op_unused_fe.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: mips64/op_unused_ff.S */ +/* File: mips64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + b MterpFallback + + + .balign 128 + .size artMterpAsmInstructionStart, .-artMterpAsmInstructionStart + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global artMterpAsmSisterStart + .type artMterpAsmSisterStart, %function + .text + .balign 4 +artMterpAsmSisterStart: + +/* continuation for op_float_to_int */ +.Lop_float_to_int_trunc: + trunc.w.s f0, f0 + mfc1 t0, f0 +.Lop_float_to_int_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction + +/* continuation for op_float_to_long */ +.Lop_float_to_long_trunc: + trunc.l.s f0, f0 + dmfc1 t0, f0 +.Lop_float_to_long_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction + +/* continuation for op_double_to_int */ +.Lop_double_to_int_trunc: + trunc.w.d f0, f0 + mfc1 t0, f0 +.Lop_double_to_int_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG t0, a1 + GOTO_OPCODE v0 # jump to next instruction + +/* continuation for op_double_to_long */ +.Lop_double_to_long_trunc: + trunc.l.d f0, f0 + dmfc1 t0, f0 +.Lop_double_to_long_done: + /* Can't include fcvtFooter.S after break */ + GET_INST_OPCODE v0 # extract opcode from rINST + SET_VREG_WIDE t0, a1 + GOTO_OPCODE v0 # jump to next instruction + + .size artMterpAsmSisterStart, .-artMterpAsmSisterStart + .global artMterpAsmSisterEnd +artMterpAsmSisterEnd: + + + .global artMterpAsmAltInstructionStart + .type artMterpAsmAltInstructionStart, %function + .text + +artMterpAsmAltInstructionStart = .L_ALT_op_nop +/* ------------------------------ */ + .balign 128 +.L_ALT_op_nop: /* 0x00 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (0 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (1 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (2 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (3 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (4 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (5 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (6 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (7 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (8 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (9 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (10 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (11 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (12 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (13 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (14 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (15 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (16 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (17 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (18 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (19 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (20 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (21 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (22 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (23 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (24 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (25 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (26 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (27 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (28 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (29 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (30 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (31 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (32 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (33 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (34 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (35 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (36 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (37 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (38 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (39 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (40 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (41 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (42 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (43 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (44 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (45 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (46 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (47 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (48 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (49 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (50 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (51 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (52 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (53 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (54 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (55 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (56 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (57 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (58 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (59 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (60 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (61 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (62 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (63 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (64 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (65 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (66 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (67 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (68 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (69 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (70 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (71 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (72 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (73 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (74 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (75 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (76 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (77 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (78 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (79 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (80 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (81 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (82 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (83 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (84 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (85 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (86 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (87 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (88 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (89 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (90 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (91 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (92 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (93 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (94 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (95 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (96 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (97 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (98 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (99 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (100 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (101 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (102 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (103 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (104 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (105 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (106 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (107 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (108 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (109 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (110 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (111 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (112 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (113 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (114 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (115 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (116 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (117 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (118 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (119 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (120 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (121 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (122 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (123 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (124 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (125 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (126 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (127 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (128 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (129 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (130 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (131 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (132 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (133 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (134 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (135 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (136 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (137 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (138 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (139 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (140 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (141 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (142 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (143 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (144 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (145 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (146 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (147 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (148 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (149 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (150 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (151 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (152 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (153 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (154 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (155 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (156 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (157 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (158 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (159 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (160 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (161 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (162 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (163 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (164 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (165 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (166 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (167 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (168 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (169 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (170 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (171 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (172 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (173 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (174 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (175 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (176 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (177 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (178 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (179 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (180 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (181 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (182 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (183 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (184 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (185 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (186 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (187 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (188 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (189 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (190 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (191 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (192 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (193 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (194 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (195 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (196 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (197 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (198 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (199 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (200 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (201 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (202 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (203 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (204 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (205 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (206 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (207 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (208 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (209 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (210 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (211 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (212 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (213 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (214 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (215 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (216 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (217 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (218 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (219 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (220 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (221 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (222 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (223 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (224 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (225 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (226 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (227 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (228 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (229 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (230 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (231 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (232 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (233 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (234 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (235 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (236 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (237 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (238 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (239 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (240 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (241 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (242 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (243 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (244 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (245 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (246 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (247 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (248 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (249 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (250 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (251 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (252 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (253 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (254 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: mips64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Note that the call to MterpCheckBefore is done as a tail call. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + dla ra, artMterpAsmInstructionStart + dla t9, MterpCheckBefore + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + daddu ra, ra, (255 * 128) # Addr of primary handler. + jalr zero, t9 # (self, shadow_frame) Note: tail call. + + .balign 128 + .size artMterpAsmAltInstructionStart, .-artMterpAsmAltInstructionStart + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: +/* File: mips64/footer.S */ +/* + * We've detected a condition that will result in an exception, but the exception + * 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. + */ + + .extern MterpLogDivideByZeroException +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogDivideByZeroException +#endif + b MterpCommonFallback + + .extern MterpLogArrayIndexException +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogArrayIndexException +#endif + b MterpCommonFallback + + .extern MterpLogNullObjectException +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogNullObjectException +#endif + b MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + ld a0, THREAD_EXCEPTION_OFFSET(rSELF) + beqzc a0, MterpFallback # If not, fall back to reference interpreter. + /* intentional fallthrough - handle pending exception. */ +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ + .extern MterpHandleException + .extern MterpShouldSwitchInterpreters +MterpException: + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpHandleException # (self, shadow_frame) + beqzc v0, MterpExceptionReturn # no local catch, back to caller. + ld a0, OFF_FP_CODE_ITEM(rFP) + lwu a1, OFF_FP_DEX_PC(rFP) + REFRESH_IBASE + daddu rPC, a0, CODEITEM_INSNS_OFFSET + dlsa rPC, a1, rPC, 1 # generate new dex_pc_ptr + /* Do we need to switch interpreters? */ + jal MterpShouldSwitchInterpreters + bnezc v0, MterpFallback + /* resume execution at catch block */ + EXPORT_PC + FETCH_INST + GET_INST_OPCODE v0 + GOTO_OPCODE v0 + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in ra. + */ + .extern MterpSuspendCheck +MterpCheckSuspendAndContinue: + REFRESH_IBASE + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + bnez ra, check1 + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction +check1: + EXPORT_PC + move a0, rSELF + jal MterpSuspendCheck # (self) + bnezc v0, MterpFallback # Something in the environment changed, switch interpreters + GET_INST_OPCODE v0 # extract opcode from rINST + GOTO_OPCODE v0 # jump to next instruction + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + move a2, rINST # rINST contains offset + jal MterpLogOSR +#endif + li v0, 1 # Signal normal return + b MterpDone + +/* + * Bail out to reference interpreter. + */ + .extern MterpLogFallback +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + move a0, rSELF + daddu a1, rFP, OFF_FP_SHADOWFRAME + jal MterpLogFallback +#endif +MterpCommonFallback: + li v0, 0 # signal retry with reference interpreter. + b MterpDone + +/* + * We pushed some registers on the stack in ExecuteMterpImpl, then saved + * SP and RA. Here we restore SP, restore the registers, and then restore + * RA to PC. + * + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + li v0, 1 # signal return to caller. + b MterpDone +/* + * Returned value is expected in a0 and if it's not 64-bit, the 32 most + * significant bits of a0 must be 0. + */ +MterpReturn: + ld a2, OFF_FP_RESULT_REGISTER(rFP) + lw ra, THREAD_FLAGS_OFFSET(rSELF) + sd a0, 0(a2) + move a0, rSELF + and ra, ra, (THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + beqzc ra, check2 + jal MterpSuspendCheck # (self) +check2: + li v0, 1 # signal return to caller. +MterpDone: + ld s5, STACK_OFFSET_S5(sp) + .cfi_restore 21 + ld s4, STACK_OFFSET_S4(sp) + .cfi_restore 20 + ld s3, STACK_OFFSET_S3(sp) + .cfi_restore 19 + ld s2, STACK_OFFSET_S2(sp) + .cfi_restore 18 + ld s1, STACK_OFFSET_S1(sp) + .cfi_restore 17 + ld s0, STACK_OFFSET_S0(sp) + .cfi_restore 16 + + ld ra, STACK_OFFSET_RA(sp) + .cfi_restore 31 + + ld t8, STACK_OFFSET_GP(sp) + .cpreturn + .cfi_restore 28 + + .set noreorder + jr ra + daddu sp, sp, STACK_SIZE + .cfi_adjust_cfa_offset -STACK_SIZE + + .cfi_endproc + .size ExecuteMterpImpl, .-ExecuteMterpImpl + diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index d365a4f986ad4a75b2bbd9bb116fecf2a5f96983..ebac5fca48cb03cd9bac86d8418e13893ae66262 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -112,25 +112,32 @@ unspecified registers or condition codes. #define SYMBOL(name) name #endif +.macro PUSH _reg + pushl \_reg + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popl \_reg + .cfi_adjust_cfa_offset -4 + .cfi_restore \_reg +.endm + /* Frame size must be 16-byte aligned. - * Remember about 4 bytes for return address + * Remember about 4 bytes for return address + 4 * 4 for spills */ -#define FRAME_SIZE 44 +#define FRAME_SIZE 28 /* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ -#define IN_ARG3 (FRAME_SIZE + 16) -#define IN_ARG2 (FRAME_SIZE + 12) -#define IN_ARG1 (FRAME_SIZE + 8) -#define IN_ARG0 (FRAME_SIZE + 4) -#define CALLER_RP (FRAME_SIZE + 0) +#define IN_ARG3 (FRAME_SIZE + 16 + 16) +#define IN_ARG2 (FRAME_SIZE + 16 + 12) +#define IN_ARG1 (FRAME_SIZE + 16 + 8) +#define IN_ARG0 (FRAME_SIZE + 16 + 4) /* Spill offsets relative to %esp */ -#define EBP_SPILL (FRAME_SIZE - 4) -#define EDI_SPILL (FRAME_SIZE - 8) -#define ESI_SPILL (FRAME_SIZE - 12) -#define EBX_SPILL (FRAME_SIZE - 16) -#define LOCAL0 (FRAME_SIZE - 20) -#define LOCAL1 (FRAME_SIZE - 24) -#define LOCAL2 (FRAME_SIZE - 28) +#define LOCAL0 (FRAME_SIZE - 4) +#define LOCAL1 (FRAME_SIZE - 8) +#define LOCAL2 (FRAME_SIZE - 12) /* Out Arg offsets, relative to %esp */ #define OUT_ARG3 ( 12) #define OUT_ARG2 ( 8) @@ -163,13 +170,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 @@ -347,16 +367,18 @@ unspecified registers or condition codes. SYMBOL(ExecuteMterpImpl): .cfi_startproc + .cfi_def_cfa esp, 4 + + /* Spill callee save regs */ + PUSH %ebp + PUSH %edi + PUSH %esi + PUSH %ebx + /* Allocate frame */ subl $FRAME_SIZE, %esp .cfi_adjust_cfa_offset FRAME_SIZE - /* Spill callee save regs */ - movl %ebp, EBP_SPILL(%esp) - movl %edi, EDI_SPILL(%esp) - movl %esi, ESI_SPILL(%esp) - movl %ebx, EBX_SPILL(%esp) - /* Load ShadowFrame pointer */ movl IN_ARG2(%esp), %edx @@ -1076,17 +1098,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 /* ------------------------------ */ @@ -1100,17 +1117,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 /* ------------------------------ */ @@ -1129,17 +1141,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 /* ------------------------------ */ @@ -1162,17 +1169,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 /* ------------------------------ */ @@ -1196,17 +1199,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 @@ -1424,20 +1423,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 @@ -1459,20 +1453,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 @@ -1494,20 +1483,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 @@ -1529,20 +1513,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 @@ -1564,20 +1543,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 @@ -1599,20 +1573,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 @@ -1630,20 +1599,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 @@ -1661,20 +1625,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 @@ -1692,20 +1651,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 @@ -1723,20 +1677,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 @@ -1754,20 +1703,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 @@ -1785,20 +1729,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 @@ -3059,8 +2998,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtual) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle a virtual method call. @@ -3092,8 +3036,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuper) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle a "super" method call. @@ -3125,8 +3074,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirect) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3151,8 +3105,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStatic) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT @@ -3178,8 +3137,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterface) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* * Handle an interface method call. @@ -3225,8 +3189,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3251,8 +3220,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeSuperRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3277,8 +3251,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeDirectRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3303,8 +3282,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeStaticRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -3329,8 +3313,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeInterfaceRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -6072,8 +6061,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuick) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -6098,8 +6092,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop call SYMBOL(MterpInvokeVirtualQuickRange) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT /* ------------------------------ */ @@ -12818,7 +12817,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 @@ -12922,13 +12920,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ @@ -12948,6 +12950,21 @@ MterpCheckSuspendAndContinue: 1: 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. */ @@ -12977,17 +12994,16 @@ MterpReturn: movl %ecx, 4(%edx) mov $1, %eax MterpDone: - /* Restore callee save register */ - movl EBP_SPILL(%esp), %ebp - movl EDI_SPILL(%esp), %edi - movl ESI_SPILL(%esp), %esi - movl EBX_SPILL(%esp), %ebx - /* pop up frame */ addl $FRAME_SIZE, %esp .cfi_adjust_cfa_offset -FRAME_SIZE - ret + /* Restore callee save register */ + POP %ebx + POP %esi + POP %edi + POP %ebp + ret .cfi_endproc SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S new file mode 100644 index 0000000000000000000000000000000000000000..a1360e0934d6c1dc658772a52ef56b1c1d89f1e0 --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -0,0 +1,11960 @@ +/* + * This file was generated automatically by gen-mterp.py for 'x86_64'. + * + * --> DO NOT EDIT <-- + */ + +/* File: x86_64/header.S */ +/* + * 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. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +/* +x86_64 ABI general notes: + +Caller save set: + rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) +Callee save set: + rbx, rbp, r12-r15 +Return regs: + 32-bit in eax + 64-bit in rax + fp on xmm0 + +First 8 fp parameters came in xmm0-xmm7. +First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. +Other parameters passed on stack, pushed right-to-left. On entry to target, first +param is at 8(%esp). Traditional entry code is: + +Stack must be 16-byte aligned to support SSE in native code. + +If we're not doing variable stack allocation (alloca), the frame pointer can be +eliminated and all arg references adjusted to be esp relative. +*/ + +/* +Mterp and x86_64 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rSELF rbp pointer to ThreadSelf. + rPC r12 interpreted program counter, used for fetching instructions + rFP r13 interpreted frame pointer, used for accessing locals and args + rINSTw bx first 16-bit code of current instruction + rINSTbl bl opcode portion of instruction word + rINSTbh bh high byte of inst word, usually contains src/tgt reg names + rIBASE r14 base of instruction handler table + rREFS r15 base of object references in shadow frame. + +Notes: + o High order 16 bits of ebx must be zero on entry to handler + o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit + o eax and ecx are scratch, rINSTw/ebx sometimes scratch + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Handle mac compiler specific + */ +#if defined(__APPLE__) + #define MACRO_LITERAL(value) $(value) + #define FUNCTION_TYPE(name) + #define SIZE(start,end) + // Mac OS' symbols have an _ prefix. + #define SYMBOL(name) _ ## name +#else + #define MACRO_LITERAL(value) $value + #define FUNCTION_TYPE(name) .type name, @function + #define SIZE(start,end) .size start, .-end + #define SYMBOL(name) name +#endif + +.macro PUSH _reg + pushq \_reg + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popq \_reg + .cfi_adjust_cfa_offset -8 + .cfi_restore \_reg +.endm + +/* Frame size must be 16-byte aligned. + * Remember about 8 bytes for return address + 6 * 8 for spills. + */ +#define FRAME_SIZE 8 + +/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ +#define IN_ARG3 %rcx +#define IN_ARG2 %rdx +#define IN_ARG1 %rsi +#define IN_ARG0 %rdi +/* Out Args */ +#define OUT_ARG3 %rcx +#define OUT_ARG2 %rdx +#define OUT_ARG1 %rsi +#define OUT_ARG0 %rdi +#define OUT_32_ARG3 %ecx +#define OUT_32_ARG2 %edx +#define OUT_32_ARG1 %esi +#define OUT_32_ARG0 %edi +#define OUT_FP_ARG1 %xmm1 +#define OUT_FP_ARG0 %xmm0 + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF %rbp +#define rPC %r12 +#define rFP %r13 +#define rINST %ebx +#define rINSTq %rbx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %r14 +#define rREFS %r15 + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#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 + +/* + * Profile branch. rINST should contain the offset. %eax is scratch. + */ +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement +#endif +.endm + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) +.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. + * + */ +.macro REFRESH_IBASE + movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE +.endm + +/* + * Refresh rINST. + * At enter to handler rINST does not contain the opcode number. + * However some utilities require the full value, so this macro + * restores the opcode number. + */ +.macro REFRESH_INST _opnum + movb rINSTbl, rINSTbh + movb $\_opnum, rINSTbl +.endm + +/* + * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. + */ +.macro FETCH_INST + movzwq (rPC), rINSTq +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll MACRO_LITERAL(7), %eax + addq rIBASE, %rax + jmp *%rax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leaq 2*\_count(rPC), rPC +.endm + +/* + * Advance rPC by instruction count, fetch instruction and jump to handler. + */ +.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count + ADVANCE_PC \_count + FETCH_INST + GOTO_NEXT +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value. */ +.macro GET_WIDE_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +/* Write wide value. reg is clobbered. */ +.macro SET_WIDE_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + xorq \_reg, \_reg + movq \_reg, (rREFS,\_vreg,4) +.endm + +.macro SET_VREG_OBJECT _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl \_reg, (rREFS,\_vreg,4) +.endm + +.macro GET_VREG_HIGH _reg _vreg + movl 4(rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG_HIGH _reg _vreg + movl \_reg, 4(rFP,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +/* File: x86_64/entry.S */ +/* + * 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. + */ +/* + * Interpreter entry point. + */ + + .text + .global SYMBOL(ExecuteMterpImpl) + FUNCTION_TYPE(ExecuteMterpImpl) + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +SYMBOL(ExecuteMterpImpl): + .cfi_startproc + .cfi_def_cfa rsp, 8 + + /* Spill callee save regs */ + PUSH %rbx + PUSH %rbp + PUSH %r12 + PUSH %r13 + PUSH %r14 + PUSH %r15 + + /* Allocate frame */ + subq $FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset FRAME_SIZE + + /* Remember the return register */ + movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) + + /* Remember the code_item */ + movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax + leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP + leaq (rFP, %rax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax + leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC + leaq (rPC, %rax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + movq IN_ARG0, rSELF + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + + + .global SYMBOL(artMterpAsmInstructionStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmInstructionStart)) +SYMBOL(artMterpAsmInstructionStart) = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: x86_64/op_nop.S */ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: x86_64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if 0 + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: x86_64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: x86_64/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movl rINST, %ecx # ecx <- BA + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] + SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: x86_64/op_move_wide_from16.S */ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 2(rPC), %ecx # ecx <- BBBB + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: x86_64/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwq 4(rPC), %rcx # ecx<- BBBB + movzwq 2(rPC), %rax # eax<- AAAA + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, %rax # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: x86_64/op_move_object.S */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 1 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: x86_64/op_move_object_from16.S */ +/* File: x86_64/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if 1 + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: x86_64/op_move_object_16.S */ +/* File: x86_64/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if 1 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: x86_64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: x86_64/op_move_result_wide.S */ + /* move-result-wide vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movq (%rax), %rdx # Get wide + SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: x86_64/op_move_result_object.S */ +/* File: x86_64/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: x86_64/op_move_exception.S */ + /* move-exception vAA */ + movl THREAD_EXCEPTION_OFFSET(rSELF), %eax + SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object + movl $0, THREAD_EXCEPTION_OFFSET(rSELF) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: x86_64/op_return_void.S */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: x86_64/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: x86_64/op_return_wide.S */ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: x86_64/op_return_object.S */ +/* File: x86_64/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: x86_64/op_const_4.S */ + /* const/4 vA, #+B */ + movsbl rINSTbl, %eax # eax <-ssssssBx + movl $0xf, rINST + andl %eax, rINST # rINST <- A + sarl $4, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: x86_64/op_const_16.S */ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx, rINSTq # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: x86_64/op_const.S */ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax, rINSTq # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: x86_64/op_const_high16.S */ + /* const/high16 vAA, #+BBBB0000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $16, %eax # eax <- BBBB0000 + SET_VREG %eax, rINSTq # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: x86_64/op_const_wide_16.S */ + /* const-wide/16 vAA, #+BBBB */ + movswq 2(rPC), %rax # rax <- ssssBBBB + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: x86_64/op_const_wide_32.S */ + /* const-wide/32 vAA, #+BBBBbbbb */ + movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: x86_64/op_const_wide.S */ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb + SET_WIDE_VREG %rax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: x86_64/op_const_wide_high16.S */ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwq 2(rPC), %rax # eax <- 0000BBBB + salq $48, %rax # eax <- BBBB0000 + SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: x86_64/op_const_string.S */ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: x86_64/op_const_string_jumbo.S */ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: x86_64/op_const_class.S */ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: x86_64/op_monitor_enter.S */ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artLockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: x86_64/op_monitor_exit.S */ +/* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artUnlockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: x86_64/op_check_cast.S */ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + leaq VREG_ADDRESS(rINSTq), OUT_ARG1 + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpCheckCast) # (index, &obj, method, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: x86_64/op_instance_of.S */ +/* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class@CCCC */ + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC + movl rINST, %eax # eax <- BA + sarl $4, %eax # eax <- B + leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) + movsbl %al, %eax + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + andb $0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: x86_64/op_array_length.S */ +/* + * Return the length of an array. + */ + movl rINST, %eax # eax <- BA + sarl $4, rINST # rINST <- B + GET_VREG %ecx, rINSTq # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST + SET_VREG rINST, %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: x86_64/op_new_instance.S */ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rSELF, OUT_ARG1 + REFRESH_INST 34 + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpNewInstance) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: x86_64/op_new_array.S */ +/* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 35 + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array: /* 0x24 */ +/* File: x86_64/op_filled_new_array.S */ +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern MterpFilledNewArray + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL(MterpFilledNewArray) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_filled_new_array_range: /* 0x25 */ +/* File: x86_64/op_filled_new_array_range.S */ +/* File: x86_64/op_filled_new_array.S */ +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern MterpFilledNewArrayRange + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL(MterpFilledNewArrayRange) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_fill_array_data: /* 0x26 */ +/* File: x86_64/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) + call SYMBOL(MterpFillArrayData) # (obj, payload) + testb %al, %al # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: x86_64/op_throw.S */ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax, rINSTq # eax<- vAA (exception object) + testb %al, %al + jz common_errNullObject + movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) + jmp MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: x86_64/op_goto.S */ +/* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: x86_64/op_goto_16.S */ +/* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: x86_64/op_goto_32.S */ +/* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Because we need the SF bit set, we'll use an adds + * to convert from Dalvik offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: x86_64/op_packed_switch.S */ +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL(MterpDoPackedSwitch) + movslq %eax, rINSTq + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: x86_64/op_sparse_switch.S */ +/* File: x86_64/op_packed_switch.S */ +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL(MterpDoSparseSwitch) + movslq %eax, rINSTq + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: x86_64/op_cmpl_float.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movss VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpl_float_nan_is_neg + je .Lop_cmpl_float_finish + jb .Lop_cmpl_float_less +.Lop_cmpl_float_nan_is_pos: + addb $1, %al + jmp .Lop_cmpl_float_finish +.Lop_cmpl_float_nan_is_neg: +.Lop_cmpl_float_less: + movl $-1, %eax +.Lop_cmpl_float_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: x86_64/op_cmpg_float.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movss VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpg_float_nan_is_pos + je .Lop_cmpg_float_finish + jb .Lop_cmpg_float_less +.Lop_cmpg_float_nan_is_pos: + addb $1, %al + jmp .Lop_cmpg_float_finish +.Lop_cmpg_float_nan_is_neg: +.Lop_cmpg_float_less: + movl $-1, %eax +.Lop_cmpg_float_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: x86_64/op_cmpl_double.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movsd VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpl_double_nan_is_neg + je .Lop_cmpl_double_finish + jb .Lop_cmpl_double_less +.Lop_cmpl_double_nan_is_pos: + addb $1, %al + jmp .Lop_cmpl_double_finish +.Lop_cmpl_double_nan_is_neg: +.Lop_cmpl_double_less: + movl $-1, %eax +.Lop_cmpl_double_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: x86_64/op_cmpg_double.S */ +/* File: x86_64/fpcmp.S */ +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movsd VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%rcx), %xmm0 + jp .Lop_cmpg_double_nan_is_pos + je .Lop_cmpg_double_finish + jb .Lop_cmpg_double_less +.Lop_cmpg_double_nan_is_pos: + addb $1, %al + jmp .Lop_cmpg_double_finish +.Lop_cmpg_double_nan_is_neg: +.Lop_cmpg_double_less: + movl $-1, %eax +.Lop_cmpg_double_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: x86_64/op_cmp_long.S */ +/* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + */ + /* cmp-long vAA, vBB, vCC */ + movzbq 2(rPC), %rdx # edx <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] + xorl %eax, %eax + xorl %edi, %edi + addb $1, %al + movl $-1, %esi + cmpq VREG_ADDRESS(%rcx), %rdx + cmovl %esi, %edi + cmovg %eax, %edi + SET_VREG %edi, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: x86_64/op_if_eq.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, rINST # assume not taken + jne 1f + movswq 2(rPC), rINSTq # Get signed branch offset +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: x86_64/op_if_ne.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, rINST # assume not taken + je 1f + movswq 2(rPC), rINSTq # Get signed branch offset +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: x86_64/op_if_lt.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, rINST # assume not taken + jge 1f + movswq 2(rPC), rINSTq # Get signed branch offset +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: x86_64/op_if_ge.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, rINST # assume not taken + jl 1f + movswq 2(rPC), rINSTq # Get signed branch offset +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: x86_64/op_if_gt.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, rINST # assume not taken + jle 1f + movswq 2(rPC), rINSTq # Get signed branch offset +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: x86_64/op_if_le.S */ +/* File: x86_64/bincmp.S */ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $2, rINST # assume not taken + jg 1f + movswq 2(rPC), rINSTq # Get signed branch offset +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: x86_64/op_if_eqz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, rINST # assume branch not taken + jne 1f + movswq 2(rPC), rINSTq # fetch signed displacement +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: x86_64/op_if_nez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, rINST # assume branch not taken + je 1f + movswq 2(rPC), rINSTq # fetch signed displacement +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: x86_64/op_if_ltz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, rINST # assume branch not taken + jge 1f + movswq 2(rPC), rINSTq # fetch signed displacement +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: x86_64/op_if_gez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, rINST # assume branch not taken + jl 1f + movswq 2(rPC), rINSTq # fetch signed displacement +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: x86_64/op_if_gtz.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, rINST # assume branch not taken + jle 1f + movswq 2(rPC), rINSTq # fetch signed displacement +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: x86_64/op_if_lez.S */ +/* File: x86_64/zcmp.S */ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $2, rINST # assume branch not taken + jg 1f + movswq 2(rPC), rINSTq # fetch signed displacement +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: x86_64/op_unused_3e.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: x86_64/op_unused_3f.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: x86_64/op_unused_40.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: x86_64/op_unused_41.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: x86_64/op_unused_42.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: x86_64/op_unused_43.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movl MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: x86_64/op_aget_wide.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 1 + movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movq MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: x86_64/op_aget_object.S */ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) + GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) + EXPORT_PC + call SYMBOL(artAGetObjectFromMterp) # (array, index) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + SET_VREG_OBJECT %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: x86_64/op_aget_boolean.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movzbl MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: x86_64/op_aget_byte.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movsbl MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: x86_64/op_aget_char.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movzwl MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: x86_64/op_aget_short.S */ +/* File: x86_64/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + movq MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + movswl MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movl rINST, MIRROR_INT_ARRAY_DATA_OFFSET(%rax,%rcx,4) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: x86_64/op_aput_wide.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 1 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movq rINSTq, MIRROR_WIDE_ARRAY_DATA_OFFSET(%rax,%rcx,8) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: x86_64/op_aput_object.S */ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 77 + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpAputObject) # (array, index) + testb %al, %al + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: x86_64/op_aput_boolean.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movb rINSTbl, MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%rax,%rcx,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: x86_64/op_aput_byte.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movb rINSTbl, MIRROR_BYTE_ARRAY_DATA_OFFSET(%rax,%rcx,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: x86_64/op_aput_char.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movw rINSTw, MIRROR_CHAR_ARRAY_DATA_OFFSET(%rax,%rcx,2) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: x86_64/op_aput_short.S */ +/* File: x86_64/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if 0 + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + movw rINSTw, MIRROR_SHORT_ARRAY_DATA_OFFSET(%rax,%rcx,2) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGet32InstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: x86_64/op_iget_wide.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGet64InstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 1 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: x86_64/op_iget_object.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetObjInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: x86_64/op_iget_boolean.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetBooleanInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: x86_64/op_iget_byte.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetByteInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: x86_64/op_iget_char.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetCharInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: x86_64/op_iget_short.S */ +/* File: x86_64/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL(artGetShortInstanceFromCode) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet32InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet32InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: x86_64/op_iput_wide.S */ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST <- A + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet64InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: x86_64/op_iput_object.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 91 + movl rINST, OUT_32_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpIputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: x86_64/op_iput_boolean.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet8InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: x86_64/op_iput_byte.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet8InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet8InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: x86_64/op_iput_char.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet16InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: x86_64/op_iput_short.S */ +/* File: x86_64/op_iput.S */ +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern artSet16InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet16InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGet32StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGet32StaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: x86_64/op_sget_wide.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGet64StaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 1 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: x86_64/op_sget_object.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetObjStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetObjStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 1 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: x86_64/op_sget_boolean.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetBooleanStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetBooleanStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: x86_64/op_sget_byte.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetByteStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetByteStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: x86_64/op_sget_char.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetCharStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetCharStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: x86_64/op_sget_short.S */ +/* File: x86_64/op_sget.S */ +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern artGetShortStaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL(artGetShortStaticFromCode) + cmpl $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if 0 + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet32StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet32StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: x86_64/op_sput_wide.S */ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet64IndirectStaticFromMterp) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: x86_64/op_sput_object.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 105 + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpSputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: x86_64/op_sput_boolean.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet8StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: x86_64/op_sput_byte.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet8StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet8StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: x86_64/op_sput_char.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet16StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: x86_64/op_sput_short.S */ +/* File: x86_64/op_sput.S */ +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern artSet16StaticFromCode + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet16StaticFromCode) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: x86_64/op_invoke_virtual.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtual + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 110 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtual) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + +/* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super: /* 0x6f */ +/* File: x86_64/op_invoke_super.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuper + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 111 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeSuper) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + +/* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct: /* 0x70 */ +/* File: x86_64/op_invoke_direct.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirect + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 112 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeDirect) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: x86_64/op_invoke_static.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStatic + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 113 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeStatic) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: x86_64/op_invoke_interface.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterface + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 114 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeInterface) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + +/* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + +/* ------------------------------ */ + .balign 128 +.L_op_return_void_no_barrier: /* 0x73 */ +/* File: x86_64/op_return_void_no_barrier.S */ + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: x86_64/op_invoke_virtual_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 116 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualRange) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: x86_64/op_invoke_super_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeSuperRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 117 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeSuperRange) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: x86_64/op_invoke_direct_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeDirectRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 118 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeDirectRange) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: x86_64/op_invoke_static_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeStaticRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 119 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeStaticRange) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: x86_64/op_invoke_interface_range.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeInterfaceRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 120 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeInterfaceRange) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: x86_64/op_unused_79.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: x86_64/op_unused_7a.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: x86_64/op_neg_int.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + negl %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: x86_64/op_not_int.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + notl %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: x86_64/op_neg_long.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + negq %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: x86_64/op_not_long.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + notq %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: x86_64/op_neg_float.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + + xorl $0x80000000, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: x86_64/op_neg_double.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + movq $0x8000000000000000, %rsi + xorq %rsi, %rax + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: x86_64/op_int_to_long.S */ + /* int to long vA, vB */ + movzbq rINSTbl, %rax # rax <- +A + sarl $4, %eax # eax <- B + andb $0xf, rINSTbl # rINST <- A + movslq VREG_ADDRESS(%rax), %rax + SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: x86_64/op_int_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2ssl VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: x86_64/op_int_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2sdl VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: x86_64/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: x86_64/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if 0 + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: x86_64/op_long_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2ssq VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: x86_64/op_long_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsi2sdq VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: x86_64/op_float_to_int.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movss VREG_ADDRESS(rINSTq), %xmm0 + movl $0x7fffffff, %eax + cvtsi2ssl %eax, %xmm1 + comiss %xmm1, %xmm0 + jae 1f + jp 2f + cvttss2sil %xmm0, %eax + jmp 1f +2: + xorl %eax, %eax +1: + .if 0 + SET_WIDE_VREG %eax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: x86_64/op_float_to_long.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movss VREG_ADDRESS(rINSTq), %xmm0 + movq $0x7fffffffffffffff, %rax + cvtsi2ssq %rax, %xmm1 + comiss %xmm1, %xmm0 + jae 1f + jp 2f + cvttss2siq %xmm0, %rax + jmp 1f +2: + xorq %rax, %rax +1: + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %rax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: x86_64/op_float_to_double.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtss2sd VREG_ADDRESS(rINSTq), %xmm0 + .if 1 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: x86_64/op_double_to_int.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movsd VREG_ADDRESS(rINSTq), %xmm0 + movl $0x7fffffff, %eax + cvtsi2sdl %eax, %xmm1 + comisd %xmm1, %xmm0 + jae 1f + jp 2f + cvttsd2sil %xmm0, %eax + jmp 1f +2: + xorl %eax, %eax +1: + .if 0 + SET_WIDE_VREG %eax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: x86_64/op_double_to_long.S */ +/* File: x86_64/cvtfp_int.S */ +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + movsd VREG_ADDRESS(rINSTq), %xmm0 + movq $0x7fffffffffffffff, %rax + cvtsi2sdq %rax, %xmm1 + comisd %xmm1, %xmm0 + jae 1f + jp 2f + cvttsd2siq %xmm0, %rax + jmp 1f +2: + xorq %rax, %rax +1: + .if 1 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %rax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: x86_64/op_double_to_float.S */ +/* File: x86_64/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + cvtsd2ss VREG_ADDRESS(rINSTq), %xmm0 + .if 0 + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: x86_64/op_int_to_byte.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movsbl %al, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: x86_64/op_int_to_char.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movzwl %ax,%eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: x86_64/op_int_to_short.S */ +/* File: x86_64/unop.S */ +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4,rINST # rINST <- B + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $0xf,%cl # ecx <- A + +movswl %ax, %eax + .if 0 + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: x86_64/op_add_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + addl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: x86_64/op_sub_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + subl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: x86_64/op_mul_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + imull (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: x86_64/op_div_int.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 0 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %ecx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %ecx, %rcx # ecx <- vCC + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %eax, rINSTq # eax <- vBB + .else + SET_VREG %eax, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: x86_64/op_rem_int.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 0 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %ecx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %ecx, %rcx # ecx <- vCC + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %edx, rINSTq # eax <- vBB + .else + SET_VREG %edx, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: x86_64/op_and_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + andl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: x86_64/op_or_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + orl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: x86_64/op_xor_int.S */ +/* File: x86_64/binop.S */ +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + xorl (rFP,%rcx,4), %eax # ex: addl (rFP,%rcx,4),%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: x86_64/op_shl_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: x86_64/op_shr_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: x86_64/op_ushr_int.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 0 + GET_WIDE_VREG %rax, %rax # rax <- vBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: x86_64/op_add_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + addq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: x86_64/op_sub_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + subq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: x86_64/op_mul_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + imulq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: x86_64/op_div_long.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 1 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %rcx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %rcx, %rcx # ecx <- vCC + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rax, rINSTq # eax <- vBB + .else + SET_VREG %rax, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorq %rax, %rax + .else + negq %rax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: x86_64/op_rem_long.S */ +/* File: x86_64/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if 1 + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG %rcx, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG %rcx, %rcx # ecx <- vCC + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rdx, rINSTq # eax <- vBB + .else + SET_VREG %rdx, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorq %rdx, %rdx + .else + negq %rdx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: x86_64/op_and_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + andq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: x86_64/op_or_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + orq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: x86_64/op_xor_long.S */ +/* File: x86_64/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + xorq (rFP,%rcx,4), %rax # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: x86_64/op_shl_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + salq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + salq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: x86_64/op_shr_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + sarq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + sarq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: x86_64/op_ushr_long.S */ +/* File: x86_64/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if 1 + GET_WIDE_VREG %rax, %rax # rax <- vBB + shrq %cl, %rax # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + shrq %cl, %rax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: x86_64/op_add_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + addss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: x86_64/op_sub_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + subss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: x86_64/op_mul_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + mulss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: x86_64/op_div_float.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + divss VREG_ADDRESS(%rax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: x86_64/op_rem_float.S */ + /* rem_float vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + flds VREG_ADDRESS(%rcx) # vBB to fp stack + flds VREG_ADDRESS(%rax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINSTq) # %st to vAA + CLEAR_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: x86_64/op_add_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + addsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: x86_64/op_sub_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + subsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: x86_64/op_mul_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + mulsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: x86_64/op_div_double.S */ +/* File: x86_64/sseBinop.S */ + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + divsd VREG_ADDRESS(%rax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: x86_64/op_rem_double.S */ + /* rem_double vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st + CLEAR_WIDE_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: x86_64/op_add_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + addl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: x86_64/op_sub_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + subl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: x86_64/op_mul_int_2addr.S */ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, %rcx # eax <- vA + imull (rFP,rINSTq,4), %eax + SET_VREG %eax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: x86_64/op_div_int_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %ecx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %ecx, %rcx # ecx <- vB + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %eax, rINSTq # vA <- result + .else + SET_VREG %eax, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: x86_64/op_rem_int_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %ecx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %ecx, %rcx # ecx <- vB + .endif + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rdx:rax <- sign-extended of rax + idivl %ecx +1: + .if 0 + SET_WIDE_VREG %edx, rINSTq # vA <- result + .else + SET_VREG %edx, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: x86_64/op_and_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + andl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: x86_64/op_or_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + orl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: x86_64/op_xor_int_2addr.S */ +/* File: x86_64/binop2addr.S */ +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + xorl %eax, (rFP,%rcx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: x86_64/op_shl_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sall %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sall %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: x86_64/op_shr_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sarl %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sarl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: x86_64/op_ushr_int_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 0 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + shrl %cl, %eax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + shrl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: x86_64/op_add_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + addq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: x86_64/op_sub_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + subq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: x86_64/op_mul_long_2addr.S */ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, %rcx # rax <- vA + imulq (rFP,rINSTq,4), %rax + SET_WIDE_VREG %rax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: x86_64/op_div_long_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %rcx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %rcx, %rcx # ecx <- vB + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rax, rINSTq # vA <- result + .else + SET_VREG %rax, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 0 + xorq %rax, %rax + .else + negq %rax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: x86_64/op_rem_long_2addr.S */ +/* File: x86_64/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # rcx <- B + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG %rcx, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG %rcx, %rcx # ecx <- vB + .endif + testq %rcx, %rcx + jz common_errDivideByZero + cmpq $-1, %rcx + je 2f + cqo # rdx:rax <- sign-extended of rax + idivq %rcx +1: + .if 1 + SET_WIDE_VREG %rdx, rINSTq # vA <- result + .else + SET_VREG %rdx, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if 1 + xorq %rdx, %rdx + .else + negq %rdx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: x86_64/op_and_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + andq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: x86_64/op_or_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + orq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: x86_64/op_xor_long_2addr.S */ +/* File: x86_64/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + xorq %rax, (rFP,%rcx,4) # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: x86_64/op_shl_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + salq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + salq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: x86_64/op_shr_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + sarq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + sarq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86_64/op_ushr_long_2addr.S */ +/* File: x86_64/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $0xf, rINSTbl # rINST <- A + .if 1 + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + shrq %cl, %rax # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + shrq %cl, %rax # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: x86_64/op_add_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: x86_64/op_sub_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: x86_64/op_mul_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: x86_64/op_div_float_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divss VREG_ADDRESS(rINSTq), %xmm0 + movss %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: x86_64/op_rem_float_2addr.S */ + /* rem_float/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $4, rINST # rINST <- B + flds VREG_ADDRESS(rINSTq) # vB to fp stack + andb $0xf, %cl # ecx <- A + flds VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%rcx) # %st to vA + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: x86_64/op_add_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: x86_64/op_sub_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: x86_64/op_mul_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: x86_64/op_div_double_2addr.S */ +/* File: x86_64/sseBinop2Addr.S */ + movl rINST, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divsd VREG_ADDRESS(rINSTq), %xmm0 + movsd %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: x86_64/op_rem_double_2addr.S */ + /* rem_double/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $4, rINST # rINST <- B + fldl VREG_ADDRESS(rINSTq) # vB to fp stack + andb $0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%rcx) # %st to vA + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: x86_64/op_add_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + addl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: x86_64/op_rsub_int.S */ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + subl %eax, %ecx # for example: addl %ecx, %eax + SET_VREG %ecx, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: x86_64/op_mul_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + imull %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: x86_64/op_div_int_lit16.S */ +/* File: x86_64/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %eax, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: x86_64/op_rem_int_lit16.S */ +/* File: x86_64/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %edx, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: x86_64/op_and_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: x86_64/op_or_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + orl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: x86_64/op_xor_int_lit16.S */ +/* File: x86_64/binopLit16.S */ +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + xorl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: x86_64/op_add_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + addl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86_64/op_rsub_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + subl %eax, %ecx # ex: addl %ecx,%eax + SET_VREG %ecx, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: x86_64/op_mul_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + imull %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: x86_64/op_div_int_lit8.S */ +/* File: x86_64/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %eax, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 0 + xorl %eax, %eax + .else + negl %eax + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: x86_64/op_rem_int_lit8.S */ +/* File: x86_64/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG %edx, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if 1 + xorl %edx, %edx + .else + negl %edx + .endif + jmp 1b + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: x86_64/op_and_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + andl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: x86_64/op_or_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + orl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: x86_64/op_xor_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + xorl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: x86_64/op_shl_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: x86_64/op_shr_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86_64/op_ushr_int_lit8.S */ +/* File: x86_64/binopLit8.S */ +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: x86_64/op_iget_wide_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 1 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movswl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: x86_64/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + .extern artIGetObjectFromMterp + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) + movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset + EXPORT_PC + callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movl rINST, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: x86_64/op_iput_wide_quick.S */ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwq 2(rPC), %rax # rax<- field byte offset + leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target + andb $0xf, rINSTbl # rINST<- A + GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] + movq %rax, (%rcx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: x86_64/op_iput_object_quick.S */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST 232 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpIputObjectQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86_64/op_invoke_virtual_quick.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuick + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 233 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualQuick) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86_64/op_invoke_virtual_range_quick.S */ +/* File: x86_64/invoke.S */ +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern MterpInvokeVirtualQuickRange + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST 234 + movl rINST, OUT_32_ARG3 + call SYMBOL(MterpInvokeVirtualQuickRange) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: x86_64/op_iput_boolean_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movb rINSTbl, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: x86_64/op_iput_byte_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movb rINSTbl, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: x86_64/op_iput_char_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movw rINSTw, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: x86_64/op_iput_short_quick.S */ +/* File: x86_64/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + movw rINSTw, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: x86_64/op_iget_boolean_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movsbl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: x86_64/op_iget_byte_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movsbl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: x86_64/op_iget_char_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movzwl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: x86_64/op_iget_short_quick.S */ +/* File: x86_64/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf,rINSTbl # rINST <- A + .if 0 + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + movswl (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_lambda: /* 0xf3 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_f4: /* 0xf4 */ +/* File: x86_64/op_unused_f4.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_capture_variable: /* 0xf5 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_create_lambda: /* 0xf6 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_liberate_variable: /* 0xf7 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_box_lambda: /* 0xf8 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unbox_lambda: /* 0xf9 */ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fa: /* 0xfa */ +/* File: x86_64/op_unused_fa.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: x86_64/op_unused_fb.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: x86_64/op_unused_fc.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: x86_64/op_unused_fd.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: x86_64/op_unused_fe.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: x86_64/op_unused_ff.S */ +/* File: x86_64/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + + .balign 128 + SIZE(SYMBOL(artMterpAsmInstructionStart),SYMBOL(artMterpAsmInstructionStart)) + .global SYMBOL(artMterpAsmInstructionEnd) +SYMBOL(artMterpAsmInstructionEnd): + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global SYMBOL(artMterpAsmSisterStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmSisterStart)) + .text + .balign 4 +SYMBOL(artMterpAsmSisterStart): + + SIZE(SYMBOL(artMterpAsmSisterStart),SYMBOL(artMterpAsmSisterStart)) + .global SYMBOL(artMterpAsmSisterEnd) +SYMBOL(artMterpAsmSisterEnd): + + + .global SYMBOL(artMterpAsmAltInstructionStart) + FUNCTION_TYPE(SYMBOL(artMterpAsmAltInstructionStart)) + .text + +SYMBOL(artMterpAsmAltInstructionStart) = .L_ALT_op_nop +/* ------------------------------ */ + .balign 128 +.L_ALT_op_nop: /* 0x00 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(0*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(1*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(2*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(3*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(4*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(5*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(6*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(7*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(8*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(9*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(10*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(11*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(12*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(13*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(14*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(15*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(16*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(17*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(18*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(19*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(20*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(21*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(22*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(23*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(24*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(25*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(26*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(27*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(28*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(29*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(30*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(31*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(32*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(33*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(34*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(35*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(36*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(37*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(38*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(39*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(40*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(41*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(42*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(43*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(44*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(45*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(46*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(47*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(48*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(49*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(50*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(51*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(52*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(53*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(54*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(55*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(56*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(57*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(58*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(59*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(60*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(61*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(62*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(63*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(64*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(65*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(66*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(67*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(68*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(69*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(70*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(71*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(72*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(73*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(74*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(75*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(76*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(77*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(78*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(79*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(80*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(81*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(82*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(83*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(84*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(85*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(86*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(87*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(88*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(89*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(90*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(91*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(92*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(93*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(94*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(95*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(96*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(97*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(98*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(99*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(100*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(101*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(102*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(103*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(104*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(105*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(106*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(107*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(108*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(109*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(110*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(111*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(112*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(113*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(114*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(115*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(116*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(117*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(118*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(119*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(120*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(121*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(122*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(123*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(124*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(125*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(126*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(127*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(128*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(129*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(130*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(131*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(132*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(133*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(134*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(135*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(136*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(137*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(138*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(139*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(140*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(141*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(142*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(143*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(144*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(145*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(146*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(147*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(148*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(149*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(150*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(151*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(152*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(153*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(154*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(155*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(156*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(157*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(158*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(159*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(160*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(161*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(162*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(163*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(164*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(165*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(166*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(167*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(168*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(169*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(170*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(171*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(172*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(173*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(174*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(175*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(176*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(177*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(178*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(179*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(180*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(181*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(182*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(183*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(184*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(185*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(186*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(187*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(188*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(189*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(190*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(191*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(192*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(193*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(194*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(195*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(196*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(197*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(198*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(199*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(200*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(201*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(202*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(203*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(204*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(205*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(206*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(207*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(208*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(209*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(210*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(211*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(212*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(213*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(214*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(215*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(216*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(217*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(218*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(219*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(220*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(221*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(222*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(223*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(224*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(225*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(226*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(227*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(228*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(229*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(230*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(231*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(232*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(233*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(234*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(235*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(236*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(237*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(238*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(239*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(240*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(241*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(242*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(243*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(244*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(245*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(246*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(247*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(248*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(249*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(250*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(251*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(252*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(253*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(254*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: x86_64/alt_stub.S */ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(255*128) + + .balign 128 + SIZE(SYMBOL(artMterpAsmAltInstructionStart),SYMBOL(artMterpAsmAltInstructionStart)) + .global SYMBOL(artMterpAsmAltInstructionEnd) +SYMBOL(artMterpAsmAltInstructionEnd): +/* File: x86_64/footer.S */ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * 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. + */ +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogDivideByZeroException) +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogArrayIndexException) +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNegativeArraySizeException) +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNoSuchMethodException) +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNullObjectException) +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogExceptionThrownException) +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 + call SYMBOL(MterpLogSuspendFallback) +#endif + jmp MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + cmpq $0, THREAD_EXCEPTION_OFFSET(rSELF) + jz MterpFallback + /* intentional fallthrough - handle pending exception. */ + +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpHandleException) + testb %al, %al + jz MterpExceptionReturn + movq OFF_FP_CODE_ITEM(rFP), %rax + mov OFF_FP_DEX_PC(rFP), %ecx + leaq CODEITEM_INSNS_OFFSET(%rax), rPC + leaq (rPC, %rcx, 2), rPC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + /* resume execution at catch block */ + REFRESH_IBASE + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + REFRESH_IBASE + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + EXPORT_PC + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GOTO_NEXT + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpLogOSR) +#endif + movl $1, %eax + jmp MterpDone + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogFallback) +#endif +MterpCommonFallback: + xorl %eax, %eax + jmp MterpDone + +/* + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + movl $1, %eax + jmp MterpDone +MterpReturn: + movq OFF_FP_RESULT_REGISTER(rFP), %rdx + movq %rax, (%rdx) + movl $1, %eax +MterpDone: + /* pop up frame */ + addq $FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset -FRAME_SIZE + + /* Restore callee save register */ + POP %r15 + POP %r14 + POP %r13 + POP %r12 + POP %rbp + POP %rbx + ret + .cfi_endproc + SIZE(ExecuteMterpImpl,ExecuteMterpImpl) + diff --git a/runtime/interpreter/mterp/rebuild.sh b/runtime/interpreter/mterp/rebuild.sh index ac8794581cdc4f6fadcd72a9b067420a8221cb8b..ca3dcd9a136a27844fcba607cc3f27c11b1e08c3 100755 --- a/runtime/interpreter/mterp/rebuild.sh +++ b/runtime/interpreter/mterp/rebuild.sh @@ -20,5 +20,4 @@ # set -e -# for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done -for arch in arm x86 arm64 ; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done +for arch in arm x86 mips arm64 x86_64 mips64; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done diff --git a/runtime/interpreter/mterp/x86/bincmp.S b/runtime/interpreter/mterp/x86/bincmp.S index 27cf6ea6d4d491e8ef982cac16fe3f6b8a62d94d..c72a5cf9d4a65658bd01e4a8da651b7541b246d7 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/entry.S b/runtime/interpreter/mterp/x86/entry.S index b83f7e1d83b39b266ec8f6e49c00471cdcd39cc0..785efdc5cc0242c4dc4512df07b9972cc316f223 100644 --- a/runtime/interpreter/mterp/x86/entry.S +++ b/runtime/interpreter/mterp/x86/entry.S @@ -32,16 +32,18 @@ SYMBOL(ExecuteMterpImpl): .cfi_startproc + .cfi_def_cfa esp, 4 + + /* Spill callee save regs */ + PUSH %ebp + PUSH %edi + PUSH %esi + PUSH %ebx + /* Allocate frame */ subl $$FRAME_SIZE, %esp .cfi_adjust_cfa_offset FRAME_SIZE - /* Spill callee save regs */ - movl %ebp, EBP_SPILL(%esp) - movl %edi, EDI_SPILL(%esp) - movl %esi, ESI_SPILL(%esp) - movl %ebx, EBX_SPILL(%esp) - /* Load ShadowFrame pointer */ movl IN_ARG2(%esp), %edx diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S index a1532fa7106c1594761fe9a7913f43890c83b4cd..3965ecde62a7462c1e3fa9dd3ac8495676e8e52f 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 @@ -116,13 +115,17 @@ MterpException: call SYMBOL(MterpHandleException) testb %al, %al jz MterpExceptionReturn - REFRESH_IBASE movl OFF_FP_CODE_ITEM(rFP), %eax movl OFF_FP_DEX_PC(rFP), %ecx lea CODEITEM_INSNS_OFFSET(%eax), rPC lea (rPC, %ecx, 2), rPC movl rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback /* resume execution at catch block */ + REFRESH_IBASE FETCH_INST GOTO_NEXT /* NOTE: no fallthrough */ @@ -142,6 +145,21 @@ MterpCheckSuspendAndContinue: 1: 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. */ @@ -171,16 +189,15 @@ MterpReturn: movl %ecx, 4(%edx) mov $$1, %eax MterpDone: - /* Restore callee save register */ - movl EBP_SPILL(%esp), %ebp - movl EDI_SPILL(%esp), %edi - movl ESI_SPILL(%esp), %esi - movl EBX_SPILL(%esp), %ebx - /* pop up frame */ addl $$FRAME_SIZE, %esp .cfi_adjust_cfa_offset -FRAME_SIZE - ret + /* Restore callee save register */ + POP %ebx + POP %esi + POP %edi + POP %ebp + ret .cfi_endproc SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 3fbbbf955e4cec63975409ffa6f625acc420d34b..5729b90ea6d2a89cd655b2a806963ebed5096892 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -105,25 +105,32 @@ unspecified registers or condition codes. #define SYMBOL(name) name #endif +.macro PUSH _reg + pushl \_reg + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popl \_reg + .cfi_adjust_cfa_offset -4 + .cfi_restore \_reg +.endm + /* Frame size must be 16-byte aligned. - * Remember about 4 bytes for return address + * Remember about 4 bytes for return address + 4 * 4 for spills */ -#define FRAME_SIZE 44 +#define FRAME_SIZE 28 /* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ -#define IN_ARG3 (FRAME_SIZE + 16) -#define IN_ARG2 (FRAME_SIZE + 12) -#define IN_ARG1 (FRAME_SIZE + 8) -#define IN_ARG0 (FRAME_SIZE + 4) -#define CALLER_RP (FRAME_SIZE + 0) +#define IN_ARG3 (FRAME_SIZE + 16 + 16) +#define IN_ARG2 (FRAME_SIZE + 16 + 12) +#define IN_ARG1 (FRAME_SIZE + 16 + 8) +#define IN_ARG0 (FRAME_SIZE + 16 + 4) /* Spill offsets relative to %esp */ -#define EBP_SPILL (FRAME_SIZE - 4) -#define EDI_SPILL (FRAME_SIZE - 8) -#define ESI_SPILL (FRAME_SIZE - 12) -#define EBX_SPILL (FRAME_SIZE - 16) -#define LOCAL0 (FRAME_SIZE - 20) -#define LOCAL1 (FRAME_SIZE - 24) -#define LOCAL2 (FRAME_SIZE - 28) +#define LOCAL0 (FRAME_SIZE - 4) +#define LOCAL1 (FRAME_SIZE - 8) +#define LOCAL2 (FRAME_SIZE - 12) /* Out Arg offsets, relative to %esp */ #define OUT_ARG3 ( 12) #define OUT_ARG2 ( 8) @@ -156,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 diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S index bbd88cf40b63d2b4cd419db02a4c0404ba1f2a21..c23053becb94cbb05aaf4b303481664afb50e2ee 100644 --- a/runtime/interpreter/mterp/x86/invoke.S +++ b/runtime/interpreter/mterp/x86/invoke.S @@ -16,5 +16,10 @@ call SYMBOL($helper) testb %al, %al jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback RESTORE_IBASE - ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + FETCH_INST + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_goto.S b/runtime/interpreter/mterp/x86/op_goto.S index 411399d3ad513b392c66e2a1582eb79bfe3c85f0..9a87361c8eee6d70287aecbad1af0a7a88d063e9 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 4f04f9e479aa0f529cf5e9b74d5c18d314444470..a25c31b2d0fc5cab23f5d904de4c5086e08396ba 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 48f6e5afd74ca7c9caf6960f6721512939d3df0a..159128be1c05af5e06f5198f88c48c1b839bffab 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_packed_switch.S b/runtime/interpreter/mterp/x86/op_packed_switch.S index 230b58e02b55c0333d6c1a5ec3926f9f014ccfd2..e33cf7549931e3ab1c57fad3cc2d1f8dffd29e14 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/zcmp.S b/runtime/interpreter/mterp/x86/zcmp.S index 5ce4f0f6a77ebe6e6c4b06cb9b3aebea1fff32bc..0f28d1acd8dbf4aa0ba371d9ca44778cd120010f 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/mterp/x86_64/alt_stub.S b/runtime/interpreter/mterp/x86_64/alt_stub.S new file mode 100644 index 0000000000000000000000000000000000000000..6fcebbba3c8196f6bb0519559e48834d8f7446a6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/alt_stub.S @@ -0,0 +1,17 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + REFRESH_IBASE + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpCheckBefore) # (self, shadow_frame) + jmp .L_op_nop+(${opnum}*${handler_size_bytes}) diff --git a/runtime/interpreter/mterp/x86_64/bincmp.S b/runtime/interpreter/mterp/x86_64/bincmp.S new file mode 100644 index 0000000000000000000000000000000000000000..a16050b3714183d53efadef4b2e279670582456d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bincmp.S @@ -0,0 +1,23 @@ +/* + * Generic two-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le + */ + /* if-cmp vA, vB, +CCCC */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # rcx <- A + GET_VREG %eax, %rcx # eax <- vA + cmpl VREG_ADDRESS(rINSTq), %eax # compare (vA, vB) + movl $$2, rINST # assume not taken + j${revcmp} 1f + movswq 2(rPC), rINSTq # Get signed branch offset +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rax <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/bindiv.S b/runtime/interpreter/mterp/x86_64/bindiv.S new file mode 100644 index 0000000000000000000000000000000000000000..e10d1dc4b10094c4a99b6a31df435fc1091e6362 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindiv.S @@ -0,0 +1,34 @@ +%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + .if $wide + GET_WIDE_VREG %rax, %rax # eax <- vBB + GET_WIDE_VREG $second, %rcx # ecx <- vCC + .else + GET_VREG %eax, %rax # eax <- vBB + GET_VREG $second, %rcx # ecx <- vCC + .endif + test${suffix} $second, $second + jz common_errDivideByZero + cmp${suffix} $$-1, $second + je 2f + $ext # rdx:rax <- sign-extended of rax + idiv${suffix} $second +1: + .if $wide + SET_WIDE_VREG $result, rINSTq # eax <- vBB + .else + SET_VREG $result, rINSTq # eax <- vBB + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xor${suffix} $result, $result + .else + neg${suffix} $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindiv2addr.S b/runtime/interpreter/mterp/x86_64/bindiv2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..8b9bc953d2e6591f1551b6a159b9bde61c2f4429 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindiv2addr.S @@ -0,0 +1,35 @@ +%default {"result":"","second":"","wide":"","suffix":"","rem":"0","ext":"cdq"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/2addr vA, vB */ + movl rINST, %ecx # rcx <- BA + sarl $$4, %ecx # rcx <- B + andb $$0xf, rINSTbl # rINST <- A + .if $wide + GET_WIDE_VREG %rax, rINSTq # eax <- vA + GET_WIDE_VREG $second, %rcx # ecx <- vB + .else + GET_VREG %eax, rINSTq # eax <- vA + GET_VREG $second, %rcx # ecx <- vB + .endif + test${suffix} $second, $second + jz common_errDivideByZero + cmp${suffix} $$-1, $second + je 2f + $ext # rdx:rax <- sign-extended of rax + idiv${suffix} $second +1: + .if $wide + SET_WIDE_VREG $result, rINSTq # vA <- result + .else + SET_VREG $result, rINSTq # vA <- result + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 +2: + .if $rem + xor${suffix} $result, $result + .else + neg${suffix} $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit16.S b/runtime/interpreter/mterp/x86_64/bindivLit16.S new file mode 100644 index 0000000000000000000000000000000000000000..80dbce297549f5b192b03beffeae28237347e011 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindivLit16.S @@ -0,0 +1,27 @@ +%default {"result":"","rem":"0"} +/* + * 32-bit binary div/rem operation. Handles special case of op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movl rINST, %eax # rax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $$0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + jz common_errDivideByZero + cmpl $$-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG $result, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xorl $result, $result + .else + negl $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/bindivLit8.S b/runtime/interpreter/mterp/x86_64/bindivLit8.S new file mode 100644 index 0000000000000000000000000000000000000000..ab535f3fb0a564e1f06e21ada90f7b87d5597971 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/bindivLit8.S @@ -0,0 +1,25 @@ +%default {"result":"","rem":"0"} +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $$-1, %ecx + je 2f + cdq # rax <- sign-extended of eax + idivl %ecx +1: + SET_VREG $result, rINSTq # vA <- result + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 +2: + .if $rem + xorl $result, $result + .else + negl $result + .endif + jmp 1b diff --git a/runtime/interpreter/mterp/x86_64/binop.S b/runtime/interpreter/mterp/x86_64/binop.S new file mode 100644 index 0000000000000000000000000000000000000000..962dd61eea875919314693b1121ab165b7705813 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop.S @@ -0,0 +1,17 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit binary operation. Provide an "instr" line that + * specifies an instruction that performs "result = eax op (rFP,%ecx,4)". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int, sub-int, and-int, or-int, + * xor-int, shl-int, shr-int, ushr-int + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB + $instr # ex: addl (rFP,%rcx,4),%eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop1.S b/runtime/interpreter/mterp/x86_64/binop1.S new file mode 100644 index 0000000000000000000000000000000000000000..bdd57325aadaf04611b72eb1dfa41dd224ad36d0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop1.S @@ -0,0 +1,19 @@ +%default {"wide":"0"} +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %ecx, %rcx # eax <- vCC + .if $wide + GET_WIDE_VREG %rax, %rax # rax <- vBB + $instr # ex: addl %ecx,%eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, %rax # eax <- vBB + $instr # ex: addl %ecx,%eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binop2addr.S b/runtime/interpreter/mterp/x86_64/binop2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..4448a815e99cee6ae86bbd36cafe350c973aa996 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binop2addr.S @@ -0,0 +1,19 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "/2addr" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = r0 op r1". + * This could be an instruction or a function call. + * + * For: add-int/2addr, sub-int/2addr, mul-int/2addr, div-int/2addr, + * rem-int/2addr, and-int/2addr, or-int/2addr, xor-int/2addr, + * shl-int/2addr, shr-int/2addr, ushr-int/2addr, add-float/2addr, + * sub-float/2addr, mul-float/2addr, div-float/2addr, rem-float/2addr + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_VREG %eax, rINSTq # eax <- vB + $instr # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/binopLit16.S b/runtime/interpreter/mterp/x86_64/binopLit16.S new file mode 100644 index 0000000000000000000000000000000000000000..de43b53d5cb3615f6a29895dafec80d11e80e656 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopLit16.S @@ -0,0 +1,19 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "lit16" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than eax, you can override "result".) + * + * For: add-int/lit16, rsub-int, + * and-int/lit16, or-int/lit16, xor-int/lit16 + */ + /* binop/lit16 vA, vB, #+CCCC */ + movl rINST, %eax # rax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax, %rax # eax <- vB + andb $$0xf, rINSTbl # rINST <- A + movswl 2(rPC), %ecx # ecx <- ssssCCCC + $instr # for example: addl %ecx, %eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopLit8.S b/runtime/interpreter/mterp/x86_64/binopLit8.S new file mode 100644 index 0000000000000000000000000000000000000000..995002b7e45caa3161910f8c8d4bd590a91d618b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopLit8.S @@ -0,0 +1,18 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "lit8" binary operation. Provide an "instr" line + * that specifies an instruction that performs "result = eax op ecx". + * This could be an x86 instruction or a function call. (If the result + * comes back in a register other than r0, you can override "result".) + * + * For: add-int/lit8, rsub-int/lit8 + * and-int/lit8, or-int/lit8, xor-int/lit8, + * shl-int/lit8, shr-int/lit8, ushr-int/lit8 + */ + /* binop/lit8 vAA, vBB, #+CC */ + movzbq 2(rPC), %rax # rax <- BB + movsbl 3(rPC), %ecx # rcx <- ssssssCC + GET_VREG %eax, %rax # eax <- rBB + $instr # ex: addl %ecx,%eax + SET_VREG $result, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide.S b/runtime/interpreter/mterp/x86_64/binopWide.S new file mode 100644 index 0000000000000000000000000000000000000000..f92f18e013a6b46c99c8319a3f991538fd78e65e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopWide.S @@ -0,0 +1,10 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rax, %rax # rax <- v[BB] + $instr # ex: addq (rFP,%rcx,4),%rax + SET_WIDE_VREG %rax, rINSTq # v[AA] <- rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/binopWide2addr.S b/runtime/interpreter/mterp/x86_64/binopWide2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..d9e6cfbc9dc05700f635886bf6dda83dd3ec50a2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/binopWide2addr.S @@ -0,0 +1,11 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, rINSTq # rax <- vB + $instr # for ex: addq %rax,(rFP,%rcx,4) + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/cvtfp_int.S b/runtime/interpreter/mterp/x86_64/cvtfp_int.S new file mode 100644 index 0000000000000000000000000000000000000000..1472bd26bd5f13ee9cab83fd374aaa87be6edf36 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/cvtfp_int.S @@ -0,0 +1,27 @@ +%default {"fp_suffix":"","i_suffix":"","max_const":"","result_reg":"","wide":""} +/* On fp to int conversions, Java requires that + * if the result > maxint, it should be clamped to maxint. If it is less + * than minint, it should be clamped to minint. If it is a nan, the result + * should be zero. Further, the rounding mode is to truncate. + */ + /* float/double to int/long vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + movs${fp_suffix} VREG_ADDRESS(rINSTq), %xmm0 + mov${i_suffix} ${max_const}, ${result_reg} + cvtsi2s${fp_suffix}${i_suffix} ${result_reg}, %xmm1 + comis${fp_suffix} %xmm1, %xmm0 + jae 1f + jp 2f + cvtts${fp_suffix}2si${i_suffix} %xmm0, ${result_reg} + jmp 1f +2: + xor${i_suffix} ${result_reg}, ${result_reg} +1: + .if $wide + SET_WIDE_VREG ${result_reg}, %rcx + .else + SET_VREG ${result_reg}, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S new file mode 100644 index 0000000000000000000000000000000000000000..69b2371dea52545bb0f881d5cfe9d8ab750bbf51 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -0,0 +1,71 @@ +/* + * 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. + */ +/* + * Interpreter entry point. + */ + + .text + .global SYMBOL(ExecuteMterpImpl) + FUNCTION_TYPE(ExecuteMterpImpl) + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +SYMBOL(ExecuteMterpImpl): + .cfi_startproc + .cfi_def_cfa rsp, 8 + + /* Spill callee save regs */ + PUSH %rbx + PUSH %rbp + PUSH %r12 + PUSH %r13 + PUSH %r14 + PUSH %r15 + + /* Allocate frame */ + subq $$FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset FRAME_SIZE + + /* Remember the return register */ + movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2) + + /* Remember the code_item */ + movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax + leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP + leaq (rFP, %rax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax + leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC + leaq (rPC, %rax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + movq IN_ARG0, rSELF + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86_64/fallback.S b/runtime/interpreter/mterp/x86_64/fallback.S new file mode 100644 index 0000000000000000000000000000000000000000..8d61166f63d57a950db9bb1e3bceef3edb6a8374 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fallback.S @@ -0,0 +1,3 @@ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S new file mode 100644 index 0000000000000000000000000000000000000000..573256b78135ac2907db87d028b8221aafd68e37 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/footer.S @@ -0,0 +1,181 @@ +/* + * =========================================================================== + * Common subroutines and data + * =========================================================================== + */ + + .text + .align 2 + +/* + * We've detected a condition that will result in an exception, but the exception + * 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. + */ +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogDivideByZeroException) +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogArrayIndexException) +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNegativeArraySizeException) +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNoSuchMethodException) +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogNullObjectException) +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogExceptionThrownException) +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl THREAD_FLAGS_OFFSET(rSELF), OUT_32_ARG2 + call SYMBOL(MterpLogSuspendFallback) +#endif + jmp MterpCommonFallback + +/* + * If we're here, something is out of the ordinary. If there is a pending + * exception, handle it. Otherwise, roll back and retry with the reference + * interpreter. + */ +MterpPossibleException: + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jz MterpFallback + /* intentional fallthrough - handle pending exception. */ + +/* + * On return from a runtime helper routine, we've found a pending exception. + * Can we handle it here - or need to bail out to caller? + * + */ +MterpException: + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpHandleException) + testb %al, %al + jz MterpExceptionReturn + movq OFF_FP_CODE_ITEM(rFP), %rax + mov OFF_FP_DEX_PC(rFP), %ecx + leaq CODEITEM_INSNS_OFFSET(%rax), rPC + leaq (rPC, %rcx, 2), rPC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) + /* Do we need to switch interpreters? */ + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + /* resume execution at catch block */ + REFRESH_IBASE + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + +/* + * Check for suspend check request. Assumes rINST already loaded, rPC advanced and + * still needs to get the opcode and branch to it, and flags are in lr. + */ +MterpCheckSuspendAndContinue: + REFRESH_IBASE + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + EXPORT_PC + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GOTO_NEXT + +/* + * On-stack replacement has happened, and now we've returned from the compiled method. + */ +MterpOnStackReplacement: +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpLogOSR) +#endif + movl $$1, %eax + jmp MterpDone + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + call SYMBOL(MterpLogFallback) +#endif +MterpCommonFallback: + xorl %eax, %eax + jmp MterpDone + +/* + * On entry: + * uint32_t* rFP (should still be live, pointer to base of vregs) + */ +MterpExceptionReturn: + movl $$1, %eax + jmp MterpDone +MterpReturn: + movq OFF_FP_RESULT_REGISTER(rFP), %rdx + movq %rax, (%rdx) + movl $$1, %eax +MterpDone: + /* pop up frame */ + addq $$FRAME_SIZE, %rsp + .cfi_adjust_cfa_offset -FRAME_SIZE + + /* Restore callee save register */ + POP %r15 + POP %r14 + POP %r13 + POP %r12 + POP %rbp + POP %rbx + ret + .cfi_endproc + SIZE(ExecuteMterpImpl,ExecuteMterpImpl) diff --git a/runtime/interpreter/mterp/x86_64/fpcmp.S b/runtime/interpreter/mterp/x86_64/fpcmp.S new file mode 100644 index 0000000000000000000000000000000000000000..806bc2b1ef392193748e8caf680b227f45f1dba9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fpcmp.S @@ -0,0 +1,35 @@ +%default {"suff":"d","nanval":"pos"} +/* + * Compare two floating-point values. Puts 0, 1, or -1 into the + * destination register based on the results of the comparison. + * + * int compare(x, y) { + * if (x == y) { + * return 0; + * } else if (x < y) { + * return -1; + * } else if (x > y) { + * return 1; + * } else { + * return nanval ? 1 : -1; + * } + * } + */ + /* op vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx<- CC + movzbq 2(rPC), %rax # eax<- BB + movs${suff} VREG_ADDRESS(%rax), %xmm0 + xor %eax, %eax + ucomis${suff} VREG_ADDRESS(%rcx), %xmm0 + jp .L${opcode}_nan_is_${nanval} + je .L${opcode}_finish + jb .L${opcode}_less +.L${opcode}_nan_is_pos: + addb $$1, %al + jmp .L${opcode}_finish +.L${opcode}_nan_is_neg: +.L${opcode}_less: + movl $$-1, %eax +.L${opcode}_finish: + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/fpcvt.S b/runtime/interpreter/mterp/x86_64/fpcvt.S new file mode 100644 index 0000000000000000000000000000000000000000..657869e0bdd60e5df4ce942f57df29d03f26d308 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/fpcvt.S @@ -0,0 +1,17 @@ +%default {"source_suffix":"","dest_suffix":"","wide":""} +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + cvts${source_suffix}2s${dest_suffix} VREG_ADDRESS(rINSTq), %xmm0 + .if $wide + movsd %xmm0, VREG_ADDRESS(%rcx) + CLEAR_WIDE_REF %rcx + .else + movss %xmm0, VREG_ADDRESS(%rcx) + CLEAR_REF %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S new file mode 100644 index 0000000000000000000000000000000000000000..eb84ea1eb545353a0856697412dec456aa486bec --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -0,0 +1,303 @@ +/* + * 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. + */ + +/* + Art assembly interpreter notes: + + First validate assembly code by implementing ExecuteXXXImpl() style body (doesn't + handle invoke, allows higher-level code to create frame & shadow frame. + + Once that's working, support direct entry code & eliminate shadow frame (and + excess locals allocation. + + Some (hopefully) temporary ugliness. We'll treat rFP as pointing to the + base of the vreg array within the shadow frame. Access the other fields, + dex_pc_, method_ and number_of_vregs_ via negative offsets. For now, we'll continue + the shadow frame mechanism of double-storing object references - via rFP & + number_of_vregs_. + + */ + +/* +x86_64 ABI general notes: + +Caller save set: + rax, rdx, rcx, rsi, rdi, r8-r11, st(0)-st(7) +Callee save set: + rbx, rbp, r12-r15 +Return regs: + 32-bit in eax + 64-bit in rax + fp on xmm0 + +First 8 fp parameters came in xmm0-xmm7. +First 6 non-fp parameters came in rdi, rsi, rdx, rcx, r8, r9. +Other parameters passed on stack, pushed right-to-left. On entry to target, first +param is at 8(%esp). Traditional entry code is: + +Stack must be 16-byte aligned to support SSE in native code. + +If we're not doing variable stack allocation (alloca), the frame pointer can be +eliminated and all arg references adjusted to be esp relative. +*/ + +/* +Mterp and x86_64 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rSELF rbp pointer to ThreadSelf. + rPC r12 interpreted program counter, used for fetching instructions + rFP r13 interpreted frame pointer, used for accessing locals and args + rINSTw bx first 16-bit code of current instruction + rINSTbl bl opcode portion of instruction word + rINSTbh bh high byte of inst word, usually contains src/tgt reg names + rIBASE r14 base of instruction handler table + rREFS r15 base of object references in shadow frame. + +Notes: + o High order 16 bits of ebx must be zero on entry to handler + o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit + o eax and ecx are scratch, rINSTw/ebx sometimes scratch + +Macros are provided for common operations. Each macro MUST emit only +one instruction to make instruction-counting easier. They MUST NOT alter +unspecified registers or condition codes. +*/ + +/* + * This is a #include, not a %include, because we want the C pre-processor + * to expand the macros into assembler assignment statements. + */ +#include "asm_support.h" + +/* + * Handle mac compiler specific + */ +#if defined(__APPLE__) + #define MACRO_LITERAL(value) $$(value) + #define FUNCTION_TYPE(name) + #define SIZE(start,end) + // Mac OS' symbols have an _ prefix. + #define SYMBOL(name) _ ## name +#else + #define MACRO_LITERAL(value) $$value + #define FUNCTION_TYPE(name) .type name, @function + #define SIZE(start,end) .size start, .-end + #define SYMBOL(name) name +#endif + +.macro PUSH _reg + pushq \_reg + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset \_reg, 0 +.endm + +.macro POP _reg + popq \_reg + .cfi_adjust_cfa_offset -8 + .cfi_restore \_reg +.endm + +/* Frame size must be 16-byte aligned. + * Remember about 8 bytes for return address + 6 * 8 for spills. + */ +#define FRAME_SIZE 8 + +/* Frame diagram while executing ExecuteMterpImpl, high to low addresses */ +#define IN_ARG3 %rcx +#define IN_ARG2 %rdx +#define IN_ARG1 %rsi +#define IN_ARG0 %rdi +/* Out Args */ +#define OUT_ARG3 %rcx +#define OUT_ARG2 %rdx +#define OUT_ARG1 %rsi +#define OUT_ARG0 %rdi +#define OUT_32_ARG3 %ecx +#define OUT_32_ARG2 %edx +#define OUT_32_ARG1 %esi +#define OUT_32_ARG0 %edi +#define OUT_FP_ARG1 %xmm1 +#define OUT_FP_ARG0 %xmm0 + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF %rbp +#define rPC %r12 +#define rFP %r13 +#define rINST %ebx +#define rINSTq %rbx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %r14 +#define rREFS %r15 + +/* + * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, + * to access other shadow frame fields, we need to use a backwards offset. Define those here. + */ +#define OFF_FP(a) (a - SHADOWFRAME_VREGS_OFFSET) +#define OFF_FP_NUMBER_OF_VREGS OFF_FP(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET) +#define OFF_FP_DEX_PC OFF_FP(SHADOWFRAME_DEX_PC_OFFSET) +#define OFF_FP_LINK OFF_FP(SHADOWFRAME_LINK_OFFSET) +#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET) +#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET) +#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET) +#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 + +/* + * Profile branch. rINST should contain the offset. %eax is scratch. + */ +.macro MTERP_PROFILE_BRANCH +#ifdef MTERP_PROFILE_BRANCHES + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpProfileBranch) + testb %al, %al + jnz MterpOnStackReplacement +#endif +.endm + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * It's okay to do this more than once. + * + * NOTE: the fast interpreter keeps track of dex pc as a direct pointer to the mapped + * dex byte codes. However, the rest of the runtime expects dex pc to be an instruction + * offset into the code_items_[] array. For effiency, we will "export" the + * current dex pc as a direct pointer using the EXPORT_PC macro, and rely on GetDexPC + * to convert to a dex pc when needed. + */ +.macro EXPORT_PC + movq rPC, OFF_FP_DEX_PC_PTR(rFP) +.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. + * + */ +.macro REFRESH_IBASE + movq THREAD_CURRENT_IBASE_OFFSET(rSELF), rIBASE +.endm + +/* + * Refresh rINST. + * At enter to handler rINST does not contain the opcode number. + * However some utilities require the full value, so this macro + * restores the opcode number. + */ +.macro REFRESH_INST _opnum + movb rINSTbl, rINSTbh + movb $$\_opnum, rINSTbl +.endm + +/* + * Fetch the next instruction from rPC into rINSTw. Does not advance rPC. + */ +.macro FETCH_INST + movzwq (rPC), rINSTq +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll MACRO_LITERAL(${handler_size_bits}), %eax + addq rIBASE, %rax + jmp *%rax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leaq 2*\_count(rPC), rPC +.endm + +/* + * Advance rPC by instruction count, fetch instruction and jump to handler. + */ +.macro ADVANCE_PC_FETCH_AND_GOTO_NEXT _count + ADVANCE_PC \_count + FETCH_INST + GOTO_NEXT +.endm + +/* + * Get/set the 32-bit value from a Dalvik register. + */ +#define VREG_ADDRESS(_vreg) (rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value. */ +.macro GET_WIDE_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +/* Write wide value. reg is clobbered. */ +.macro SET_WIDE_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + xorq \_reg, \_reg + movq \_reg, (rREFS,\_vreg,4) +.endm + +.macro SET_VREG_OBJECT _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl \_reg, (rREFS,\_vreg,4) +.endm + +.macro GET_VREG_HIGH _reg _vreg + movl 4(rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG_HIGH _reg _vreg + movl \_reg, 4(rFP,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl MACRO_LITERAL(0), (rREFS,\_vreg,4) + movl MACRO_LITERAL(0), 4(rREFS,\_vreg,4) +.endm diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S new file mode 100644 index 0000000000000000000000000000000000000000..f7e6155c161db943fe415f12ab937aa05a88a7ac --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/invoke.S @@ -0,0 +1,22 @@ +%default { "helper":"UndefinedInvokeHandler" } +/* + * Generic invoke handler wrapper. + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ + .extern $helper + EXPORT_PC + movq rSELF, OUT_ARG0 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG1 + movq rPC, OUT_ARG2 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG3 + call SYMBOL($helper) + testb %al, %al + jz MterpException + ADVANCE_PC 3 + call SYMBOL(MterpShouldSwitchInterpreters) + testb %al, %al + jnz MterpFallback + FETCH_INST + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_add_double.S b/runtime/interpreter/mterp/x86_64/op_add_double.S new file mode 100644 index 0000000000000000000000000000000000000000..cb462cb816224e5f19a6471fd222ab7a866f690e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..063bde3fb3b5f88f06e35e36745e6046e7cbdb15 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float.S b/runtime/interpreter/mterp/x86_64/op_add_float.S new file mode 100644 index 0000000000000000000000000000000000000000..7753bf88702c879997680f3be5f3abc60929ca06 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..6c8005b1822c8b406efc37202c38397d412ceae2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int.S b/runtime/interpreter/mterp/x86_64/op_add_int.S new file mode 100644 index 0000000000000000000000000000000000000000..e316be7b9dcb32f7d7a26cfdeaad3b13fa5c8515 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"addl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..2ff82935ae87efc82f476bbbd167f740ec6850fd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"addl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..bfeb7caabcb03a0f6387f93985f8512448046a6e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..8954844eae0f0f2e449f07de13184447f59b0968 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long.S b/runtime/interpreter/mterp/x86_64/op_add_long.S new file mode 100644 index 0000000000000000000000000000000000000000..89131ffe0e8e3f73a9314d6ea9b3988515e7d49e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"addq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..fed98bc3e688f5bcc7048adf63a61e83b7b20fe3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_add_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"addq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aget.S b/runtime/interpreter/mterp/x86_64/op_aget.S new file mode 100644 index 0000000000000000000000000000000000000000..58d49481cfdeaa164d5dbd19268a7a66926f78f5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget.S @@ -0,0 +1,24 @@ +%default { "load":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short, aget-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # eax <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if $wide + movq $data_offset(%rax,%rcx,8), %rax + SET_WIDE_VREG %rax, rINSTq + .else + $load $data_offset(%rax,%rcx,$shift), %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_boolean.S b/runtime/interpreter/mterp/x86_64/op_aget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..cf7bdb582b21c73612d343cbd06d6d3853904278 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movzbl", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_byte.S b/runtime/interpreter/mterp/x86_64/op_aget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..1cbb56902498628c451de56f4312c593f37c34f4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movsbl", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_char.S b/runtime/interpreter/mterp/x86_64/op_aget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..45c90851fd3308adcfe7a4cf6f48374df60ac31a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movzwl", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_object.S b/runtime/interpreter/mterp/x86_64/op_aget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..8baedeab5e7c0f77dc44e622e27e1693e2806e1d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_object.S @@ -0,0 +1,16 @@ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG OUT_32_ARG0, %rax # eax <- vBB (array object) + GET_VREG OUT_32_ARG1, %rcx # ecx <- vCC (requested index) + EXPORT_PC + call SYMBOL(artAGetObjectFromMterp) # (array, index) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + SET_VREG_OBJECT %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aget_short.S b/runtime/interpreter/mterp/x86_64/op_aget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..82c4a1ddf34498fbb9b748831a2a7ad80e7483e0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movswl", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aget_wide.S b/runtime/interpreter/mterp/x86_64/op_aget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..4f2771b9c403c24d158712f6e8de4b640694c670 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_aget.S" { "load":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_and_int.S b/runtime/interpreter/mterp/x86_64/op_and_int.S new file mode 100644 index 0000000000000000000000000000000000000000..446988993df6db0f10eb63a130ebb08767e7d497 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"andl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..16315bba0353820ae51fcd87e75303ddcd18e36a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"andl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..63e851b4497da49bad125313747773e4b52027fa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..da7a20fdff904fb3f472455b660dbb324c6d63ba --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long.S b/runtime/interpreter/mterp/x86_64/op_and_long.S new file mode 100644 index 0000000000000000000000000000000000000000..ce1dd264dd55d151b709a6132ac04590a10595c7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"andq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..d17ab8d58bd19921acb98ac127cbd570abcb5bad --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"andq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_aput.S b/runtime/interpreter/mterp/x86_64/op_aput.S new file mode 100644 index 0000000000000000000000000000000000000000..11500ad201dd5166046bd7165174901b73d66085 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput.S @@ -0,0 +1,23 @@ +%default { "reg":"rINST", "store":"movl", "shift":"4", "data_offset":"MIRROR_INT_ARRAY_DATA_OFFSET", "wide":"0" } +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short, aput-wide + * + */ + /* op vAA, vBB, vCC */ + movzbq 2(rPC), %rax # rax <- BB + movzbq 3(rPC), %rcx # rcx <- CC + GET_VREG %eax, %rax # eax <- vBB (array object) + GET_VREG %ecx, %rcx # ecx <- vCC (requested index) + testl %eax, %eax # null array object? + je common_errNullObject # bail if so + cmpl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ecx + jae common_errArrayIndex # index >= length, bail. + .if $wide + GET_WIDE_VREG rINSTq, rINSTq + .else + GET_VREG rINST, rINSTq + .endif + $store $reg, $data_offset(%rax,%rcx,$shift) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_boolean.S b/runtime/interpreter/mterp/x86_64/op_aput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..7d77a865288fe99f9788fb83d45f147a4ba81896 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_byte.S b/runtime/interpreter/mterp/x86_64/op_aput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..7a1723e0fe24a6879ea6a71690eaef0c9ac0d91f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_char.S b/runtime/interpreter/mterp/x86_64/op_aput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..f8f50a3b2e61837196298c2496d3d0305d1335f1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_object.S b/runtime/interpreter/mterp/x86_64/op_aput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..b1bae0f45758b5740a12035fe6b260707b73a478 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_object.S @@ -0,0 +1,13 @@ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpAputObject) # (array, index) + testb %al, %al + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_aput_short.S b/runtime/interpreter/mterp/x86_64/op_aput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..481fd6847b7dfe0027f075eb81d2bbb49a6fcc28 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86_64/op_aput_wide.S b/runtime/interpreter/mterp/x86_64/op_aput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..5bbd39b0b6426c15466d1c18f40cd7baf85d9078 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_aput_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_aput.S" { "reg":"rINSTq", "store":"movq", "shift":"8", "data_offset":"MIRROR_WIDE_ARRAY_DATA_OFFSET", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_array_length.S b/runtime/interpreter/mterp/x86_64/op_array_length.S new file mode 100644 index 0000000000000000000000000000000000000000..e80d665160ec20cc0b5c88da3f97eb308ecbb1d9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_array_length.S @@ -0,0 +1,12 @@ +/* + * Return the length of an array. + */ + movl rINST, %eax # eax <- BA + sarl $$4, rINST # rINST <- B + GET_VREG %ecx, rINSTq # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $$0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%rcx), rINST + SET_VREG rINST, %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_check_cast.S b/runtime/interpreter/mterp/x86_64/op_check_cast.S new file mode 100644 index 0000000000000000000000000000000000000000..f8fa7b2036e3527b410ef2792b79d7834d4a9041 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_check_cast.S @@ -0,0 +1,13 @@ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + leaq VREG_ADDRESS(rINSTq), OUT_ARG1 + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpCheckCast) # (index, &obj, method, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmp_long.S b/runtime/interpreter/mterp/x86_64/op_cmp_long.S new file mode 100644 index 0000000000000000000000000000000000000000..23ca3e5e6d5f09d30ac6c635fc91b021fee10f00 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmp_long.S @@ -0,0 +1,17 @@ +/* + * Compare two 64-bit values. Puts 0, 1, or -1 into the destination + * register based on the results of the comparison. + */ + /* cmp-long vAA, vBB, vCC */ + movzbq 2(rPC), %rdx # edx <- BB + movzbq 3(rPC), %rcx # ecx <- CC + GET_WIDE_VREG %rdx, %rdx # rdx <- v[BB] + xorl %eax, %eax + xorl %edi, %edi + addb $$1, %al + movl $$-1, %esi + cmpq VREG_ADDRESS(%rcx), %rdx + cmovl %esi, %edi + cmovg %eax, %edi + SET_VREG %edi, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_double.S b/runtime/interpreter/mterp/x86_64/op_cmpg_double.S new file mode 100644 index 0000000000000000000000000000000000000000..7c0aa1bdbab7f247e4cdce74788677d924514801 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpg_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"d","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpg_float.S b/runtime/interpreter/mterp/x86_64/op_cmpg_float.S new file mode 100644 index 0000000000000000000000000000000000000000..14e8472672a8f8f5f61ce70347b47f0d2642b6c6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpg_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"s","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_double.S b/runtime/interpreter/mterp/x86_64/op_cmpl_double.S new file mode 100644 index 0000000000000000000000000000000000000000..1d4c4243ae16cc9489152a487f7b5349ba2a735c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpl_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"d","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_cmpl_float.S b/runtime/interpreter/mterp/x86_64/op_cmpl_float.S new file mode 100644 index 0000000000000000000000000000000000000000..97a12a6a7dd5cc80772f5487c0f4e35ee3711a93 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_cmpl_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcmp.S" {"suff":"s","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86_64/op_const.S b/runtime/interpreter/mterp/x86_64/op_const.S new file mode 100644 index 0000000000000000000000000000000000000000..3cfafdb13bbd44445ffde9e31038aa99db292c47 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const.S @@ -0,0 +1,4 @@ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax, rINSTq # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_16.S b/runtime/interpreter/mterp/x86_64/op_const_16.S new file mode 100644 index 0000000000000000000000000000000000000000..1a139c683e2ff2d03bb90dde630797e608fb31ed --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_16.S @@ -0,0 +1,4 @@ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx, rINSTq # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_4.S b/runtime/interpreter/mterp/x86_64/op_const_4.S new file mode 100644 index 0000000000000000000000000000000000000000..23c4816f8288071cbec7f3f55e60664d015a28c0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_4.S @@ -0,0 +1,7 @@ + /* const/4 vA, #+B */ + movsbl rINSTbl, %eax # eax <-ssssssBx + movl $$0xf, rINST + andl %eax, rINST # rINST <- A + sarl $$4, %eax + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_const_class.S b/runtime/interpreter/mterp/x86_64/op_const_class.S new file mode 100644 index 0000000000000000000000000000000000000000..494920a4a8fe651c1794ebeafde262466437aea2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_class.S @@ -0,0 +1,10 @@ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # eax <- OUT_ARG0 + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_high16.S b/runtime/interpreter/mterp/x86_64/op_const_high16.S new file mode 100644 index 0000000000000000000000000000000000000000..64e633c7a0d3799206aaca32868f3038c9c8296c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_high16.S @@ -0,0 +1,5 @@ + /* const/high16 vAA, #+BBBB0000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $$16, %eax # eax <- BBBB0000 + SET_VREG %eax, rINSTq # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string.S b/runtime/interpreter/mterp/x86_64/op_const_string.S new file mode 100644 index 0000000000000000000000000000000000000000..7c199ecad9845e8d1108dac7f7c07abf110c768b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_string.S @@ -0,0 +1,10 @@ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # OUT_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S new file mode 100644 index 0000000000000000000000000000000000000000..ae03d20f4f17cba3de06bfeda0cee2f8935975de --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_string_jumbo.S @@ -0,0 +1,10 @@ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- BBBB + movq rINSTq, OUT_ARG1 + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide.S b/runtime/interpreter/mterp/x86_64/op_const_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..56151771752cd13efdef2090825c3d6e8c9e7308 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide.S @@ -0,0 +1,4 @@ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movq 2(rPC), %rax # rax <- HHHHhhhhBBBBbbbb + SET_WIDE_VREG %rax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_16.S new file mode 100644 index 0000000000000000000000000000000000000000..593b62466f677a55f65a62138a103deb1019db6d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_16.S @@ -0,0 +1,4 @@ + /* const-wide/16 vAA, #+BBBB */ + movswq 2(rPC), %rax # rax <- ssssBBBB + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_32.S b/runtime/interpreter/mterp/x86_64/op_const_wide_32.S new file mode 100644 index 0000000000000000000000000000000000000000..5ef363612973230c8d5477968a2e643002bc1a77 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_32.S @@ -0,0 +1,4 @@ + /* const-wide/32 vAA, #+BBBBbbbb */ + movslq 2(rPC), %rax # eax <- ssssssssBBBBbbbb + SET_WIDE_VREG %rax, rINSTq # store + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S b/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S new file mode 100644 index 0000000000000000000000000000000000000000..b86b4e582bb7733c659fd98368fdf946970adabd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_const_wide_high16.S @@ -0,0 +1,5 @@ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwq 2(rPC), %rax # eax <- 0000BBBB + salq $$48, %rax # eax <- BBBB0000 + SET_WIDE_VREG %rax, rINSTq # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_div_double.S b/runtime/interpreter/mterp/x86_64/op_div_double.S new file mode 100644 index 0000000000000000000000000000000000000000..45c700c0667791a5736cec47546439b9d8a9ce6c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..83f270e245b2ddc3317db25d2247cdac080ed456 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float.S b/runtime/interpreter/mterp/x86_64/op_div_float.S new file mode 100644 index 0000000000000000000000000000000000000000..aa90b24698859e915198657a39f506481a2d8e49 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..f0f8f1a6c8636de59341f5293c2d1ba5bf279c88 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int.S b/runtime/interpreter/mterp/x86_64/op_div_int.S new file mode 100644 index 0000000000000000000000000000000000000000..bba5a176a0f9cb039904ad5313747f38854f00eb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..fa4255ddfa8c13a39bb81b95006ea431db20490b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%eax","second":"%ecx","wide":"0","suffix":"l"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..3fa1e09fd696ee2bb7214edb012b1bc4534aca68 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit16.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..859883e5c74e7133cf58e68a87f73a6b0d09b4ae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit8.S" {"result":"%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long.S b/runtime/interpreter/mterp/x86_64/op_div_long.S new file mode 100644 index 0000000000000000000000000000000000000000..a061a88b135a0b06654041eb64ea18d86c451f71 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_long.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..8886e6824878f445251a054a72ac6735f8a96d5b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_div_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%rax","second":"%rcx","wide":"1","suffix":"q","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_float.S b/runtime/interpreter/mterp/x86_64/op_double_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..cea1482038a021322d3b74ff281049b46aaab5b8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"d","dest_suffix":"s","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_int.S b/runtime/interpreter/mterp/x86_64/op_double_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..a9965edcc3fe49784f26675fdba6a3fa7676a005 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_int.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_double_to_long.S b/runtime/interpreter/mterp/x86_64/op_double_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..179e6a16053779fcbaf69a7e93b53d5b3ed62a10 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_double_to_long.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"d","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_fill_array_data.S b/runtime/interpreter/mterp/x86_64/op_fill_array_data.S new file mode 100644 index 0000000000000000000000000000000000000000..626bad47c9847e1cc47ee810ebcd3d189bae0f54 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_fill_array_data.S @@ -0,0 +1,9 @@ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leaq (rPC,%rcx,2), OUT_ARG1 # OUT_ARG1 <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG0, rINSTq # OUT_ARG0 <- vAA (array object) + call SYMBOL(MterpFillArrayData) # (obj, payload) + 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_64/op_filled_new_array.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array.S new file mode 100644 index 0000000000000000000000000000000000000000..a7f7ddc2a0da16d320acf1fd95d2d7626442fe63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_filled_new_array.S @@ -0,0 +1,17 @@ +%default { "helper":"MterpFilledNewArray" } +/* + * Create a new array with elements filled from registers. + * + * for: filled-new-array, filled-new-array/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, type@BBBB */ + .extern $helper + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + movq rSELF, OUT_ARG2 + call SYMBOL($helper) + 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_64/op_filled_new_array_range.S b/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S new file mode 100644 index 0000000000000000000000000000000000000000..4ca79a3fe16c4b370f66553422f0665d30630def --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "x86_64/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_double.S b/runtime/interpreter/mterp/x86_64/op_float_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..785520557503fdd89b7bdfc32cd776f00dbbafa1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"s","dest_suffix":"d","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_int.S b/runtime/interpreter/mterp/x86_64/op_float_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..cb90555405c59354555deee41996573bbf738fa2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_int.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"l","max_const":"$0x7fffffff","result_reg":"%eax","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_float_to_long.S b/runtime/interpreter/mterp/x86_64/op_float_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..96bb4eee6f55c257c707b39f75f165d11bb9c861 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_float_to_long.S @@ -0,0 +1 @@ +%include "x86_64/cvtfp_int.S" {"fp_suffix":"s","i_suffix":"q","max_const":"$0x7fffffffffffffff","result_reg":"%rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_goto.S b/runtime/interpreter/mterp/x86_64/op_goto.S new file mode 100644 index 0000000000000000000000000000000000000000..c4fc97644fce2b321eb6d196e216c4dcf4d288ed --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto.S @@ -0,0 +1,14 @@ +/* + * Unconditional branch, 8-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto +AA */ + movsbq rINSTbl, rINSTq # rINSTq <- ssssssAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_16.S b/runtime/interpreter/mterp/x86_64/op_goto_16.S new file mode 100644 index 0000000000000000000000000000000000000000..8cb9a5c50f416dd14943adb834246fab2c2567c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto_16.S @@ -0,0 +1,14 @@ +/* + * Unconditional branch, 16-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + */ + /* goto/16 +AAAA */ + movswq 2(rPC), rINSTq # rINSTq <- ssssAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_goto_32.S b/runtime/interpreter/mterp/x86_64/op_goto_32.S new file mode 100644 index 0000000000000000000000000000000000000000..4ecdacd3e6db4419ff2b83956864fe4d41c1d89e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_goto_32.S @@ -0,0 +1,17 @@ +/* + * Unconditional branch, 32-bit offset. + * + * The branch distance is a signed code-unit offset, which we need to + * double to get a byte offset. + * + * Because we need the SF bit set, we'll use an adds + * to convert from Dalvik offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movslq 2(rPC), rINSTq # rINSTq <- AAAAAAAA + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_if_eq.S b/runtime/interpreter/mterp/x86_64/op_if_eq.S new file mode 100644 index 0000000000000000000000000000000000000000..d56ce72461c76894a9d8e9eba5f8c4f3015ae569 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_eq.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_eqz.S b/runtime/interpreter/mterp/x86_64/op_if_eqz.S new file mode 100644 index 0000000000000000000000000000000000000000..a0fc4448a3980ebf8497955ed9e7981f123ddebe --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_eqz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ge.S b/runtime/interpreter/mterp/x86_64/op_if_ge.S new file mode 100644 index 0000000000000000000000000000000000000000..a7832efb68368126366149d82f27fc769158bbf3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ge.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gez.S b/runtime/interpreter/mterp/x86_64/op_if_gez.S new file mode 100644 index 0000000000000000000000000000000000000000..f9af5db933dbf65b4f6fc14971e09d080c6fcc19 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gt.S b/runtime/interpreter/mterp/x86_64/op_if_gt.S new file mode 100644 index 0000000000000000000000000000000000000000..70f2b9e12f29ec8c64302ba5318da02333c66a90 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gt.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_gtz.S b/runtime/interpreter/mterp/x86_64/op_if_gtz.S new file mode 100644 index 0000000000000000000000000000000000000000..2fb0d509377a06d45d5b8a3522e98e073938de17 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_gtz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_le.S b/runtime/interpreter/mterp/x86_64/op_if_le.S new file mode 100644 index 0000000000000000000000000000000000000000..321962a040367738c278b2fbd6f0929b4feeaa67 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_le.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lez.S b/runtime/interpreter/mterp/x86_64/op_if_lez.S new file mode 100644 index 0000000000000000000000000000000000000000..d3dc334f7bcad1be0a42b630f904dd0bed16b987 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_lez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_lt.S b/runtime/interpreter/mterp/x86_64/op_if_lt.S new file mode 100644 index 0000000000000000000000000000000000000000..f028005844911e31c39d413a33736f17953f4895 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_lt.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ltz.S b/runtime/interpreter/mterp/x86_64/op_if_ltz.S new file mode 100644 index 0000000000000000000000000000000000000000..383d73aa7a70ba35c9dac15644724fc3b92541a6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ltz.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_ne.S b/runtime/interpreter/mterp/x86_64/op_if_ne.S new file mode 100644 index 0000000000000000000000000000000000000000..ac6e063cd100a41f5cb834f1ecb98fa97cabe5c7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_ne.S @@ -0,0 +1 @@ +%include "x86_64/bincmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_if_nez.S b/runtime/interpreter/mterp/x86_64/op_if_nez.S new file mode 100644 index 0000000000000000000000000000000000000000..c96e4f3d16b878ffb472b750fc8a2032a31dae78 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_if_nez.S @@ -0,0 +1 @@ +%include "x86_64/zcmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget.S b/runtime/interpreter/mterp/x86_64/op_iget.S new file mode 100644 index 0000000000000000000000000000000000000000..a0d0fafba11fb1d31600c613405628d0ac00dacd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget.S @@ -0,0 +1,27 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode", "wide":"0"} +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short, iget-wide + */ + EXPORT_PC + movzbq rINSTbl, %rcx # rcx <- BA + movzwl 2(rPC), OUT_32_ARG0 # eax <- field ref CCCC + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 + call SYMBOL($helper) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <-value + .else + .if $wide + SET_WIDE_VREG %rax, rINSTq # fp[A] <-value + .else + SET_VREG %eax, rINSTq # fp[A] <-value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..6ac55231b8a5c3a0c9ac020315cd1b003e9c4d49 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..07139c75ee394338f4680c97860af46cb24af45c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte.S b/runtime/interpreter/mterp/x86_64/op_iget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..6a861b1b7a30a84cac2cbde5ca96370e8d0f5245 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..07139c75ee394338f4680c97860af46cb24af45c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char.S b/runtime/interpreter/mterp/x86_64/op_iget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..021a0f1b24e4b49940b0997191882849e24e5074 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..8cb3be3b65288583d6b506c6a91c68bc94c4cbc7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movzwl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object.S b/runtime/interpreter/mterp/x86_64/op_iget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..d92bc9c3458c07443986e2b0073919f9b97cdb43 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_object.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..964d20ad74b72178dc7f27dd7f43124d8c65171b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_object_quick.S @@ -0,0 +1,14 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + .extern artIGetObjectFromMterp + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG0, %rcx # vB (object we're operating on) + movzwl 2(rPC), OUT_32_ARG1 # eax <- field byte offset + EXPORT_PC + callq SYMBOL(artIGetObjectFromMterp) # (obj, offset) + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..bfb7530167678594cb2defbe551aa2d5cbf8a0d2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_quick.S @@ -0,0 +1,18 @@ +%default { "load":"movl", "wide":"0"} + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick, iget-wide-quick */ + /* op vA, vB, offset@CCCC */ + movl rINST, %ecx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + movzwq 2(rPC), %rax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $$0xf,rINSTbl # rINST <- A + .if $wide + movq (%rcx,%rax,1), %rax + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + ${load} (%rcx,%rax,1), %eax + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short.S b/runtime/interpreter/mterp/x86_64/op_iget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..f158bea573401c7d9ca91d212bd198fbc51818d9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..56ca858e74db9c028e724047f83db8469dba57a9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movswl" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide.S b/runtime/interpreter/mterp/x86_64/op_iget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..74bb9ffe1c3a3d5423c1d425cf765b4618137004 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_iget.S" { "helper":"artGet64InstanceFromCode", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..169d625529c7437ea65ac3b927ce81b6c82949d5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iget_wide_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iget_quick.S" { "load":"movswl", "wide":"1" } diff --git a/runtime/interpreter/mterp/x86_64/op_instance_of.S b/runtime/interpreter/mterp/x86_64/op_instance_of.S new file mode 100644 index 0000000000000000000000000000000000000000..6be37f916634ed001ff72bed3bc3d216b83cc9c0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_instance_of.S @@ -0,0 +1,21 @@ +/* + * Check to see if an object reference is an instance of a class. + * + * Most common situation is a non-null object, being compared against + * an already-resolved class. + */ + /* instance-of vA, vB, class@CCCC */ + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # OUT_32_ARG0 <- CCCC + movl rINST, %eax # eax <- BA + sarl $$4, %eax # eax <- B + leaq VREG_ADDRESS(%rax), OUT_ARG1 # Get object address + movq OFF_FP_METHOD(rFP), OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpInstanceOf) # (index, &obj, method, self) + movsbl %al, %eax + cmpq $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + andb $$0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax, rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_byte.S b/runtime/interpreter/mterp/x86_64/op_int_to_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..f4e578f868e1046ce40121d461206958e849b3cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_byte.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movsbl %al, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_char.S b/runtime/interpreter/mterp/x86_64/op_int_to_char.S new file mode 100644 index 0000000000000000000000000000000000000000..c1bf17f271e4685b8075dbea04acc39fdba66770 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_char.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movzwl %ax,%eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_double.S b/runtime/interpreter/mterp/x86_64/op_int_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..27ebf42dbb5920ae7f444f830967342aa546a495 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_float.S b/runtime/interpreter/mterp/x86_64/op_int_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..5a98d44337e9498e31b25f67b09ff1669b2b1c80 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sl","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_long.S b/runtime/interpreter/mterp/x86_64/op_int_to_long.S new file mode 100644 index 0000000000000000000000000000000000000000..9281137a546acac0e62f57b0027b26e55c0d6ed7 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_long.S @@ -0,0 +1,8 @@ + /* int to long vA, vB */ + movzbq rINSTbl, %rax # rax <- +A + sarl $$4, %eax # eax <- B + andb $$0xf, rINSTbl # rINST <- A + movslq VREG_ADDRESS(%rax), %rax + SET_WIDE_VREG %rax, rINSTq # v[A] <- %rax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + diff --git a/runtime/interpreter/mterp/x86_64/op_int_to_short.S b/runtime/interpreter/mterp/x86_64/op_int_to_short.S new file mode 100644 index 0000000000000000000000000000000000000000..6ae6b50f3460dbd7fc1f08d2325c1074686d4fd2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_int_to_short.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":"movswl %ax, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct.S new file mode 100644 index 0000000000000000000000000000000000000000..9628589b03b777fb321be09e41ac22480ae88522 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_direct.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S new file mode 100644 index 0000000000000000000000000000000000000000..09ac8812fcda8f13f3ea20c9e285f54760911418 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface.S new file mode 100644 index 0000000000000000000000000000000000000000..76d9cd426f617b84fd3262ea7bad5c5bf35e061b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_interface.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeInterface" } +/* + * Handle an interface method call. + * + * for: invoke-interface, invoke-interface/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S new file mode 100644 index 0000000000000000000000000000000000000000..785b43c1a881c21eb7af1b9b8e550339f0de5c77 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static.S b/runtime/interpreter/mterp/x86_64/op_invoke_static.S new file mode 100644 index 0000000000000000000000000000000000000000..dd8027d58c5501a594a0c6f429660e0661717585 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_static.S @@ -0,0 +1,2 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeStatic" } + diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S new file mode 100644 index 0000000000000000000000000000000000000000..ee26074f921801d705d60b59c3519f5f7bb6e1cd --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super.S b/runtime/interpreter/mterp/x86_64/op_invoke_super.S new file mode 100644 index 0000000000000000000000000000000000000000..d07f8d555b0fbd34445a219036b60c573e473300 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_super.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeSuper" } +/* + * Handle a "super" method call. + * + * for: invoke-super, invoke-super/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S new file mode 100644 index 0000000000000000000000000000000000000000..7245cfd405c833d74dfe71a3e4c4ac7fdbbcc861 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S new file mode 100644 index 0000000000000000000000000000000000000000..19c708bd2aa7f460c4f0ec9cafeff8f019bee36e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual.S @@ -0,0 +1,8 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtual" } +/* + * Handle a virtual method call. + * + * for: invoke-virtual, invoke-virtual/range + */ + /* op vB, {vD, vE, vF, vG, vA}, class@CCCC */ + /* op vAA, {vCCCC..v(CCCC+AA-1)}, meth@BBBB */ diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..313bd058b1ec69f8dfe818df786e83b105ad8e94 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S new file mode 100644 index 0000000000000000000000000000000000000000..424ad321a3524aaaf1259e6dddcad8f9600df8ce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..556f718ffbf682b84aa379fb0fe5103268ef84d1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "x86_64/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput.S b/runtime/interpreter/mterp/x86_64/op_iput.S new file mode 100644 index 0000000000000000000000000000000000000000..6b7cb1cc84bb62a0b1c4dea3dbb99f106f13f1f1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput.S @@ -0,0 +1,20 @@ +%default { "handler":"artSet32InstanceFromMterp"} +/* + * General 32-bit instance field put. + * + * for: iput, iput-object, iput-boolean, iput-byte, iput-char, iput-short + */ + /* op vA, vB, field@CCCC */ + .extern $handler + EXPORT_PC + movzwl 2(rPC), OUT_32_ARG0 # field ref <- 0000CCCC + movzbq rINSTbl, %rcx # rcx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $$0xf, rINSTbl # rINST<- A + GET_VREG OUT_32_ARG2, rINSTq # fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL($handler) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..cb4b1cde4586c12af465339e7d17c9e568b856fb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..6bd060e4f3808dd94515fdf114a66dbfdf6f15ed --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte.S b/runtime/interpreter/mterp/x86_64/op_iput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..cb4b1cde4586c12af465339e7d17c9e568b856fb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..6bd060e4f3808dd94515fdf114a66dbfdf6f15ed --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char.S b/runtime/interpreter/mterp/x86_64/op_iput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..b4e147cf5e725de78f2895c16f7c60874b5dad5a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..3da96d53afcec7a3948365c9b016ee491912cff2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object.S b/runtime/interpreter/mterp/x86_64/op_iput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..828712d8ba26777c45240713ae699ebb78fdb1f3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_object.S @@ -0,0 +1,10 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpIputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..b5b128ab7fdc83fee0df8ad34805dfeeee0ec1a1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_object_quick.S @@ -0,0 +1,9 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movl rINST, OUT_32_ARG2 + call SYMBOL(MterpIputObjectQuick) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..ecaf98e415c69d572b9275cea41a3583186b789f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_quick.S @@ -0,0 +1,13 @@ +%default { "reg":"rINST", "store":"movl" } + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $$0xf, rINSTbl # rINST <- A + GET_VREG rINST, rINSTq # rINST <- v[A] + movzwq 2(rPC), %rax # rax <- field byte offset + ${store} ${reg}, (%rcx,%rax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short.S b/runtime/interpreter/mterp/x86_64/op_iput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..b4e147cf5e725de78f2895c16f7c60874b5dad5a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..3da96d53afcec7a3948365c9b016ee491912cff2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "x86_64/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide.S b/runtime/interpreter/mterp/x86_64/op_iput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..e59717b8467a081cd653f7b2640500d85adab76a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_wide.S @@ -0,0 +1,14 @@ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movzbq rINSTbl, %rcx # rcx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG OUT_32_ARG1, %rcx # the object pointer + andb $$0xf, rINSTbl # rINST <- A + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[A] + movq OFF_FP_METHOD(rFP), OUT_ARG3 # referrer + call SYMBOL(artSet64InstanceFromMterp) + testb %al, %al + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S b/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S new file mode 100644 index 0000000000000000000000000000000000000000..473189d007122db21736588cc45c1f0d16e05ac1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_iput_wide_quick.S @@ -0,0 +1,12 @@ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbq rINSTbl, %rcx # rcx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG %ecx, %rcx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwq 2(rPC), %rax # rax<- field byte offset + leaq (%rcx,%rax,1), %rcx # ecx<- Address of 64-bit target + andb $$0xf, rINSTbl # rINST<- A + GET_WIDE_VREG %rax, rINSTq # rax<- fp[A]/fp[A+1] + movq %rax, (%rcx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_double.S b/runtime/interpreter/mterp/x86_64/op_long_to_double.S new file mode 100644 index 0000000000000000000000000000000000000000..7cdae32373109d76c50933ecfdbe44c3f215cfb6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_double.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"dq","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_float.S b/runtime/interpreter/mterp/x86_64/op_long_to_float.S new file mode 100644 index 0000000000000000000000000000000000000000..7553348633db34683e0d4d418155d69c44cd98e4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_float.S @@ -0,0 +1 @@ +%include "x86_64/fpcvt.S" {"source_suffix":"i","dest_suffix":"sq","wide":"0"} diff --git a/runtime/interpreter/mterp/x86_64/op_long_to_int.S b/runtime/interpreter/mterp/x86_64/op_long_to_int.S new file mode 100644 index 0000000000000000000000000000000000000000..7b50c8e0b357dafee5aab52f1c13a1c8255fb7e9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_long_to_int.S @@ -0,0 +1,2 @@ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +%include "x86_64/op_move.S" diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_enter.S b/runtime/interpreter/mterp/x86_64/op_monitor_enter.S new file mode 100644 index 0000000000000000000000000000000000000000..411091f23e044e12c98bd4ed01a312abed95f40e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_monitor_enter.S @@ -0,0 +1,11 @@ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artLockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_monitor_exit.S b/runtime/interpreter/mterp/x86_64/op_monitor_exit.S new file mode 100644 index 0000000000000000000000000000000000000000..72d9a23a87978fd18153f6420718cdf7a591c4a5 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_monitor_exit.S @@ -0,0 +1,15 @@ +/* + * Unlock an object. + * + * Exceptions that occur when unlocking a monitor need to appear as + * if they happened at the following instruction. See the Dalvik + * instruction spec. + */ + /* monitor-exit vAA */ + EXPORT_PC + GET_VREG OUT_32_ARG0, rINSTq + movq rSELF, OUT_ARG1 + call SYMBOL(artUnlockObjectFromCode) # (object, self) + testq %rax, %rax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move.S b/runtime/interpreter/mterp/x86_64/op_move.S new file mode 100644 index 0000000000000000000000000000000000000000..ccaac2caa89db061daf6a30272c393745e0da058 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move.S @@ -0,0 +1,13 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movl rINST, %eax # eax <- BA + andb $$0xf, %al # eax <- A + shrl $$4, rINST # rINST <- B + GET_VREG %edx, rINSTq + .if $is_object + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_16.S b/runtime/interpreter/mterp/x86_64/op_move_16.S new file mode 100644 index 0000000000000000000000000000000000000000..6a813eb5ce2d8592b7283785537aaa5d365adbfb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_16.S @@ -0,0 +1,12 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwq 4(rPC), %rcx # ecx <- BBBB + movzwq 2(rPC), %rax # eax <- AAAA + GET_VREG %edx, %rcx + .if $is_object + SET_VREG_OBJECT %edx, %rax # fp[A] <- fp[B] + .else + SET_VREG %edx, %rax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_exception.S b/runtime/interpreter/mterp/x86_64/op_move_exception.S new file mode 100644 index 0000000000000000000000000000000000000000..d0a14fdc8d01b94c1f8d8cf71f5eadb3799080fb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_exception.S @@ -0,0 +1,5 @@ + /* move-exception vAA */ + movl THREAD_EXCEPTION_OFFSET(rSELF), %eax + SET_VREG_OBJECT %eax, rINSTq # fp[AA] <- exception object + movl $$0, THREAD_EXCEPTION_OFFSET(rSELF) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_from16.S b/runtime/interpreter/mterp/x86_64/op_move_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..150e9c2f2c8ef0218f2aaab67ce2249eac9bc9a8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_from16.S @@ -0,0 +1,11 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzwq 2(rPC), %rax # eax <- BBBB + GET_VREG %edx, %rax # edx <- fp[BBBB] + .if $is_object + SET_VREG_OBJECT %edx, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %edx, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_move_object.S b/runtime/interpreter/mterp/x86_64/op_move_object.S new file mode 100644 index 0000000000000000000000000000000000000000..0d866496e8a17f20927019de15a9c2340368ecf8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object.S @@ -0,0 +1 @@ +%include "x86_64/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_16.S b/runtime/interpreter/mterp/x86_64/op_move_object_16.S new file mode 100644 index 0000000000000000000000000000000000000000..32541ff2bfdfee06ddee388d13bdea5f5a86bbcb --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object_16.S @@ -0,0 +1 @@ +%include "x86_64/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_object_from16.S b/runtime/interpreter/mterp/x86_64/op_move_object_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..983e4abae5174c2794e494360a983ed27547f928 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_object_from16.S @@ -0,0 +1 @@ +%include "x86_64/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result.S b/runtime/interpreter/mterp/x86_64/op_move_result.S new file mode 100644 index 0000000000000000000000000000000000000000..8268344bcedc0ae4996282c86eabc9550f9e6434 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result.S @@ -0,0 +1,11 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movl (%rax), %eax # r0 <- result.i. + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- fp[B] + .else + SET_VREG %eax, rINSTq # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_object.S b/runtime/interpreter/mterp/x86_64/op_move_result_object.S new file mode 100644 index 0000000000000000000000000000000000000000..c5aac17f41594016ab9651d035f1527752319770 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result_object.S @@ -0,0 +1 @@ +%include "x86_64/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_move_result_wide.S b/runtime/interpreter/mterp/x86_64/op_move_result_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..03de7836273ef9726837e198647a68300cd19ab8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_result_wide.S @@ -0,0 +1,5 @@ + /* move-result-wide vAA */ + movq OFF_FP_RESULT_REGISTER(rFP), %rax # get pointer to result JType. + movq (%rax), %rdx # Get wide + SET_WIDE_VREG %rdx, rINSTq # v[AA] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide.S b/runtime/interpreter/mterp/x86_64/op_move_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..508f8cc1521fcbf2cbd741d3251fa19630f84b86 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide.S @@ -0,0 +1,8 @@ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movl rINST, %ecx # ecx <- BA + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rdx, rINSTq # rdx <- v[B] + SET_WIDE_VREG %rdx, %rcx # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_16.S new file mode 100644 index 0000000000000000000000000000000000000000..ce371a920eccb60fc402492389628f04292f4007 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide_16.S @@ -0,0 +1,7 @@ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwq 4(rPC), %rcx # ecx<- BBBB + movzwq 2(rPC), %rax # eax<- AAAA + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, %rax # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S b/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S new file mode 100644 index 0000000000000000000000000000000000000000..0d6971a674da52e598ec1d507c5cb2b16777fa6c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_move_wide_from16.S @@ -0,0 +1,6 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 2(rPC), %ecx # ecx <- BBBB + GET_WIDE_VREG %rdx, %rcx # rdx <- v[B] + SET_WIDE_VREG %rdx, rINSTq # v[A] <- rdx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double.S b/runtime/interpreter/mterp/x86_64/op_mul_double.S new file mode 100644 index 0000000000000000000000000000000000000000..1f4bcb3d00b49c217baff3bc1cf099676a930f19 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..9850a28995cdbc543e0adac9e418ac61f0be150d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float.S b/runtime/interpreter/mterp/x86_64/op_mul_float.S new file mode 100644 index 0000000000000000000000000000000000000000..85960e9decbf6a35aeaf0e701447912bc071d455 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..6d36b6a178554028806dec63876541e5b57dd655 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int.S b/runtime/interpreter/mterp/x86_64/op_mul_int.S new file mode 100644 index 0000000000000000000000000000000000000000..5f3923a20e7d446322f814a82bfe815823e1fc2f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"imull (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..0b5af8a927fd83417ea0e43ae8db6bde28edf80d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_2addr.S @@ -0,0 +1,8 @@ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_VREG %eax, %rcx # eax <- vA + imull (rFP,rINSTq,4), %eax + SET_VREG %eax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..a4cfdbce3eac0bd6f07f5b12c0893b883cc27f1e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..89e9acb77df807c8f15443d61f1fac4cf34f27ab --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"imull %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long.S b/runtime/interpreter/mterp/x86_64/op_mul_long.S new file mode 100644 index 0000000000000000000000000000000000000000..2b853705cf42bab73a42c6f4731f9084e399a891 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"imulq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..167128b4d180887d6ca457c3063d8ecf592ae5ce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_mul_long_2addr.S @@ -0,0 +1,8 @@ + /* mul vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_VREG %rax, %rcx # rax <- vA + imulq (rFP,rINSTq,4), %rax + SET_WIDE_VREG %rax, %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_neg_double.S b/runtime/interpreter/mterp/x86_64/op_neg_double.S new file mode 100644 index 0000000000000000000000000000000000000000..2c14b091cb38e148fac4431638fc4ea6a6af2627 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_double.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"preinstr":" movq $0x8000000000000000, %rsi", "instr":" xorq %rsi, %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_float.S b/runtime/interpreter/mterp/x86_64/op_neg_float.S new file mode 100644 index 0000000000000000000000000000000000000000..148b21ec9a325a63bf79f8be590ac58fd5c2f9cf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_float.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" xorl $0x80000000, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_int.S b/runtime/interpreter/mterp/x86_64/op_neg_int.S new file mode 100644 index 0000000000000000000000000000000000000000..f90a937a8b7d784777fcbd859fca4a6514288739 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_int.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" negl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_neg_long.S b/runtime/interpreter/mterp/x86_64/op_neg_long.S new file mode 100644 index 0000000000000000000000000000000000000000..18fc3ccecef02c6895ddfd81467818dd997f81b2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_neg_long.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" negq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_new_array.S b/runtime/interpreter/mterp/x86_64/op_new_array.S new file mode 100644 index 0000000000000000000000000000000000000000..9831a0b8884c87205f2aa341e70b1bb6b5197eae --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_new_array.S @@ -0,0 +1,18 @@ +/* + * Allocate an array of objects, specified with the array class + * and a count. + * + * The verifier guarantees that this is an array class, so we don't + * check for it here. + */ + /* new-array vA, vB, class@CCCC */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpNewArray) + 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_64/op_new_instance.S b/runtime/interpreter/mterp/x86_64/op_new_instance.S new file mode 100644 index 0000000000000000000000000000000000000000..fc8c8cd98c4cd5c17731f333f00ff1fe71ccb225 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_new_instance.S @@ -0,0 +1,13 @@ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rSELF, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + call SYMBOL(MterpNewInstance) + 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_64/op_nop.S b/runtime/interpreter/mterp/x86_64/op_nop.S new file mode 100644 index 0000000000000000000000000000000000000000..4cb68e392e719513e5e85a72fa4aeb942105536b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_nop.S @@ -0,0 +1 @@ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_not_int.S b/runtime/interpreter/mterp/x86_64/op_not_int.S new file mode 100644 index 0000000000000000000000000000000000000000..463d080de96da8e08188110e5b1e1fac4fa3a453 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_not_int.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" notl %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_not_long.S b/runtime/interpreter/mterp/x86_64/op_not_long.S new file mode 100644 index 0000000000000000000000000000000000000000..c97bb9ea1aebab74f3c33ef176818919b77f5c04 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_not_long.S @@ -0,0 +1 @@ +%include "x86_64/unop.S" {"instr":" notq %rax", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int.S b/runtime/interpreter/mterp/x86_64/op_or_int.S new file mode 100644 index 0000000000000000000000000000000000000000..730310f6afa7e0f6074db8648170aafd09b628f6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"orl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..f722e4dd9c6b095ba2e3d422404e67d25c904df0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"orl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..fee86c7c62d5810c22a60545765e9603c7188c98 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..81104c7e56a65eedc8668c38c9682b0c575fc3bf --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long.S b/runtime/interpreter/mterp/x86_64/op_or_long.S new file mode 100644 index 0000000000000000000000000000000000000000..6c70a2001ee10dd6f2e4ac4fa57f30a9fed0e3b2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"orq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..546da1de2d02b5b8a99f3059e6323bc029df52f0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"orq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_packed_switch.S b/runtime/interpreter/mterp/x86_64/op_packed_switch.S new file mode 100644 index 0000000000000000000000000000000000000000..cb0acb7a72b29f221d15dd3f8654e7b5dce4da0b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_packed_switch.S @@ -0,0 +1,22 @@ +%default { "func":"MterpDoPackedSwitch" } +/* + * Handle a packed-switch or sparse-switch instruction. In both cases + * we decode it and hand it off to a helper function. + * + * We don't really expect backward branches in a switch statement, but + * they're perfectly legal, so we check for them here. + * + * for: packed-switch, sparse-switch + */ + /* op vAA, +BBBB */ + movslq 2(rPC), OUT_ARG0 # rcx <- BBBBbbbb + leaq (rPC,OUT_ARG0,2), OUT_ARG0 # rcx <- PC + BBBBbbbb*2 + GET_VREG OUT_32_ARG1, rINSTq # eax <- vAA + call SYMBOL($func) + movslq %eax, rINSTq + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double.S b/runtime/interpreter/mterp/x86_64/op_rem_double.S new file mode 100644 index 0000000000000000000000000000000000000000..00aed787cb33d9ea21e9c5536a9860b4ec9a547c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_double.S @@ -0,0 +1,14 @@ + /* rem_double vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + fldl VREG_ADDRESS(%rcx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%rax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINSTq) # fp[vAA] <- %st + CLEAR_WIDE_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..9768266e211c6b6546dcb0f633c2dd7fe4b134e0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_double_2addr.S @@ -0,0 +1,15 @@ + /* rem_double/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $$4, rINST # rINST <- B + fldl VREG_ADDRESS(rINSTq) # vB to fp stack + andb $$0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%rcx) # %st to vA + CLEAR_WIDE_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float.S b/runtime/interpreter/mterp/x86_64/op_rem_float.S new file mode 100644 index 0000000000000000000000000000000000000000..5af28accec3261409af74d1affe2fc398953a7c1 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_float.S @@ -0,0 +1,14 @@ + /* rem_float vAA, vBB, vCC */ + movzbq 3(rPC), %rcx # ecx <- BB + movzbq 2(rPC), %rax # eax <- CC + flds VREG_ADDRESS(%rcx) # vBB to fp stack + flds VREG_ADDRESS(%rax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINSTq) # %st to vAA + CLEAR_REF rINSTq + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..e9282a8de95d9d587fa2777dbaae1460a23630d4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_float_2addr.S @@ -0,0 +1,15 @@ + /* rem_float/2addr vA, vB */ + movzbq rINSTbl, %rcx # ecx <- A+ + sarl $$4, rINST # rINST <- B + flds VREG_ADDRESS(rINSTq) # vB to fp stack + andb $$0xf, %cl # ecx <- A + flds VREG_ADDRESS(%rcx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%rcx) # %st to vA + CLEAR_REF %rcx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int.S b/runtime/interpreter/mterp/x86_64/op_rem_int.S new file mode 100644 index 0000000000000000000000000000000000000000..fd77d7cdfe229554c1ea9b10758363ca27283b78 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..25ffbf713bdd750367a95832d5a145f004c66d05 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%edx","second":"%ecx","wide":"0","suffix":"l","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..21cc37087d64e7cb91b3213351edf36d80719f12 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit16.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..2eb0150f63091614c9b386bea2bd241026bfb13b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/bindivLit8.S" {"result":"%edx","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long.S b/runtime/interpreter/mterp/x86_64/op_rem_long.S new file mode 100644 index 0000000000000000000000000000000000000000..efa721520dd415a14cc89f0ddc867db18aae4716 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_long.S @@ -0,0 +1 @@ +%include "x86_64/bindiv.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","ext":"cqo","rem":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..ce0dd86539c98efdf07c06d569172c59371833a6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/bindiv2addr.S" {"result":"%rdx","second":"%rcx","wide":"1","suffix":"q","rem":"1","ext":"cqo"} diff --git a/runtime/interpreter/mterp/x86_64/op_return.S b/runtime/interpreter/mterp/x86_64/op_return.S new file mode 100644 index 0000000000000000000000000000000000000000..14f4f8a44671ab11636ea931b9fecb2971e8d869 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return.S @@ -0,0 +1,15 @@ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_VREG %eax, rINSTq # eax <- vAA + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_object.S b/runtime/interpreter/mterp/x86_64/op_return_object.S new file mode 100644 index 0000000000000000000000000000000000000000..1ae69a501c4e6db5621262f68d690bba0379882b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_object.S @@ -0,0 +1 @@ +%include "x86_64/op_return.S" diff --git a/runtime/interpreter/mterp/x86_64/op_return_void.S b/runtime/interpreter/mterp/x86_64/op_return_void.S new file mode 100644 index 0000000000000000000000000000000000000000..46a5753c87b2258e6c4b11e719d4ad7a1cf60182 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_void.S @@ -0,0 +1,9 @@ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S new file mode 100644 index 0000000000000000000000000000000000000000..92e3506d1d7c0b9f0dc4a31400e257b49f7d8581 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_void_no_barrier.S @@ -0,0 +1,7 @@ + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + xorq %rax, %rax + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_return_wide.S b/runtime/interpreter/mterp/x86_64/op_return_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..f2d6e04cab785a82eb6caabe0dbb97adc9af8dce --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_return_wide.S @@ -0,0 +1,13 @@ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call SYMBOL(MterpThreadFenceForConstructor) + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(rSELF) + jz 1f + movq rSELF, OUT_ARG0 + call SYMBOL(MterpSuspendCheck) +1: + GET_WIDE_VREG %rax, rINSTq # eax <- v[AA] + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int.S b/runtime/interpreter/mterp/x86_64/op_rsub_int.S new file mode 100644 index 0000000000000000000000000000000000000000..2dd20026df95c6c4486b610495fea0f146365625 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rsub_int.S @@ -0,0 +1,2 @@ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +%include "x86_64/binopLit16.S" {"instr":"subl %eax, %ecx","result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..64d0d8a704fedd32bd4ce99bb9c2589d01fed04d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"subl %eax, %ecx" , "result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget.S b/runtime/interpreter/mterp/x86_64/op_sget.S new file mode 100644 index 0000000000000000000000000000000000000000..38d9a5e6c8bb4b94fd468f47ed7040dd202ebbc2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget.S @@ -0,0 +1,25 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode", "wide":"0" } +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short, sget-wide + */ + /* op vAA, field@BBBB */ + .extern $helper + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref CCCC + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + movq rSELF, OUT_ARG2 # self + call SYMBOL($helper) + cmpl $$0, THREAD_EXCEPTION_OFFSET(rSELF) + jnz MterpException + .if $is_object + SET_VREG_OBJECT %eax, rINSTq # fp[A] <- value + .else + .if $wide + SET_WIDE_VREG %rax, rINSTq # fp[A] <- value + .else + SET_VREG %eax, rINSTq # fp[A] <- value + .endif + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sget_boolean.S b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..7d358daec2c2941ebd565e05d59329090fe8c151 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetBooleanStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_byte.S b/runtime/interpreter/mterp/x86_64/op_sget_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..79d9ff448b4d1015b39762f3e1e41f8e560c683a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetByteStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_char.S b/runtime/interpreter/mterp/x86_64/op_sget_char.S new file mode 100644 index 0000000000000000000000000000000000000000..448861052f75eb59ed7ee50b275aa80f51887629 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_char.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetCharStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_object.S b/runtime/interpreter/mterp/x86_64/op_sget_object.S new file mode 100644 index 0000000000000000000000000000000000000000..09b627e1247219fde91ccc658b92f000b8c04ef2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_object.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_short.S b/runtime/interpreter/mterp/x86_64/op_sget_short.S new file mode 100644 index 0000000000000000000000000000000000000000..47ac23803ce238b4c7e7462fad366fc5104610aa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_short.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGetShortStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sget_wide.S b/runtime/interpreter/mterp/x86_64/op_sget_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..aa223434cfe9ef9435598d8c1a1e0d0288fd886d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sget_wide.S @@ -0,0 +1 @@ +%include "x86_64/op_sget.S" {"helper":"artGet64StaticFromCode", "wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int.S b/runtime/interpreter/mterp/x86_64/op_shl_int.S new file mode 100644 index 0000000000000000000000000000000000000000..fa1edb7555f01219fbb8b1523f2b7ebe1d261f68 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..dd962792c9f0b71c785905bbd1ece29a054a09c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..39b23ae1fb4e531c7cd377e973271831e962aa4b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long.S b/runtime/interpreter/mterp/x86_64/op_shl_long.S new file mode 100644 index 0000000000000000000000000000000000000000..fdc7cb64de5c6a5d16176ac2d261ffef2c5ec1cc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..546633f7d591219d9c3b63e22ff6811b8e45f121 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shl_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"salq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int.S b/runtime/interpreter/mterp/x86_64/op_shr_int.S new file mode 100644 index 0000000000000000000000000000000000000000..fc289f4638c9c352eed0180ed4d7386ad0b6726e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..0e5bca7057e800f24a6c55c02d35dd41a3234623 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..3cc930756979cb562683e0c10a14374ad3151b79 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long.S b/runtime/interpreter/mterp/x86_64/op_shr_long.S new file mode 100644 index 0000000000000000000000000000000000000000..25028d3560f4c617eb047a138b12e9a3959a7e39 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..373841322dfdeca26abb79ef81bd667e8ea30653 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_shr_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"sarq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_sparse_switch.S b/runtime/interpreter/mterp/x86_64/op_sparse_switch.S new file mode 100644 index 0000000000000000000000000000000000000000..0eaa5148135db6d92cddda70bf7c87380795c262 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sparse_switch.S @@ -0,0 +1 @@ +%include "x86_64/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/x86_64/op_sput.S b/runtime/interpreter/mterp/x86_64/op_sput.S new file mode 100644 index 0000000000000000000000000000000000000000..e92b03273bcdccc94c65a7a081d04e7c9d493457 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput.S @@ -0,0 +1,17 @@ +%default { "helper":"artSet32StaticFromCode"} +/* + * General SPUT handler wrapper. + * + * for: sput, sput-boolean, sput-byte, sput-char, sput-short + */ + /* op vAA, field@BBBB */ + .extern $helper + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + GET_VREG OUT_32_ARG1, rINSTq # fp[AA] + movq OFF_FP_METHOD(rFP), OUT_ARG2 # referrer + movq rSELF, OUT_ARG3 # self + call SYMBOL($helper) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_boolean.S b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S new file mode 100644 index 0000000000000000000000000000000000000000..8718915cb2609bc08cfed2badf5fc6d3ff5a9a63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_boolean.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_byte.S b/runtime/interpreter/mterp/x86_64/op_sput_byte.S new file mode 100644 index 0000000000000000000000000000000000000000..8718915cb2609bc08cfed2badf5fc6d3ff5a9a63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_byte.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_char.S b/runtime/interpreter/mterp/x86_64/op_sput_char.S new file mode 100644 index 0000000000000000000000000000000000000000..2fe9d14816c081f8f8238c766daafad7e260cdcc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_char.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_object.S b/runtime/interpreter/mterp/x86_64/op_sput_object.S new file mode 100644 index 0000000000000000000000000000000000000000..eb5a37673ef8f23e860576361ef553addfc40a1e --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_object.S @@ -0,0 +1,10 @@ + EXPORT_PC + leaq OFF_FP_SHADOWFRAME(rFP), OUT_ARG0 + movq rPC, OUT_ARG1 + REFRESH_INST ${opnum} + movq rINSTq, OUT_ARG2 + movq rSELF, OUT_ARG3 + call SYMBOL(MterpSputObject) + testb %al, %al + jz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sput_short.S b/runtime/interpreter/mterp/x86_64/op_sput_short.S new file mode 100644 index 0000000000000000000000000000000000000000..2fe9d14816c081f8f8238c766daafad7e260cdcc --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_short.S @@ -0,0 +1 @@ +%include "x86_64/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86_64/op_sput_wide.S b/runtime/interpreter/mterp/x86_64/op_sput_wide.S new file mode 100644 index 0000000000000000000000000000000000000000..c4bc269eb65c4308bc52d2eee8b01541bbdeae23 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sput_wide.S @@ -0,0 +1,15 @@ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwq 2(rPC), OUT_ARG0 # field ref BBBB + movq OFF_FP_METHOD(rFP), OUT_ARG1 # referrer + leaq VREG_ADDRESS(rINSTq), OUT_ARG2 # &fp[AA] + movq rSELF, OUT_ARG3 # self + call SYMBOL(artSet64IndirectStaticFromMterp) + testb %al, %al + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double.S b/runtime/interpreter/mterp/x86_64/op_sub_double.S new file mode 100644 index 0000000000000000000000000000000000000000..952667e831e0235f73ef71afcb943be3546d8276 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_double.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..0bd5dbb8ff0543d2ad8b469ca70bcfa561afea45 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float.S b/runtime/interpreter/mterp/x86_64/op_sub_float.S new file mode 100644 index 0000000000000000000000000000000000000000..ea0ae14f5b1fb3b2b44d76351d12f9f424717980 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_float.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..9dd17805c809b11f33f555534191939e3a3413c3 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "x86_64/sseBinop2Addr.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int.S b/runtime/interpreter/mterp/x86_64/op_sub_int.S new file mode 100644 index 0000000000000000000000000000000000000000..560394f43f0d9b020c5238510db4e74d87e67cf6 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"subl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..6f50f78f41465da7a0ff4da814db8c1f6bf6c198 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"subl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long.S b/runtime/interpreter/mterp/x86_64/op_sub_long.S new file mode 100644 index 0000000000000000000000000000000000000000..7fa54e7a11662b549eff85839385e2e3f7e88ef9 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"subq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..c18be10919af0f7349a5251b609ed329aeefdb0d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_sub_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"subq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_throw.S b/runtime/interpreter/mterp/x86_64/op_throw.S new file mode 100644 index 0000000000000000000000000000000000000000..22ed9906450b310de0654943f6ede8552de69d2b --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_throw.S @@ -0,0 +1,10 @@ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax, rINSTq # eax<- vAA (exception object) + testb %al, %al + jz common_errNullObject + movq %rax, THREAD_EXCEPTION_OFFSET(rSELF) + jmp MterpException diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3e.S b/runtime/interpreter/mterp/x86_64/op_unused_3e.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_3e.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_3f.S b/runtime/interpreter/mterp/x86_64/op_unused_3f.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_3f.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_40.S b/runtime/interpreter/mterp/x86_64/op_unused_40.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_40.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_41.S b/runtime/interpreter/mterp/x86_64/op_unused_41.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_41.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_42.S b/runtime/interpreter/mterp/x86_64/op_unused_42.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_42.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_43.S b/runtime/interpreter/mterp/x86_64/op_unused_43.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_43.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_79.S b/runtime/interpreter/mterp/x86_64/op_unused_79.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_79.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_7a.S b/runtime/interpreter/mterp/x86_64/op_unused_7a.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_7a.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_f4.S b/runtime/interpreter/mterp/x86_64/op_unused_f4.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_f4.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fa.S b/runtime/interpreter/mterp/x86_64/op_unused_fa.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fa.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fb.S b/runtime/interpreter/mterp/x86_64/op_unused_fb.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fb.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fc.S b/runtime/interpreter/mterp/x86_64/op_unused_fc.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fc.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fd.S b/runtime/interpreter/mterp/x86_64/op_unused_fd.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fd.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_fe.S b/runtime/interpreter/mterp/x86_64/op_unused_fe.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_fe.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_unused_ff.S b/runtime/interpreter/mterp/x86_64/op_unused_ff.S new file mode 100644 index 0000000000000000000000000000000000000000..280615f08b124bd9c513704501c49895504edcd0 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_unused_ff.S @@ -0,0 +1 @@ +%include "x86_64/unused.S" diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int.S b/runtime/interpreter/mterp/x86_64/op_ushr_int.S new file mode 100644 index 0000000000000000000000000000000000000000..dd91086371e241d76d6a7957af630ee348211b84 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..d38aedd234bb0da0300b60c4f183fc39cb373bad --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..f7ff8abc869bc0a6721da5ef0e87b15ca92c582c --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long.S b/runtime/interpreter/mterp/x86_64/op_ushr_long.S new file mode 100644 index 0000000000000000000000000000000000000000..7c6daca05d4f92a2b1a49b6b31b1b002f6ff65e2 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_long.S @@ -0,0 +1 @@ +%include "x86_64/binop1.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..cd6a22c6fa398d2f8f7e11a8a0509e73dd2df111 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_ushr_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/shop2addr.S" {"instr":"shrq %cl, %rax","wide":"1"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int.S b/runtime/interpreter/mterp/x86_64/op_xor_int.S new file mode 100644 index 0000000000000000000000000000000000000000..b295d74de0cc53821f02f187f3c9b832b6b20d63 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int.S @@ -0,0 +1 @@ +%include "x86_64/binop.S" {"instr":"xorl (rFP,%rcx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..879bfc05dc87b24ee77d81865c867ec3272a45ad --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binop2addr.S" {"instr":"xorl %eax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S new file mode 100644 index 0000000000000000000000000000000000000000..5d375a1cf69ce01aed1f823e27bd37d0a3b75b04 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "x86_64/binopLit16.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S b/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S new file mode 100644 index 0000000000000000000000000000000000000000..54cce9c18e36677f1dadd87d6538269c775274c8 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "x86_64/binopLit8.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long.S b/runtime/interpreter/mterp/x86_64/op_xor_long.S new file mode 100644 index 0000000000000000000000000000000000000000..52b44e29c113337b0522f92f536ce5d35c7ce5d4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_long.S @@ -0,0 +1 @@ +%include "x86_64/binopWide.S" {"instr":"xorq (rFP,%rcx,4), %rax"} diff --git a/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S b/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..d75c4ba6ce95e9024edfcbe8e82f8acc9719131f --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "x86_64/binopWide2addr.S" {"instr":"xorq %rax, (rFP,%rcx,4)"} diff --git a/runtime/interpreter/mterp/x86_64/shop2addr.S b/runtime/interpreter/mterp/x86_64/shop2addr.S new file mode 100644 index 0000000000000000000000000000000000000000..6b06d002fc99c3e1cc441507185ad5934b0f9aaa --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/shop2addr.S @@ -0,0 +1,19 @@ +%default {"wide":"0"} +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movl rINST, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %rcx # ecx <- vBB + andb $$0xf, rINSTbl # rINST <- A + .if $wide + GET_WIDE_VREG %rax, rINSTq # rax <- vAA + $instr # ex: sarl %cl, %eax + SET_WIDE_VREG %rax, rINSTq + .else + GET_VREG %eax, rINSTq # eax <- vAA + $instr # ex: sarl %cl, %eax + SET_VREG %eax, rINSTq + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop.S b/runtime/interpreter/mterp/x86_64/sseBinop.S new file mode 100644 index 0000000000000000000000000000000000000000..09d3364de76631a7c8920436e4a1c69580cc794a --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/sseBinop.S @@ -0,0 +1,9 @@ +%default {"instr":"","suff":""} + movzbq 2(rPC), %rcx # ecx <- BB + movzbq 3(rPC), %rax # eax <- CC + movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + ${instr}${suff} VREG_ADDRESS(%rax), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(rINSTq) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S b/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S new file mode 100644 index 0000000000000000000000000000000000000000..084166b95d1dde1ed4e31eb0005bda49aa6b465d --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/sseBinop2Addr.S @@ -0,0 +1,10 @@ +%default {"instr":"","suff":""} + movl rINST, %ecx # ecx <- A+ + andl $$0xf, %ecx # ecx <- A + movs${suff} VREG_ADDRESS(%rcx), %xmm0 # %xmm0 <- 1st src + sarl $$4, rINST # rINST<- B + ${instr}${suff} VREG_ADDRESS(rINSTq), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(%rcx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINSTq) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unop.S b/runtime/interpreter/mterp/x86_64/unop.S new file mode 100644 index 0000000000000000000000000000000000000000..1777123f3603a51dea8635c43b3e435618ae4672 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/unop.S @@ -0,0 +1,22 @@ +%default {"preinstr":"", "instr":"", "wide":"0"} +/* + * Generic 32/64-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movl rINST, %ecx # rcx <- A+ + sarl $$4,rINST # rINST <- B + .if ${wide} + GET_WIDE_VREG %rax, rINSTq # rax <- vB + .else + GET_VREG %eax, rINSTq # eax <- vB + .endif + andb $$0xf,%cl # ecx <- A +$preinstr +$instr + .if ${wide} + SET_WIDE_VREG %rax, %rcx + .else + SET_VREG %eax, %rcx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86_64/unused.S b/runtime/interpreter/mterp/x86_64/unused.S new file mode 100644 index 0000000000000000000000000000000000000000..c95ef947d3c4187addee86d70f601836922c05c4 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback diff --git a/runtime/interpreter/mterp/x86_64/zcmp.S b/runtime/interpreter/mterp/x86_64/zcmp.S new file mode 100644 index 0000000000000000000000000000000000000000..0051407cad603a9ef4127470fc98b31ff5ae8413 --- /dev/null +++ b/runtime/interpreter/mterp/x86_64/zcmp.S @@ -0,0 +1,19 @@ +/* + * Generic one-operand compare-and-branch operation. Provide a "revcmp" + * fragment that specifies the *reverse* comparison to perform, e.g. + * for "if-le" you would use "gt". + * + * for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez + */ + /* if-cmp vAA, +BBBB */ + cmpl $$0, VREG_ADDRESS(rINSTq) # compare (vA, 0) + movl $$2, rINST # assume branch not taken + j${revcmp} 1f + movswq 2(rPC), rINSTq # fetch signed displacement +1: + MTERP_PROFILE_BRANCH + addq rINSTq, rINSTq # rINSTq <- AA * 2 + leaq (rPC, rINSTq), rPC + FETCH_INST + jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check + GOTO_NEXT diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 2481a4fd216eeb04ffa33ffb82f57aaa2bd1db1a..5bd9a6b76b267b5f8806d2c95cfb418b8f431664 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -81,12 +81,7 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt } void Jit::DumpInfo(std::ostream& os) { - os << "JIT code cache size=" << PrettySize(code_cache_->CodeCacheSize()) << "\n" - << "JIT data cache size=" << PrettySize(code_cache_->DataCacheSize()) << "\n" - << "JIT current capacity=" << PrettySize(code_cache_->GetCurrentCapacity()) << "\n" - << "JIT number of compiled code=" << code_cache_->NumberOfCompiledCode() << "\n" - << "JIT total number of compilations=" << code_cache_->NumberOfCompilations() << "\n" - << "JIT total number of osr compilations=" << code_cache_->NumberOfOsrCompilations() << "\n"; + code_cache_->Dump(os); cumulative_timings_.Dump(os); } @@ -119,7 +114,7 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { return nullptr; } jit->save_profiling_info_ = options->GetSaveProfilingInfo(); - LOG(INFO) << "JIT created with initial_capacity=" + VLOG(jit) << "JIT created with initial_capacity=" << PrettySize(options->GetCodeCacheInitialCapacity()) << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity()) << ", compile_threshold=" << options->GetCompileThreshold() @@ -196,7 +191,6 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { // of that proxy method, as the compiler does not expect a proxy method. ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*)); if (!code_cache_->NotifyCompilationOf(method_to_compile, self, osr)) { - VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to code cache"; return false; } bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, osr); @@ -398,8 +392,7 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, continue; } - DCHECK(location == DexRegisterLocation::Kind::kInStack) - << DexRegisterLocation::PrettyDescriptor(location); + DCHECK_EQ(location, DexRegisterLocation::Kind::kInStack); int32_t vreg_value = shadow_frame->GetVReg(vreg); int32_t slot_offset = vreg_map.GetStackOffsetInBytes(vreg, diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 2c1508799cd197dc1c4351c2fda7df4800da066c..af47da63c4a5df9de9fdba01f8e9d741161f653c 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -25,6 +25,7 @@ #include "debugger_interface.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/bitmap-inl.h" +#include "jit/jit.h" #include "jit/profiling_info.h" #include "linear_alloc.h" #include "mem_map.h" @@ -131,7 +132,9 @@ JitCodeCache::JitCodeCache(MemMap* code_map, used_memory_for_data_(0), used_memory_for_code_(0), number_of_compilations_(0), - number_of_osr_compilations_(0) { + number_of_osr_compilations_(0), + number_of_deoptimizations_(0), + number_of_collections_(0) { DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity); code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/); @@ -294,6 +297,15 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { } } +void JitCodeCache::ClearGcRootsInInlineCaches(Thread* self) { + MutexLock mu(self, lock_); + for (ProfilingInfo* info : profiling_infos_) { + if (!info->IsInUseByCompiler()) { + info->ClearGcRootsInInlineCaches(); + } + } +} + uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, ArtMethod* method, const uint8_t* mapping_table, @@ -370,16 +382,6 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, return reinterpret_cast(method_header); } -size_t JitCodeCache::NumberOfCompilations() { - MutexLock mu(Thread::Current(), lock_); - return number_of_compilations_; -} - -size_t JitCodeCache::NumberOfOsrCompilations() { - MutexLock mu(Thread::Current(), lock_); - return number_of_osr_compilations_; -} - size_t JitCodeCache::CodeCacheSize() { MutexLock mu(Thread::Current(), lock_); return CodeCacheSizeLocked(); @@ -398,11 +400,6 @@ size_t JitCodeCache::DataCacheSizeLocked() { return used_memory_for_data_; } -size_t JitCodeCache::NumberOfCompiledCode() { - MutexLock mu(Thread::Current(), lock_); - return method_code_map_.size(); -} - void JitCodeCache::ClearData(Thread* self, void* data) { MutexLock mu(self, lock_); FreeData(reinterpret_cast(data)); @@ -586,6 +583,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { if (WaitForPotentialCollectionToComplete(self)) { return; } else { + number_of_collections_++; live_bitmap_.reset(CodeCacheBitmap::Create( "code-cache-bitmap", reinterpret_cast(code_map_->Begin()), @@ -594,81 +592,85 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } } - bool do_full_collection = false; + TimingLogger logger("JIT code cache timing logger", true, VLOG_IS_ON(jit)); { - MutexLock mu(self, lock_); - do_full_collection = ShouldDoFullCollection(); - } - - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "Do " - << (do_full_collection ? "full" : "partial") - << " code cache collection, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); - } + TimingLogger::ScopedTiming st("Code cache collection", &logger); - DoCollection(self, /* collect_profiling_info */ do_full_collection); + bool do_full_collection = false; + { + MutexLock mu(self, lock_); + do_full_collection = ShouldDoFullCollection(); + } - if (!kIsDebugBuild || VLOG_IS_ON(jit)) { - LOG(INFO) << "After code cache collection, code=" - << PrettySize(CodeCacheSize()) - << ", data=" << PrettySize(DataCacheSize()); - } + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "Do " + << (do_full_collection ? "full" : "partial") + << " code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); + } - { - MutexLock mu(self, lock_); + DoCollection(self, /* collect_profiling_info */ do_full_collection); - // Increase the code cache only when we do partial collections. - // TODO: base this strategy on how full the code cache is? - if (do_full_collection) { - last_collection_increased_code_cache_ = false; - } else { - last_collection_increased_code_cache_ = true; - IncreaseCodeCacheCapacity(); + if (!kIsDebugBuild || VLOG_IS_ON(jit)) { + LOG(INFO) << "After code cache collection, code=" + << PrettySize(CodeCacheSize()) + << ", data=" << PrettySize(DataCacheSize()); } - bool next_collection_will_be_full = ShouldDoFullCollection(); + { + MutexLock mu(self, lock_); - // Start polling the liveness of compiled code to prepare for the next full collection. - // We avoid doing this if exit stubs are installed to not mess with the instrumentation. - // TODO(ngeoffray): Clean up instrumentation and code cache interactions. - if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() && - next_collection_will_be_full) { - // Save the entry point of methods we have compiled, and update the entry - // point of those methods to the interpreter. If the method is invoked, the - // interpreter will update its entry point to the compiled code and call it. - for (ProfilingInfo* info : profiling_infos_) { - const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); - if (ContainsPc(entry_point)) { - info->SetSavedEntryPoint(entry_point); - info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); - } + // Increase the code cache only when we do partial collections. + // TODO: base this strategy on how full the code cache is? + if (do_full_collection) { + last_collection_increased_code_cache_ = false; + } else { + last_collection_increased_code_cache_ = true; + IncreaseCodeCacheCapacity(); } - DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + bool next_collection_will_be_full = ShouldDoFullCollection(); + + // Start polling the liveness of compiled code to prepare for the next full collection. + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if (!Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() && + next_collection_will_be_full) { + // Save the entry point of methods we have compiled, and update the entry + // point of those methods to the interpreter. If the method is invoked, the + // interpreter will update its entry point to the compiled code and call it. + for (ProfilingInfo* info : profiling_infos_) { + const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); + if (ContainsPc(entry_point)) { + info->SetSavedEntryPoint(entry_point); + info->GetMethod()->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); + } + } + + DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + } + live_bitmap_.reset(nullptr); + NotifyCollectionDone(self); } - live_bitmap_.reset(nullptr); - NotifyCollectionDone(self); } + Runtime::Current()->GetJit()->AddTimingLogger(logger); } -void JitCodeCache::RemoveUnusedAndUnmarkedCode(Thread* self) { +void JitCodeCache::RemoveUnmarkedCode(Thread* self) { ScopedTrace trace(__FUNCTION__); MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); - // Iterate over all compiled code and remove entries that are not marked and not - // the entrypoint of their corresponding ArtMethod. + // Iterate over all compiled code and remove entries that are not marked. 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); - const void* entrypoint = method->GetEntryPointFromQuickCompiledCode(); - if ((entrypoint == method_header->GetEntryPoint()) || GetLiveBitmap()->Test(allocation)) { + if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - if (entrypoint == GetQuickToInterpreterBridge()) { + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (method_header->GetEntryPoint() == GetQuickToInterpreterBridge()) { method->ClearCounter(); } FreeCode(code_ptr, method); @@ -686,7 +688,7 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // Also remove the saved entry point from the ProfilingInfo objects. for (ProfilingInfo* info : profiling_infos_) { const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); - if (!ContainsPc(ptr) && !info->IsMethodBeingCompiled()) { + if (!ContainsPc(ptr) && !info->IsInUseByCompiler()) { info->GetMethod()->SetProfilingInfo(nullptr); } info->SetSavedEntryPoint(nullptr); @@ -698,6 +700,19 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { } } + // Mark compiled code that are entrypoints of ArtMethods. Compiled code that is not + // an entry point is either: + // - an osr compiled code, that will be removed if not in a thread call stack. + // - discarded compiled code, that will be removed if not in a thread call stack. + for (const auto& it : method_code_map_) { + ArtMethod* method = it.second; + const void* code_ptr = it.first; + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); + } + } + // Empty osr method map, as osr compiled code will be deleted (except the ones // on thread stacks). osr_code_map_.clear(); @@ -706,9 +721,10 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // Run a checkpoint on all threads to mark the JIT compiled code they are running. MarkCompiledCodeOnThreadStacks(self); - // Remove compiled code that is not the entrypoint of their method and not in the call - // stack. - RemoveUnusedAndUnmarkedCode(self); + // At this point, mutator threads are still running, and entrypoints of methods can + // change. We do know they cannot change to a code cache entry that is not marked, + // therefore we can safely remove those entries. + RemoveUnmarkedCode(self); if (collect_profiling_info) { MutexLock mu(self, lock_); @@ -724,7 +740,7 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // code cache collection. if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) { // We clear the inline caches as classes in it might be stalled. - info->ClearInlineCaches(); + info->ClearGcRootsInInlineCaches(); // Do a fence to make sure the clearing is seen before attaching to the method. QuasiAtomic::ThreadFenceRelease(); info->GetMethod()->SetProfilingInfo(info); @@ -801,23 +817,38 @@ OatQuickMethodHeader* JitCodeCache::LookupOsrMethodHeader(ArtMethod* method) { ProfilingInfo* JitCodeCache::AddProfilingInfo(Thread* self, ArtMethod* method, const std::vector& entries, - bool retry_allocation) { - ProfilingInfo* info = AddProfilingInfoInternal(self, method, entries); + bool retry_allocation) + // No thread safety analysis as we are using TryLock/Unlock explicitly. + NO_THREAD_SAFETY_ANALYSIS { + ProfilingInfo* info = nullptr; + if (!retry_allocation) { + // If we are allocating for the interpreter, just try to lock, to avoid + // lock contention with the JIT. + if (lock_.ExclusiveTryLock(self)) { + info = AddProfilingInfoInternal(self, method, entries); + lock_.ExclusiveUnlock(self); + } + } else { + { + MutexLock mu(self, lock_); + info = AddProfilingInfoInternal(self, method, entries); + } - if (info == nullptr && retry_allocation) { - GarbageCollectCache(self); - info = AddProfilingInfoInternal(self, method, entries); + if (info == nullptr) { + GarbageCollectCache(self); + MutexLock mu(self, lock_); + info = AddProfilingInfoInternal(self, method, entries); + } } return info; } -ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self, +ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNUSED, ArtMethod* method, const std::vector& entries) { size_t profile_info_size = RoundUp( sizeof(ProfilingInfo) + sizeof(InlineCache) * entries.size(), sizeof(void*)); - MutexLock mu(self, lock_); // Check whether some other thread has concurrently created it. ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); @@ -872,21 +903,47 @@ uint64_t JitCodeCache::GetLastUpdateTimeNs() const { bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) { if (!osr && ContainsPc(method->GetEntryPointFromQuickCompiledCode())) { + VLOG(jit) << PrettyMethod(method) << " is already compiled"; return false; } MutexLock mu(self, lock_); if (osr && (osr_code_map_.find(method) != osr_code_map_.end())) { + VLOG(jit) << PrettyMethod(method) << " is already osr compiled"; return false; } + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); - if (info == nullptr || info->IsMethodBeingCompiled()) { + if (info == nullptr) { + VLOG(jit) << PrettyMethod(method) << " needs a ProfilingInfo to be compiled"; + return false; + } + + if (info->IsMethodBeingCompiled()) { + VLOG(jit) << PrettyMethod(method) << " is already being compiled"; return false; } + info->SetIsMethodBeingCompiled(true); return true; } +ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { + MutexLock mu(self, lock_); + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); + if (info != nullptr) { + info->IncrementInlineUse(); + } + return info; +} + +void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { + MutexLock mu(self, lock_); + ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); + DCHECK(info != nullptr); + info->DecrementInlineUse(); +} + void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED) { ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*)); DCHECK(info->IsMethodBeingCompiled()); @@ -922,6 +979,8 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, osr_code_map_.erase(it); } } + MutexLock mu(Thread::Current(), lock_); + number_of_deoptimizations_++; } uint8_t* JitCodeCache::AllocateCode(size_t code_size) { @@ -951,5 +1010,18 @@ void JitCodeCache::FreeData(uint8_t* data) { mspace_free(data_mspace_, data); } +void JitCodeCache::Dump(std::ostream& os) { + MutexLock mu(Thread::Current(), lock_); + os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" + << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" + << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" + << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" + << "Total number of JIT compilations: " << number_of_compilations_ << "\n" + << "Total number of JIT compilations for on stack replacement: " + << number_of_osr_compilations_ << "\n" + << "Total number of deoptimizations: " << number_of_deoptimizations_ << "\n" + << "Total number of JIT code cache collections: " << number_of_collections_ << std::endl; +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 7b33b928e66a8b408002cc210b4a698652730c69..98dd70dcf98d02acdedeca58ff8a3116aff17242 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -67,22 +67,26 @@ class JitCodeCache { // Number of bytes allocated in the data cache. size_t DataCacheSize() REQUIRES(!lock_); - // Number of compiled code in the code cache. Note that this is not the number - // of methods that got JIT compiled, as we might have collected some. - size_t NumberOfCompiledCode() REQUIRES(!lock_); - - // Number of compilations done throughout the lifetime of the JIT. - size_t NumberOfCompilations() REQUIRES(!lock_); - size_t NumberOfOsrCompilations() REQUIRES(!lock_); - bool NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); + // Notify to the code cache that the compiler wants to use the + // profiling info of `method` to drive optimizations, + // and therefore ensure the returned profiling info object is not + // collected. + ProfilingInfo* NotifyCompilerUse(ArtMethod* method, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!lock_); + void DoneCompiling(ArtMethod* method, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_); + void DoneCompilerUse(ArtMethod* method, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!lock_); + // Allocate and write code and its metadata to the code cache. uint8_t* CommitCode(Thread* self, ArtMethod* method, @@ -151,6 +155,8 @@ class JitCodeCache { REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + void ClearGcRootsInInlineCaches(Thread* self) REQUIRES(!lock_); + // Create a 'ProfileInfo' for 'method'. If 'retry_allocation' is true, // will collect and retry if the first allocation is unsuccessful. ProfilingInfo* AddProfilingInfo(Thread* self, @@ -185,6 +191,8 @@ class JitCodeCache { REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); + void Dump(std::ostream& os) REQUIRES(!lock_); + private: // Take ownership of maps. JitCodeCache(MemMap* code_map, @@ -213,7 +221,7 @@ class JitCodeCache { ProfilingInfo* AddProfilingInfoInternal(Thread* self, ArtMethod* method, const std::vector& entries) - REQUIRES(!lock_) + REQUIRES(lock_) SHARED_REQUIRES(Locks::mutator_lock_); // If a collection is in progress, wait for it to finish. Return @@ -244,7 +252,7 @@ class JitCodeCache { REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); - void RemoveUnusedAndUnmarkedCode(Thread* self) + void RemoveUnmarkedCode(Thread* self) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -256,6 +264,11 @@ class JitCodeCache { REQUIRES(lock_) SHARED_REQUIRES(Locks::mutator_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_); + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -307,19 +320,21 @@ class JitCodeCache { // 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_); + + // Number of compilations for on-stack-replacement done throughout the lifetime of the JIT. size_t number_of_osr_compilations_ GUARDED_BY(lock_); + // Number of deoptimizations done throughout the lifetime of the JIT. + size_t number_of_deoptimizations_ GUARDED_BY(lock_); + + // Number of code cache collections done throughout the lifetime of the JIT. + size_t number_of_collections_ GUARDED_BY(lock_); + DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; - } // namespace jit } // namespace art diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 3820592c4c16711c38b11488b16c794c7c7e07a3..07c805121422d6812ffa25b6a23ae2477ca2f370 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -97,8 +97,8 @@ void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) { } } } - // Unsuccessfull - cache is full, making it megamorphic. - DCHECK(cache->IsMegamorphic()); + // Unsuccessfull - cache is full, making it megamorphic. We do not DCHECK it though, + // as the garbage collector might clear the entries concurrently. } } // namespace art diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index a8c056c7c93f89445ca8dd3effb47592256c93a9..55d627ab4816a952f35f9a6b848ee2036642bfba 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -56,10 +56,11 @@ class InlineCache { mirror::Class* GetMonomorphicType() const SHARED_REQUIRES(Locks::mutator_lock_) { // Note that we cannot ensure the inline cache is actually monomorphic // at this point, as other threads may have updated it. + DCHECK(!classes_[0].IsNull()); return classes_[0].Read(); } - bool IsUnitialized() const { + bool IsUninitialized() const { return classes_[0].IsNull(); } @@ -134,8 +135,27 @@ class ProfilingInfo { return saved_entry_point_; } - void ClearInlineCaches() { - memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); + void ClearGcRootsInInlineCaches() { + for (size_t i = 0; i < number_of_inline_caches_; ++i) { + InlineCache* cache = &cache_[i]; + memset(&cache->classes_[0], + 0, + InlineCache::kIndividualCacheSize * sizeof(GcRoot)); + } + } + + void IncrementInlineUse() { + DCHECK_NE(current_inline_uses_, std::numeric_limits::max()); + current_inline_uses_++; + } + + void DecrementInlineUse() { + DCHECK_GT(current_inline_uses_, 0); + current_inline_uses_--; + } + + bool IsInUseByCompiler() const { + return IsMethodBeingCompiled() || (current_inline_uses_ > 0); } private: @@ -143,8 +163,9 @@ class ProfilingInfo { : number_of_inline_caches_(entries.size()), method_(method), is_method_being_compiled_(false), + current_inline_uses_(0), saved_entry_point_(nullptr) { - ClearInlineCaches(); + memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache)); for (size_t i = 0; i < number_of_inline_caches_; ++i) { cache_[i].dex_pc_ = entries[i]; } @@ -161,6 +182,10 @@ class ProfilingInfo { // TODO: Make the JIT code cache lock global. bool is_method_being_compiled_; + // When the compiler inlines the method associated to this ProfilingInfo, + // it updates this counter so that the GC does not try to clear the inline caches. + uint16_t current_inline_uses_; + // Entry point of the corresponding ArtMethod, while the JIT code cache // is poking for the liveness of compiled code. const void* saved_entry_point_; diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 11156c6229d41825bcc9e394c7a9b859cf3331b0..421641ce395ba3f79ab377b0ca735598cc810eac 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -590,7 +590,19 @@ void MemMap::MadviseDontNeedAndZero() { } bool MemMap::Sync() { - return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; + bool result; + if (redzone_size_ != 0) { + // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing + // it to msync() as it only accepts page-aligned base address, and exclude the higher-end + // noaccess protection from the msync range. b/27552451. + uint8_t* base_begin = reinterpret_cast(base_begin_); + MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin); + result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0; + MEMORY_TOOL_MAKE_NOACCESS(base_begin, begin_ - base_begin); + } else { + result = msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; + } + return result; } bool MemMap::Protect(int prot) { diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 4d941302f989aad16a3ffce738289943dd4f4286..701c600822535028e13d2cb6c326aaf778e49cee 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -183,7 +183,7 @@ int32_t Object::IdentityHashCode() const { break; } case LockWord::kFatLocked: { - // Already inflated, return the has stored in the monitor. + // Already inflated, return the hash stored in the monitor. Monitor* monitor = lw.FatLockMonitor(); DCHECK(monitor != nullptr); return monitor->GetHashCode(); diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc index 2832e32dd170f73d7b1aa63734647d12e7451704..ce38e4f108de1b9c8e213c89b2a55029ede2a433 100644 --- a/runtime/monitor_pool.cc +++ b/runtime/monitor_pool.cc @@ -42,16 +42,17 @@ void MonitorPool::AllocateChunk() { if (capacity_ == 0U) { // Initialization. capacity_ = kInitialChunkStorage; - uintptr_t* new_backing = new uintptr_t[capacity_]; + uintptr_t* new_backing = new uintptr_t[capacity_](); + DCHECK(monitor_chunks_.LoadRelaxed() == nullptr); monitor_chunks_.StoreRelaxed(new_backing); } else { size_t new_capacity = 2 * capacity_; - uintptr_t* new_backing = new uintptr_t[new_capacity]; + uintptr_t* new_backing = new uintptr_t[new_capacity](); uintptr_t* old_backing = monitor_chunks_.LoadRelaxed(); memcpy(new_backing, old_backing, sizeof(uintptr_t) * capacity_); monitor_chunks_.StoreRelaxed(new_backing); capacity_ = new_capacity; - old_chunk_arrays_.push_back(old_backing); + old_chunk_arrays_.push_back(std::unique_ptr(old_backing)); VLOG(monitor) << "Resizing to capacity " << capacity_; } } @@ -88,6 +89,25 @@ void MonitorPool::AllocateChunk() { first_free_ = last; } +void MonitorPool::FreeInternal() { + // This is on shutdown with NO_THREAD_SAFETY_ANALYSIS, can't/don't need to lock. + uintptr_t* backing = monitor_chunks_.LoadRelaxed(); + DCHECK(backing != nullptr); + DCHECK_GT(capacity_, 0U); + DCHECK_GT(num_chunks_, 0U); + + for (size_t i = 0; i < capacity_; ++i) { + if (i < num_chunks_) { + DCHECK_NE(backing[i], 0U); + allocator_.deallocate(reinterpret_cast(backing[i]), kChunkSize); + } else { + DCHECK_EQ(backing[i], 0U); + } + } + + delete[] backing; +} + Monitor* MonitorPool::CreateMonitorInPool(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_REQUIRES(Locks::mutator_lock_) { diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h index 240ca61641314419209f1fd42bb45d2d034446b2..875b3fe73d9d72223aff96847eaa5b7d9a92cdc5 100644 --- a/runtime/monitor_pool.h +++ b/runtime/monitor_pool.h @@ -104,6 +104,12 @@ class MonitorPool { #endif } + ~MonitorPool() { +#ifdef __LP64__ + FreeInternal(); +#endif + } + private: #ifdef __LP64__ // When we create a monitor pool, threads have not been initialized, yet, so ignore thread-safety @@ -112,6 +118,10 @@ class MonitorPool { void AllocateChunk() REQUIRES(Locks::allocated_monitor_ids_lock_); + // Release all chunks and metadata. This is done on shutdown, where threads have been destroyed, + // so ignore thead-safety analysis. + void FreeInternal() NO_THREAD_SAFETY_ANALYSIS; + Monitor* CreateMonitorInPool(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_REQUIRES(Locks::mutator_lock_); @@ -176,7 +186,8 @@ class MonitorPool { size_t capacity_ GUARDED_BY(Locks::allocated_monitor_ids_lock_); // To avoid race issues when resizing, we keep all the previous arrays. - std::vector old_chunk_arrays_ GUARDED_BY(Locks::allocated_monitor_ids_lock_); + std::vector> old_chunk_arrays_ + GUARDED_BY(Locks::allocated_monitor_ids_lock_); typedef TrackingAllocator Allocator; Allocator allocator_; diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc index 34d6a37ab2b346beceb2774426679616baf19394..5a219efc7bf89de8a160ec0707ba52b96fb686e0 100644 --- a/runtime/native/java_lang_StringFactory.cc +++ b/runtime/native/java_lang_StringFactory.cc @@ -50,8 +50,10 @@ static jstring StringFactory_newStringFromBytes(JNIEnv* env, jclass, jbyteArray return soa.AddLocalReference(result); } +// The char array passed as `java_data` must not be a null reference. static jstring StringFactory_newStringFromChars(JNIEnv* env, jclass, jint offset, jint char_count, jcharArray java_data) { + DCHECK(java_data != nullptr); ScopedFastNativeObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle char_array(hs.NewHandle(soa.Decode(java_data))); diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index 6ffd476edff3ab4f92f99b2ed24d71e63122a030..858849f98046cc43855f61efbe1f9492b8222bd4 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -26,6 +26,7 @@ #include #include #include +#include namespace art { @@ -473,6 +474,18 @@ static void Unsafe_putDouble(JNIEnv* env, jobject, jobject javaObj, jlong offset obj->SetField64(MemberOffset(offset), conv.converted); } +static void Unsafe_loadFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_acquire); +} + +static void Unsafe_storeFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_release); +} + +static void Unsafe_fullFence(JNIEnv*, jobject) { + std::atomic_thread_fence(std::memory_order_seq_cst); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"), NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"), @@ -532,6 +545,11 @@ static JNINativeMethod gMethods[] = { OVERLOADED_NATIVE_METHOD(Unsafe, putLong, "!(JJ)V", putLongJJ), OVERLOADED_NATIVE_METHOD(Unsafe, putFloat, "!(JF)V", putFloatJF), OVERLOADED_NATIVE_METHOD(Unsafe, putDouble, "!(JD)V", putDoubleJD), + + // CAS + NATIVE_METHOD(Unsafe, loadFence, "!()V"), + NATIVE_METHOD(Unsafe, storeFence, "!()V"), + NATIVE_METHOD(Unsafe, fullFence, "!()V"), }; void register_sun_misc_Unsafe(JNIEnv* env) { diff --git a/runtime/oat.cc b/runtime/oat.cc index 2ac105291d49208d0bc28ec55f909dea0cdd1238..ed99cbabbd3c32701faf0bbe22b6cd48fcadce70 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -468,6 +468,10 @@ bool OatHeader::IsDebuggable() const { return IsKeyEnabled(OatHeader::kDebuggableKey); } +bool OatHeader::IsNativeDebuggable() const { + return IsKeyEnabled(OatHeader::kNativeDebuggableKey); +} + bool OatHeader::IsExtractOnly() const { return KeyHasValue(kCompilationType, kExtractOnlyValue, diff --git a/runtime/oat.h b/runtime/oat.h index 0660e19ff4f1bc5059a745c1e7519d34fc738e5e..1d6c076c1b60b8af9786ca0ac17ceb810cea16c5 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,6 +38,7 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; + static constexpr const char* kNativeDebuggableKey = "native-debuggable"; static constexpr const char* kCompilationType = "compilation-type"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPath = "bootclasspath"; @@ -110,6 +111,7 @@ class PACKED(4) OatHeader { size_t GetHeaderSize() const; bool IsPic() const; bool IsDebuggable() const; + bool IsNativeDebuggable() const; bool IsExtractOnly() const; bool IsProfileGuideCompiled() const; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c0f5906fb429bb0efad2ca655ec8c57a4f227817..7155c79afb4d7d1c4af3864e7ffc48d6216df114 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -169,7 +169,10 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, return false; } if (requested_base != nullptr && begin_ != requested_base) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + // Host can fail this check. Do not dump there to avoid polluting the output. + if (kIsTargetBuild) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + } *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " "oatdata=%p != expected=%p. See process maps in the log.", begin_, requested_base); diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 9bf8410ca5fdedc1e62503c5799bc4a8ed1d1e06..1084253a88ae4e44d797d64e15b0525d5567ce39 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -230,6 +230,10 @@ class OatFile { return End() - Begin(); } + bool Contains(const void* p) const { + return p >= Begin() && p < End(); + } + size_t BssSize() const { return BssEnd() - BssBegin(); } diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index 2b7eca285909d5bf2f81c81af20023a150d6418a..daabc6ee09d96687e7b16a610f030419e834309d 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -63,16 +63,24 @@ class PACKED(4) OatQuickMethodHeader { return gc_map_offset_ == 0 && vmap_table_offset_ != 0; } - CodeInfo GetOptimizedCodeInfo() const { + const void* GetOptimizedCodeInfoPtr() const { DCHECK(IsOptimized()); const void* data = reinterpret_cast(code_ - vmap_table_offset_); - return CodeInfo(data); + return data; + } + + CodeInfo GetOptimizedCodeInfo() const { + return CodeInfo(GetOptimizedCodeInfoPtr()); } const uint8_t* GetCode() const { return code_; } + uint32_t GetCodeSize() const { + return code_size_; + } + const uint8_t* GetNativeGcMap() const { return (gc_map_offset_ == 0) ? nullptr : code_ - gc_map_offset_; } @@ -111,7 +119,7 @@ class PACKED(4) OatQuickMethodHeader { uint32_t GetFrameSizeInBytes() const { uint32_t result = frame_info_.FrameSizeInBytes(); if (kCheckFrameSize) { - DCHECK_LE(static_cast(kStackAlignment), result); + DCHECK_ALIGNED(result, kStackAlignment); } return result; } diff --git a/runtime/primitive.h b/runtime/primitive.h index ca42c4790c868fed9c0b1b85eeaffcd2ef5ac69e..2454a2117b939fea059dcba08628bf25f1ed6456 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -46,6 +46,7 @@ class Primitive { kPrimFloat, kPrimDouble, kPrimVoid, + kPrimLast = kPrimVoid }; static Type GetType(char type) { diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc index 9b10f2e0b8bf97bbf04929f09343445ff85b20ce..c7ccee2125d05c36922b584384f3832080c467ae 100644 --- a/runtime/quick/inline_method_analyser.cc +++ b/runtime/quick/inline_method_analyser.cc @@ -744,9 +744,12 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method, return false; } DCHECK_GE(field->GetOffset().Int32Value(), 0); + // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451. + uint32_t field_offset = field->GetOffset().Uint32Value(); + bool is_volatile = field->IsVolatile(); result->field_idx = field_idx; - result->field_offset = field->GetOffset().Int32Value(); - result->is_volatile = field->IsVolatile(); + result->field_offset = field_offset; + result->is_volatile = is_volatile ? 1u : 0u; return true; } diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 0b09a70be47890b2e1ea151db6dfaeef90897d95..0e12d735954d4138f29426c1594538dd11ee66ec 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -37,6 +37,8 @@ class MethodVerifier; enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, + kIntrinsicFloat2Int, + kIntrinsicDouble2Long, kIntrinsicFloatIsInfinite, kIntrinsicDoubleIsInfinite, kIntrinsicFloatIsNaN, @@ -99,6 +101,17 @@ enum InlineMethodOpcode : uint16_t { kIntrinsicCas, kIntrinsicUnsafeGet, kIntrinsicUnsafePut, + + // 1.8. + kIntrinsicUnsafeGetAndAddInt, + kIntrinsicUnsafeGetAndAddLong, + kIntrinsicUnsafeGetAndSetInt, + kIntrinsicUnsafeGetAndSetLong, + kIntrinsicUnsafeGetAndSetObject, + kIntrinsicUnsafeLoadFence, + kIntrinsicUnsafeStoreFence, + kIntrinsicUnsafeFullFence, + kIntrinsicSystemArrayCopyCharArray, kIntrinsicSystemArrayCopy, diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index 2dfa860dcb818bc48eb6162fa31761dcc8de2b36..6317f5e40147d27b89a6fd930fb29c677a12b80d 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -204,8 +204,7 @@ static VRegKind ToVRegKind(DexRegisterLocation::Kind kind) { return VRegKind::kDoubleHiVReg; default: - LOG(FATAL) << "Unexpected vreg location " - << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected vreg location " << kind; UNREACHABLE(); } } @@ -456,12 +455,11 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { } default: { LOG(FATAL) - << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor( - vreg_map.GetLocationInternalKind(vreg, - number_of_vregs, - code_info, - encoding)); + << "Unexpected location kind " + << vreg_map.GetLocationInternalKind(vreg, + number_of_vregs, + code_info, + encoding); UNREACHABLE(); } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b23bd66fb71946939ad7285959abc3c2fcec85d6..e95f2c539f3e4b1f64169001fb5955768e1d9f95 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -309,6 +309,7 @@ Runtime::~Runtime() { linear_alloc_.reset(); low_4gb_arena_pool_.reset(); arena_pool_.reset(); + jit_arena_pool_.reset(); MemMap::Shutdown(); // TODO: acquire a static mutex on Runtime to avoid racing. @@ -1009,10 +1010,13 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { // Use MemMap arena pool for jit, malloc otherwise. Malloc arenas are faster to allocate but // can't be trimmed as easily. const bool use_malloc = IsAotCompiler(); - arena_pool_.reset(new ArenaPool(use_malloc, false)); + arena_pool_.reset(new ArenaPool(use_malloc, /* low_4gb */ false)); + jit_arena_pool_.reset( + new ArenaPool(/* use_malloc */ false, /* low_4gb */ false, "CompilerMetadata")); + if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) { // 4gb, no malloc. Explanation in header. - low_4gb_arena_pool_.reset(new ArenaPool(false, true)); + low_4gb_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ true)); } linear_alloc_.reset(CreateLinearAlloc()); diff --git a/runtime/runtime.h b/runtime/runtime.h index ba75a8b8f8a10f574383063db7506dd2f1a50ca1..8e99f800e0b8ad2740eef66e2bf1db7f0077954d 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -564,6 +564,9 @@ class Runtime { ArenaPool* GetArenaPool() { return arena_pool_.get(); } + ArenaPool* GetJitArenaPool() { + return jit_arena_pool_.get(); + } const ArenaPool* GetArenaPool() const { return arena_pool_.get(); } @@ -672,6 +675,7 @@ class Runtime { gc::Heap* heap_; + std::unique_ptr jit_arena_pool_; std::unique_ptr arena_pool_; // Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are // compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc index 8237b06a5623b355b2735c171e4f855a91c0cf9b..bc963c5b8c7c3677175867d71045bcec86235cee 100644 --- a/runtime/runtime_linux.cc +++ b/runtime/runtime_linux.cc @@ -36,6 +36,7 @@ namespace art { static constexpr bool kDumpHeapObjectOnSigsevg = false; static constexpr bool kUseSigRTTimeout = true; +static constexpr bool kDumpNativeStackOnTimeout = true; struct Backtrace { public: @@ -350,7 +351,9 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex if (runtime != nullptr) { if (IsTimeoutSignal(signal_number)) { // Special timeout signal. Try to dump all threads. - runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL)); + // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts + // are of value here. + runtime->GetThreadList()->Dump(LOG(INTERNAL_FATAL), kDumpNativeStackOnTimeout); } gc::Heap* heap = runtime->GetHeap(); LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage(); diff --git a/runtime/safe_map.h b/runtime/safe_map.h index a8b48ee4dc3f2242c4f0a3950166ed2a992c8dbc..0e5b503e26e49c0606ee52c3be023ab6e34277b0 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/simulator/Android.mk b/runtime/simulator/Android.mk index c154eb6346078338121dbd4917f177b4b4f975bb..5c71da6255bd53057377a10576d627163b936684 100644 --- a/runtime/simulator/Android.mk +++ b/runtime/simulator/Android.mk @@ -86,7 +86,7 @@ define build-libart-simulator LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE) # For simulator_arm64. ifeq ($$(art_ndebug_or_debug),debug) - LOCAL_SHARED_LIBRARIES += libvixld + LOCAL_SHARED_LIBRARIES += libvixl else LOCAL_SHARED_LIBRARIES += libvixl endif diff --git a/runtime/stack.cc b/runtime/stack.cc index 34227349862bf81554170d82fbc96d5287851f9d..ee5da8e15041a38f53c6f7c6c00b721507503f3e 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -352,12 +352,11 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin return false; default: LOG(FATAL) - << "Unexpected location kind" - << DexRegisterLocation::PrettyDescriptor( - dex_register_map.GetLocationInternalKind(vreg, - number_of_dex_registers, - code_info, - encoding)); + << "Unexpected location kind " + << dex_register_map.GetLocationInternalKind(vreg, + number_of_dex_registers, + code_info, + encoding); UNREACHABLE(); } } diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 5544507c06f2e873e037cde199066a20e0b4a40a..30934360ace8626f29ef934cb6cc46828b08addc 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -27,6 +27,31 @@ constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex; constexpr uint32_t StackMap::kNoDexRegisterMap; constexpr uint32_t StackMap::kNoInlineInfo; +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind) { + using Kind = DexRegisterLocation::Kind; + switch (kind) { + case Kind::kNone: + return stream << "none"; + case Kind::kInStack: + return stream << "in stack"; + case Kind::kInRegister: + return stream << "in register"; + case Kind::kInRegisterHigh: + return stream << "in register high"; + case Kind::kInFpuRegister: + return stream << "in fpu register"; + case Kind::kInFpuRegisterHigh: + return stream << "in fpu register high"; + case Kind::kConstant: + return stream << "as constant"; + case Kind::kInStackLargeOffset: + return stream << "in stack (large offset)"; + case Kind::kConstantLargeValue: + return stream << "as constant (large value)"; + } + return stream << "Kind<" << static_cast(kind) << ">"; +} + DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind( uint16_t dex_register_number, uint16_t number_of_dex_registers, @@ -97,7 +122,7 @@ static void DumpRegisterMapping(std::ostream& os, const std::string& prefix = "v", const std::string& suffix = "") { os << prefix << dex_register_num << ": " - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()) + << location.GetInternalKind() << " (" << location.GetValue() << ")" << suffix << '\n'; } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 97eb805501f086927f90fa1bfa8032c50506781c..dbf23aafc3eea1a707cdddaa674bb92696181709 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -110,30 +110,6 @@ class DexRegisterLocation { sizeof(Kind) == 1u, "art::DexRegisterLocation::Kind has a size different from one byte."); - static const char* PrettyDescriptor(Kind kind) { - switch (kind) { - case Kind::kNone: - return "none"; - case Kind::kInStack: - return "in stack"; - case Kind::kInRegister: - return "in register"; - case Kind::kInRegisterHigh: - return "in register high"; - case Kind::kInFpuRegister: - return "in fpu register"; - case Kind::kInFpuRegisterHigh: - return "in fpu register high"; - case Kind::kConstant: - return "as constant"; - case Kind::kInStackLargeOffset: - return "in stack (large offset)"; - case Kind::kConstantLargeValue: - return "as constant (large value)"; - } - UNREACHABLE(); - } - static bool IsShortLocationKind(Kind kind) { switch (kind) { case Kind::kInStack: @@ -149,7 +125,7 @@ class DexRegisterLocation { return false; case Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind"; } UNREACHABLE(); } @@ -215,6 +191,8 @@ class DexRegisterLocation { friend class DexRegisterLocationHashFn; }; +std::ostream& operator<<(std::ostream& stream, const DexRegisterLocation::Kind& kind); + /** * Store information on unique Dex register locations used in a method. * The information is of the form: @@ -349,7 +327,7 @@ class DexRegisterLocationCatalog { case DexRegisterLocation::Kind::kConstantLargeValue: case DexRegisterLocation::Kind::kInStackLargeOffset: case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind " << kind; } UNREACHABLE(); } @@ -373,7 +351,7 @@ class DexRegisterLocationCatalog { case DexRegisterLocation::Kind::kConstantLargeValue: case DexRegisterLocation::Kind::kInStackLargeOffset: case DexRegisterLocation::Kind::kNone: - LOG(FATAL) << "Unexpected location kind " << DexRegisterLocation::PrettyDescriptor(kind); + LOG(FATAL) << "Unexpected location kind " << kind; } UNREACHABLE(); } @@ -515,8 +493,7 @@ class DexRegisterMap { const StackMapEncoding& enc) const { DexRegisterLocation location = GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info, enc); - DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant) - << DexRegisterLocation::PrettyDescriptor(location.GetKind()); + DCHECK_EQ(location.GetKind(), DexRegisterLocation::Kind::kConstant); return location.GetValue(); } @@ -530,7 +507,7 @@ class DexRegisterMap { location.GetInternalKind() == DexRegisterLocation::Kind::kInRegisterHigh || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegisterHigh) - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); + << location.GetInternalKind(); return location.GetValue(); } diff --git a/runtime/thread.h b/runtime/thread.h index 97c47e1490956ed4e8e9e69ecfa89055f831722a..234750cd9ebe3de131dc651b2878ade6aaf65d03 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1324,8 +1324,8 @@ class Thread { instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr), stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0), - last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr), - thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0), + last_no_thread_suspension_cause(nullptr), thread_local_objects(0), + thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr), @@ -1440,10 +1440,12 @@ class Thread { QuickEntryPoints quick_entrypoints; // Thread-local allocation pointer. + size_t thread_local_objects; uint8_t* thread_local_start; + // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for + // potentially better performance. uint8_t* thread_local_pos; uint8_t* thread_local_end; - size_t thread_local_objects; // Mterp jump table bases. void* mterp_current_ibase; diff --git a/runtime/trace.cc b/runtime/trace.cc index b9d6a4aa64b6d559fc73b77090a2810ba785ae74..b8793556d8d77fbd7e6b91d4aecd77a4d4895f1e 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -386,9 +386,10 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { bool stop_alloc_counting = false; Runtime* const runtime = Runtime::Current(); Trace* the_trace = nullptr; + Thread* const self = Thread::Current(); pthread_t sampling_pthread = 0U; { - MutexLock mu(Thread::Current(), *Locks::trace_lock_); + MutexLock mu(self, *Locks::trace_lock_); if (the_trace_ == nullptr) { LOG(ERROR) << "Trace stop requested, but no trace currently running"; } else { @@ -406,6 +407,9 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { } { + gc::ScopedGCCriticalSection gcs(self, + gc::kGcCauseInstrumentation, + gc::kCollectorTypeInstrumentation); ScopedSuspendAll ssa(__FUNCTION__); if (the_trace != nullptr) { stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; @@ -414,7 +418,7 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { } if (the_trace->trace_mode_ == TraceMode::kSampling) { - MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + MutexLock mu(self, *Locks::thread_list_lock_); runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); } else { runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); diff --git a/runtime/utf_test.cc b/runtime/utf_test.cc index c67879b427fb01de7f8450c9d542cc2fda953687..328492523f4d4f3c6a576f0dca721c24dfe9fa4b 100644 --- a/runtime/utf_test.cc +++ b/runtime/utf_test.cc @@ -312,8 +312,8 @@ static void codePointToSurrogatePair(uint32_t code_point, uint16_t &first, uint1 } static void testConversions(uint16_t *buf, int char_count) { - char bytes_test[8], bytes_reference[8]; - uint16_t out_buf_test[4], out_buf_reference[4]; + char bytes_test[8] = { 0 }, bytes_reference[8] = { 0 }; + uint16_t out_buf_test[4] = { 0 }, out_buf_reference[4] = { 0 }; int byte_count_test, byte_count_reference; int char_count_test, char_count_reference; @@ -349,7 +349,7 @@ static void testConversions(uint16_t *buf, int char_count) { TEST_F(UtfTest, ExhaustiveBidirectionalCodePointCheck) { for (int codePoint = 0; codePoint <= 0x10ffff; ++codePoint) { - uint16_t buf[4]; + uint16_t buf[4] = { 0 }; if (codePoint <= 0xffff) { if (codePoint >= 0xd800 && codePoint <= 0xdfff) { // According to the Unicode standard, no character will ever diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 30f613c3892dbdbe4aeb00d22cd8629fc1d5b2ab..b171b75e97c6d8f715805797139c94e85045cb25 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -17,6 +17,7 @@ #include "reg_type_cache-inl.h" #include "base/arena_bit_vector.h" +#include "base/bit_vector-inl.h" #include "base/casts.h" #include "base/scoped_arena_allocator.h" #include "base/stl_util.h" @@ -351,9 +352,11 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegT types.Copy(&left_merge->GetUnresolvedTypes()); left_resolved = &left_merge->GetResolvedPart(); } else if (left.IsUnresolvedTypes()) { + types.ClearAllBits(); types.SetBit(left.GetId()); left_resolved = &Zero(); } else { + types.ClearAllBits(); left_resolved = &left; } diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 22ac7e4ab264499117fad22f52c05e8943205bc3..42a74f88e187cb2c0ad990f1682440b8a4529a0b 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -30,23 +30,14 @@ namespace art { namespace verifier { -class BaseRegTypeTest : public CommonRuntimeTest { - public: - void PostRuntimeCreate() OVERRIDE { - stack.reset(new ArenaStack(Runtime::Current()->GetArenaPool())); - allocator.reset(new ScopedArenaAllocator(stack.get())); - } - - std::unique_ptr stack; - std::unique_ptr allocator; -}; - -class RegTypeTest : public BaseRegTypeTest {}; +class RegTypeTest : public CommonRuntimeTest {}; TEST_F(RegTypeTest, ConstLoHi) { // Tests creating primitive types types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_const_0 = cache.FromCat1Const(10, true); const RegType& ref_type_const_1 = cache.FromCat1Const(10, true); const RegType& ref_type_const_2 = cache.FromCat1Const(30, true); @@ -67,8 +58,10 @@ TEST_F(RegTypeTest, ConstLoHi) { } TEST_F(RegTypeTest, Pairs) { + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); int64_t val = static_cast(1234); const RegType& precise_lo = cache.FromCat2ConstLo(static_cast(val), true); const RegType& precise_hi = cache.FromCat2ConstHi(static_cast(val >> 32), true); @@ -91,8 +84,10 @@ TEST_F(RegTypeTest, Pairs) { } TEST_F(RegTypeTest, Primitives) { + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& bool_reg_type = cache.Boolean(); EXPECT_FALSE(bool_reg_type.IsUndefined()); @@ -359,13 +354,15 @@ TEST_F(RegTypeTest, Primitives) { EXPECT_TRUE(double_reg_type.HasClass()); } -class RegTypeReferenceTest : public BaseRegTypeTest {}; +class RegTypeReferenceTest : public CommonRuntimeTest {}; TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { // Tests matching precisions. A reference type that was created precise doesn't // match the one that is imprecise. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& imprecise_obj = cache.JavaLangObject(false); const RegType& precise_obj = cache.JavaLangObject(true); const RegType& precise_obj_2 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); @@ -379,8 +376,10 @@ TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) { TEST_F(RegTypeReferenceTest, UnresolvedType) { // Tests creating unresolved types. Miss for the first time asking the cache and // a hit second time. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes()); @@ -395,8 +394,10 @@ TEST_F(RegTypeReferenceTest, UnresolvedType) { TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { // Tests creating types uninitialized types from unresolved types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type_0 = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); const RegType& ref_type = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); @@ -417,8 +418,10 @@ TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { TEST_F(RegTypeReferenceTest, Dump) { // Tests types for proper Dump messages. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& unresolved_ref = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExist;", true); const RegType& unresolved_ref_another = cache.FromDescriptor(nullptr, "Ljava/lang/DoesNotExistEither;", true); const RegType& resolved_ref = cache.JavaLangString(); @@ -442,8 +445,10 @@ TEST_F(RegTypeReferenceTest, JavalangString) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then check for the same effect when using // The JavaLangObject method instead of FromDescriptor. String class is final. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type = cache.JavaLangString(); const RegType& ref_type_2 = cache.JavaLangString(); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/String;", true); @@ -462,8 +467,10 @@ TEST_F(RegTypeReferenceTest, JavalangObject) { // Add a class to the cache then look for the same class and make sure it is a // Hit the second time. Then I am checking for the same effect when using // The JavaLangObject method instead of FromDescriptor. Object Class in not final. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(true, *allocator); + RegTypeCache cache(true, allocator); const RegType& ref_type = cache.JavaLangObject(true); const RegType& ref_type_2 = cache.JavaLangObject(true); const RegType& ref_type_3 = cache.FromDescriptor(nullptr, "Ljava/lang/Object;", true); @@ -476,7 +483,9 @@ TEST_F(RegTypeReferenceTest, Merging) { // Tests merging logic // String and object , LUB is object. ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); + RegTypeCache cache_new(true, allocator); const RegType& string = cache_new.JavaLangString(); const RegType& Object = cache_new.JavaLangObject(true); EXPECT_TRUE(string.Merge(Object, &cache_new).IsJavaLangObject()); @@ -498,8 +507,10 @@ TEST_F(RegTypeReferenceTest, Merging) { TEST_F(RegTypeTest, MergingFloat) { // Testing merging logic with float and float constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& float_type = cache_new.Float(); @@ -529,8 +540,10 @@ TEST_F(RegTypeTest, MergingFloat) { TEST_F(RegTypeTest, MergingLong) { // Testing merging logic with long and long constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& long_lo_type = cache_new.LongLo(); @@ -583,8 +596,10 @@ TEST_F(RegTypeTest, MergingLong) { TEST_F(RegTypeTest, MergingDouble) { // Testing merging logic with double and double constants. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); constexpr int32_t kTestConstantValue = 10; const RegType& double_lo_type = cache_new.DoubleLo(); @@ -637,8 +652,10 @@ TEST_F(RegTypeTest, MergingDouble) { TEST_F(RegTypeTest, ConstPrecision) { // Tests creating primitive types types. + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(true, *allocator); + RegTypeCache cache_new(true, allocator); const RegType& imprecise_const = cache_new.FromCat1Const(10, false); const RegType& precise_const = cache_new.FromCat1Const(10, true); diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java index a9a7a058e0710ed0beede9830f62dc4b6fcbf11a..b2f905e0eec5855ce65f58cbf0d22af1f69f3508 100644 --- a/test/004-UnsafeTest/src/Main.java +++ b/test/004-UnsafeTest/src/Main.java @@ -40,7 +40,7 @@ public class Main { } private static Unsafe getUnsafe() throws Exception { - Class unsafeClass = Class.forName("sun.misc.Unsafe"); + Class unsafeClass = Unsafe.class; Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); diff --git a/test/004-checker-UnsafeTest18/expected.txt b/test/004-checker-UnsafeTest18/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..651da727af3887e1169042fea8f32ed901d56060 --- /dev/null +++ b/test/004-checker-UnsafeTest18/expected.txt @@ -0,0 +1,2 @@ +starting +passed diff --git a/test/004-checker-UnsafeTest18/info.txt b/test/004-checker-UnsafeTest18/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..0fca5ebf03cc34508e273e05ae29d2efdbf2ea64 --- /dev/null +++ b/test/004-checker-UnsafeTest18/info.txt @@ -0,0 +1 @@ +Test support for 1.8 sun.misc.Unsafe. diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..bb020b9b9f6bda1724e478202aabd6c29d3d0ce2 --- /dev/null +++ b/test/004-checker-UnsafeTest18/src/Main.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Field; + +import sun.misc.Unsafe; + +/** + * Checker test on the 1.8 unsafe operations. Note, this is by no means an + * exhaustive unit test for these CAS (compare-and-swap) and fence operations. + * Instead, this test ensures the methods are recognized as intrinsic and behave + * as expected. + */ +public class Main { + + private static final Unsafe unsafe = getUnsafe(); + + private static Thread[] sThreads = new Thread[10]; + + // + // Fields accessed by setters and adders. + // + + public int i = 0; + public long l = 0; + public Object o = null; + + // + // Setters. + // + + /// CHECK-START: int Main.set32(java.lang.Object, long, int) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndSetInt + /// CHECK-DAG: Return [<>] + private static int set32(Object o, long offset, int newValue) { + return unsafe.getAndSetInt(o, offset, newValue); + } + + /// CHECK-START: long Main.set64(java.lang.Object, long, long) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndSetLong + /// CHECK-DAG: Return [<>] + private static long set64(Object o, long offset, long newValue) { + return unsafe.getAndSetLong(o, offset, newValue); + } + + /// CHECK-START: java.lang.Object Main.setObj(java.lang.Object, long, java.lang.Object) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndSetObject + /// CHECK-DAG: Return [<>] + private static Object setObj(Object o, long offset, Object newValue) { + return unsafe.getAndSetObject(o, offset, newValue); + } + + // + // Adders. + // + + /// CHECK-START: int Main.add32(java.lang.Object, long, int) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndAddInt + /// CHECK-DAG: Return [<>] + private static int add32(Object o, long offset, int delta) { + return unsafe.getAndAddInt(o, offset, delta); + } + + /// CHECK-START: long Main.add64(java.lang.Object, long, long) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeVirtual intrinsic:UnsafeGetAndAddLong + /// CHECK-DAG: Return [<>] + private static long add64(Object o, long offset, long delta) { + return unsafe.getAndAddLong(o, offset, delta); + } + + // + // Fences (native). + // + + /// CHECK-START: void Main.load() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence + // + /// CHECK-START: void Main.load() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence + // + /// CHECK-START: void Main.load() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:LoadAny + private static void load() { + unsafe.loadFence(); + } + + /// CHECK-START: void Main.store() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence + // + /// CHECK-START: void Main.store() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence + // + /// CHECK-START: void Main.store() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:AnyStore + private static void store() { + unsafe.storeFence(); + } + + /// CHECK-START: void Main.full() intrinsics_recognition (after) + /// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence + // + /// CHECK-START: void Main.full() instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence + // + /// CHECK-START: void Main.full() instruction_simplifier (after) + /// CHECK-DAG: MemoryBarrier kind:AnyAny + private static void full() { + unsafe.fullFence(); + } + + // + // Thread fork/join. + // + + private static void fork(Runnable r) { + for (int i = 0; i < 10; i++) { + sThreads[i] = new Thread(r); + sThreads[i].start(); + } + } + + private static void join() { + try { + for (int i = 0; i < 10; i++) { + sThreads[i].join(); + } + } catch (InterruptedException e) { + throw new Error("Failed join: " + e); + } + } + + // + // Driver. + // + + public static void main(String[] args) { + System.out.println("starting"); + + final Main m = new Main(); + + // Get the offsets. + + final long intOffset, longOffset, objOffset; + try { + Field intField = Main.class.getDeclaredField("i"); + Field longField = Main.class.getDeclaredField("l"); + Field objField = Main.class.getDeclaredField("o"); + + intOffset = unsafe.objectFieldOffset(intField); + longOffset = unsafe.objectFieldOffset(longField); + objOffset = unsafe.objectFieldOffset(objField); + + } catch (NoSuchFieldException e) { + throw new Error("No offset: " + e); + } + + // Some sanity within same thread. + + set32(m, intOffset, 3); + expectEquals32(3, m.i); + + set64(m, longOffset, 7L); + expectEquals64(7L, m.l); + + setObj(m, objOffset, m); + expectEqualsObj(m, m.o); + + add32(m, intOffset, 11); + expectEquals32(14, m.i); + + add64(m, longOffset, 13L); + expectEquals64(20L, m.l); + + // Some sanity on setters within different threads. + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + set32(m, intOffset, i); + } + }); + join(); + expectEquals32(9, m.i); // one thread's last value wins + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + set64(m, longOffset, (long) (100 + i)); + } + }); + join(); + expectEquals64(109L, m.l); // one thread's last value wins + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + setObj(m, objOffset, sThreads[i]); + } + }); + join(); + expectEqualsObj(sThreads[9], m.o); // one thread's last value wins + + // Some sanity on adders within different threads. + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + add32(m, intOffset, i + 1); + } + }); + join(); + expectEquals32(559, m.i); // all values accounted for + + fork(new Runnable() { + public void run() { + for (int i = 0; i < 10; i++) + add64(m, longOffset, (long) (i + 1)); + } + }); + join(); + expectEquals64(659L, m.l); // all values accounted for + + // TODO: the fences + + System.out.println("passed"); + } + + // Use reflection to implement "Unsafe.getUnsafe()"; + private static Unsafe getUnsafe() { + try { + Class unsafeClass = Unsafe.class; + Field f = unsafeClass.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } catch (Exception e) { + throw new Error("Cannot get Unsafe instance"); + } + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEqualsObj(Object expected, Object result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/011-array-copy/src/Main.java b/test/011-array-copy/src/Main.java index 96e1dbf21a7f585e04dd2106131087f6b74f95aa..d9b61e7acf962edba76f83cf22fa7bb86dd71142 100644 --- a/test/011-array-copy/src/Main.java +++ b/test/011-array-copy/src/Main.java @@ -69,6 +69,11 @@ public class Main { array[i] = (long) i; } } + static void initCharArray(char[] array) { + for (int i = 0; i < ARRAY_SIZE; i++) { + array[i] = (char) i; + } + } /* * Perform an array copy operation on primitive arrays with different @@ -79,16 +84,19 @@ public class Main { short[] shortArray = new short[ARRAY_SIZE]; int[] intArray = new int[ARRAY_SIZE]; long[] longArray = new long[ARRAY_SIZE]; + char[] charArray = new char[ARRAY_SIZE]; initByteArray(byteArray); initShortArray(shortArray); initIntArray(intArray); initLongArray(longArray); + initCharArray(charArray); System.arraycopy(byteArray, srcPos, byteArray, dstPos, length); System.arraycopy(shortArray, srcPos, shortArray, dstPos, length); System.arraycopy(intArray, srcPos, intArray, dstPos, length); System.arraycopy(longArray, srcPos, longArray, dstPos, length); + System.arraycopy(charArray, srcPos, charArray, dstPos, length); for (int i = 0; i < ARRAY_SIZE; i++) { if (intArray[i] != byteArray[i]) { @@ -103,6 +111,10 @@ public class Main { System.out.println("mismatch int vs long at " + i + " : " + Arrays.toString(longArray)); break; + } else if (intArray[i] != charArray[i]) { + System.out.println("mismatch int vs char at " + i + " : " + + Arrays.toString(charArray)); + break; } } diff --git a/test/020-string/expected.txt b/test/020-string/expected.txt index 081fea3a414f4c81dbe080c9c2f1b901afaf40dc..76b8929bd758a531eee772f8345411f40abc4b1f 100644 --- a/test/020-string/expected.txt +++ b/test/020-string/expected.txt @@ -5,3 +5,9 @@ Compare unicode: -65302 Got expected exception subStr is 'uick brown fox jumps over the lazy ' Indexes are: 0:-1:0:43:33:-1:18:13:13:-1:18:18:-1:13:-1:-1:-1 +Got expected exception +Got expected exception +Got expected exception +Got expected exception +Got expected exception +llo And diff --git a/test/020-string/src/Main.java b/test/020-string/src/Main.java index b876e6ad2145740657395a2b0c73dbdc7c99a3b9..710808255c806d7bea57f16246cca2aa5ddcd69b 100644 --- a/test/020-string/src/Main.java +++ b/test/020-string/src/Main.java @@ -25,6 +25,7 @@ public class Main { basicTest(); indexTest(); constructorTest(); + copyTest(); } public static void basicTest() { @@ -117,4 +118,48 @@ public class Main { String s14 = new String(codePoints, 1, 3); String s15 = new String(stringBuilder); } + + public static void copyTest() { + String src = new String("Hello Android"); + char[] dst = new char[7]; + char[] tmp = null; + + try { + src.getChars(2, 9, tmp, 0); + System.out.println("GLITCH: expected exception"); + } catch (NullPointerException npe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(-1, 9, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 19, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 1, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (StringIndexOutOfBoundsException sioobe) { + System.out.println("Got expected exception"); + } + + try { + src.getChars(2, 10, dst, 0); + System.out.println("GLITCH: expected exception"); + } catch (ArrayIndexOutOfBoundsException aioobe) { + System.out.println("Got expected exception"); + } + + src.getChars(2, 9, dst, 0); + System.out.println(new String(dst)); + } } diff --git a/test/048-reflect-v8/build b/test/048-reflect-v8/build index 4ea18384654d666334dfdb0df372566b568d6689..3552b5c46c2fcd6dc4ac84efcf601d111c16bba9 100644 --- a/test/048-reflect-v8/build +++ b/test/048-reflect-v8/build @@ -20,9 +20,5 @@ set -e # Hard-wired use of experimental jack. # TODO: fix this temporary work-around for lambdas, see b/19467889 export USE_JACK=true -export JACK_SERVER=false -export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" -# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA -export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" ./default-build "$@" --experimental default-methods diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java index e5c9dba63f913929366a8d512197cf717b95d3dd..9aaed9d589ad8b5f80440ea9331b109270a7cf6d 100644 --- a/test/082-inline-execute/src/Main.java +++ b/test/082-inline-execute/src/Main.java @@ -40,6 +40,10 @@ public class Main { test_Math_rint(); test_Math_round_D(); test_Math_round_F(); + test_Math_isNaN_D(); + test_Math_isNaN_F(); + test_Math_isInfinite_D(); + test_Math_isInfinite_F(); test_Short_reverseBytes(); test_Integer_reverseBytes(); test_Long_reverseBytes(); @@ -826,6 +830,8 @@ 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); @@ -834,6 +840,106 @@ public class Main { Assert.assertEquals(Math.round(Float.NEGATIVE_INFINITY), Integer.MIN_VALUE); } + public static void test_Math_isNaN_D() { + // Quiet NaN. + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0x7FF4000000000000l))); + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0xFFF4000000000000l))); + // Signaling NaN. + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0x7FF8000000000000l))); + Assert.assertTrue(Double.isNaN(Double.longBitsToDouble(0xFFF8000000000000l))); + // Distinct from +/- infinity. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x7FF0000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0xFFF0000000000000l))); + // Distinct from normal numbers. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x7FE0000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0xFFE0000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0010000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8010000000000000l))); + // Distinct from +/- zero. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0000000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8000000000000000l))); + // Distinct from subnormal numbers. + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0008000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8008000000000000l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x0000000000000001l))); + Assert.assertFalse(Double.isNaN(Double.longBitsToDouble(0x8000000000000001l))); + } + + public static void test_Math_isNaN_F() { + // Quiet NaN. + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0x7FA00000))); + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0xFFA00000))); + // Signaling NaN. + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0x7FC00000))); + Assert.assertTrue(Float.isNaN(Float.intBitsToFloat(0xFFC00000))); + // Distinct from +/- infinity. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x7F800000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0xFF800000))); + // Distinct from normal numbers. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x7F000000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0xFF000000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00800000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80800000))); + // Distinct from +/- zero. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00000000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80000000))); + // Distinct from subnormal numbers. + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00400000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80400000))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x00000001))); + Assert.assertFalse(Float.isNaN(Float.intBitsToFloat(0x80000001))); + } + + public static void test_Math_isInfinite_D() { + // Distinct from Quiet NaN. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x7FF4000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0xFFF4000000000000l))); + // Distinct from Signaling NaN. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x7FF8000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0xFFF8000000000000l))); + // +/- infinity. + Assert.assertTrue(Double.isInfinite(Double.longBitsToDouble(0x7FF0000000000000l))); + Assert.assertTrue(Double.isInfinite(Double.longBitsToDouble(0xFFF0000000000000l))); + // Distinct from normal numbers. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x7FE0000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0xFFE0000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0010000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8010000000000000l))); + // Distinct from +/- zero. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0000000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8000000000000000l))); + // Distinct from subnormal numbers. + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0008000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8008000000000000l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x0000000000000001l))); + Assert.assertFalse(Double.isInfinite(Double.longBitsToDouble(0x8000000000000001l))); + } + + public static void test_Math_isInfinite_F() { + // Distinct from Quiet NaN. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x7FA00000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0xFFA00000))); + // Distinct from Signaling NaN. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x7FC00000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0xFFC00000))); + // +/- infinity. + Assert.assertTrue(Float.isInfinite(Float.intBitsToFloat(0x7F800000))); + Assert.assertTrue(Float.isInfinite(Float.intBitsToFloat(0xFF800000))); + // Distinct from normal numbers. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x7F000000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0xFF000000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00800000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80800000))); + // Distinct from +/- zero. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00000000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80000000))); + // Distinct from subnormal numbers. + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00400000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80400000))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x00000001))); + Assert.assertFalse(Float.isInfinite(Float.intBitsToFloat(0x80000001))); + } + public static void test_StrictMath_abs_I() { StrictMath.abs(-1); Assert.assertEquals(StrictMath.abs(0), 0); @@ -1037,6 +1143,7 @@ public class Main { Assert.assertEquals(StrictMath.round(-2.9d), -3l); Assert.assertEquals(StrictMath.round(-3.0d), -3l); Assert.assertEquals(StrictMath.round(0.49999999999999994d), 0l); + Assert.assertEquals(StrictMath.round(9007199254740991.0d), 9007199254740991l); // 2^53 - 1 Assert.assertEquals(StrictMath.round(Double.NaN), (long)+0.0d); Assert.assertEquals(StrictMath.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE); Assert.assertEquals(StrictMath.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE); @@ -1058,6 +1165,9 @@ 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(16777215.0f), 16777215); // 2^24 - 1 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/137-cfi/expected.txt b/test/137-cfi/expected.txt index 6a5618ebc67de22d67fae3dfcd84e929be352659..8db7853696207b7358d3d5a4256e0b80d1a0e8e4 100644 --- a/test/137-cfi/expected.txt +++ b/test/137-cfi/expected.txt @@ -1 +1,2 @@ JNI_OnLoad called +JNI_OnLoad called diff --git a/test/137-cfi/run b/test/137-cfi/run index 8ec98c11dc96db89b1da3901646adc932ffd6612..ebc729bc7435f55919fd37ea6fd5b8ca05755b3f 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -16,9 +16,10 @@ # Test with full DWARF debugging information. # Check full signatures of methods. -${RUN} "$@" -Xcompiler-option --generate-debug-info --args --full-signatures +${RUN} "$@" -Xcompiler-option --generate-debug-info \ + --args --full-signatures --args --test-local --args --test-remote # Test with minimal compressed debugging information. # Check only method names (parameters are omitted to save space). -# Temporarily disable due to bug 27172087 (leak/race in libunwind). -# ${RUN} "$@" -Xcompiler-option --generate-mini-debug-info +# Check only remote unwinding since decompression is disabled in local unwinds (b/27391690). +${RUN} "$@" -Xcompiler-option --generate-mini-debug-info --args --test-remote diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java index d60a4ebba821f0576888a7ef1fa6ec14ce222a38..5cfe33dc590d0cecfdf442441206b11e1579d435 100644 --- a/test/137-cfi/src/Main.java +++ b/test/137-cfi/src/Main.java @@ -21,43 +21,48 @@ import java.util.Arrays; import java.util.Comparator; public class Main implements Comparator
{ - // Whether to test local unwinding. Libunwind uses linker info to find executables. As we do - // not dlopen at the moment, this doesn't work, so keep it off for now. - public final static boolean TEST_LOCAL_UNWINDING = true; + // Whether to test local unwinding. + private boolean testLocal; - // Unwinding another process, modelling debuggerd. This doesn't use the linker, so should work - // no matter whether we're using dlopen or not. - public final static boolean TEST_REMOTE_UNWINDING = true; + // Unwinding another process, modelling debuggerd. + private boolean testRemote; + // We fork ourself to create the secondary process for remote unwinding. private boolean secondary; - private boolean full_signatures; + // Expect the symbols to contain full method signatures including parameters. + private boolean fullSignatures; private boolean passed; - public Main(boolean secondary, boolean full_signatures) { - this.secondary = secondary; - this.full_signatures = full_signatures; + public Main(String[] args) throws Exception { + System.loadLibrary(args[0]); + for (String arg : args) { + if (arg.equals("--test-local")) { + testLocal = true; + } + if (arg.equals("--test-remote")) { + testRemote = true; + } + if (arg.equals("--secondary")) { + secondary = true; + } + if (arg.equals("--full-signatures")) { + fullSignatures = true; + } + } + if (!testLocal && !testRemote) { + System.out.println("No test selected."); + } } public static void main(String[] args) throws Exception { - System.loadLibrary(args[0]); - boolean secondary = false; - boolean full_signatures = false; - for (String arg : args) { - if (arg.equals("--secondary")) { - secondary = true; - } - if (arg.equals("--full-signatures")) { - full_signatures = true; - } - } - new Main(secondary, full_signatures).run(); + new Main(args).run(); } private void run() { if (secondary) { - if (!TEST_REMOTE_UNWINDING) { + if (!testRemote) { throw new RuntimeException("Should not be running secondary!"); } runSecondary(); @@ -73,11 +78,11 @@ public class Main implements Comparator
{ private void runPrimary() { // First do the in-process unwinding. - if (TEST_LOCAL_UNWINDING && !foo()) { + if (testLocal && !foo()) { System.out.println("Unwinding self failed."); } - if (!TEST_REMOTE_UNWINDING) { + if (!testRemote) { // Skip the remote step. return; } @@ -105,7 +110,7 @@ public class Main implements Comparator
{ throw new RuntimeException(e); } - if (!unwindOtherProcess(full_signatures, pid)) { + if (!unwindOtherProcess(fullSignatures, pid)) { System.out.println("Unwinding other process failed."); } } finally { @@ -163,7 +168,7 @@ public class Main implements Comparator
{ if (b) { return sleep(2, b, 1.0); } else { - return unwindInProcess(full_signatures, 1, b); + return unwindInProcess(fullSignatures, 1, b); } } @@ -171,6 +176,6 @@ public class Main implements Comparator
{ public native boolean sleep(int i, boolean b, double dummy); - public native boolean unwindInProcess(boolean full_signatures, int i, boolean b); - public native boolean unwindOtherProcess(boolean full_signatures, int pid); + public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b); + public native boolean unwindOtherProcess(boolean fullSignatures, int pid); } diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java index bcb697a396863b14866976f81e9dcea0d0fa4dbf..15683b0b1ef199ff504aa47ec2e23bee361672ce 100644 --- a/test/141-class-unload/src/Main.java +++ b/test/141-class-unload/src/Main.java @@ -181,6 +181,7 @@ public class Main { Class intHolder = loader.loadClass("IntHolder"); Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class); loadLibrary.invoke(intHolder, nativeLibraryName); + waitForCompilation(intHolder); return new WeakReference(loader); } diff --git a/test/145-alloc-tracking-stress/src/Main.java b/test/145-alloc-tracking-stress/src/Main.java index 752fdd91350e1417f13e8c302ca77ec23c85adb6..418690a2a6ffc10e620b509f8edc6f26a4b04c80 100644 --- a/test/145-alloc-tracking-stress/src/Main.java +++ b/test/145-alloc-tracking-stress/src/Main.java @@ -1,5 +1,4 @@ /* - * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java index 93fe397273435249b6c5ce29cf7f0a96565a5e9c..b7712a701ffb9119250640e50443ae6627ce2da1 100644 --- a/test/442-checker-constant-folding/src/Main.java +++ b/test/442-checker-constant-folding/src/Main.java @@ -51,6 +51,21 @@ public class Main { } } + private static int $inline$int(int x) { + return x; + } + + private static long $inline$long(long x) { + return x; + } + + private static float $inline$float(float x) { + return x; + } + + private static double $inline$double(double x) { + return x; + } // Wrappers around methods located in file TestCmp.smali. @@ -194,121 +209,119 @@ public class Main { return y; } - /** * Exercise constant folding on addition. */ - /// CHECK-START: int Main.IntAddition1() constant_folding (before) + /// CHECK-START: int Main.IntAddition1() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition1() constant_folding (after) + /// CHECK-START: int Main.IntAddition1() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition1() constant_folding (after) + /// CHECK-START: int Main.IntAddition1() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static int IntAddition1() { int a, b, c; - a = 1; - b = 2; + a = $inline$int(1); + b = $inline$int(2); c = a + b; return c; } - /// CHECK-START: int Main.IntAddition2() constant_folding (before) + /// CHECK-START: int Main.IntAddition2() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> IntConstant 6 - /// CHECK-DAG: <> IntConstant 11 /// CHECK-DAG: <> Add [<>,<>] - /// CHECK-DAG: Add [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition2() constant_folding (after) + /// CHECK-START: int Main.IntAddition2() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 14 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntAddition2() constant_folding (after) + /// CHECK-START: int Main.IntAddition2() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static int IntAddition2() { int a, b, c; - a = 1; - b = 2; + a = $inline$int(1); + b = $inline$int(2); a += b; - b = 5; - c = 6; + b = $inline$int(5); + c = $inline$int(6); b += c; c = a + b; return c; } - /// CHECK-START: long Main.LongAddition() constant_folding (before) + /// CHECK-START: long Main.LongAddition() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 1 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongAddition() constant_folding (after) + /// CHECK-START: long Main.LongAddition() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongAddition() constant_folding (after) + /// CHECK-START: long Main.LongAddition() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static long LongAddition() { long a, b, c; - a = 1L; - b = 2L; + a = $inline$long(1L); + b = $inline$long(2L); c = a + b; return c; } - /// CHECK-START: float Main.FloatAddition() constant_folding (before) + /// CHECK-START: float Main.FloatAddition() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 1 /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-START: float Main.FloatAddition() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatAddition() constant_folding (after) + /// CHECK-START: float Main.FloatAddition() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static float FloatAddition() { float a, b, c; - a = 1F; - b = 2F; + a = $inline$float(1F); + b = $inline$float(2F); c = a + b; return c; } - /// CHECK-START: double Main.DoubleAddition() constant_folding (before) + /// CHECK-START: double Main.DoubleAddition() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 1 /// CHECK-DAG: <> DoubleConstant 2 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-START: double Main.DoubleAddition() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 3 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleAddition() constant_folding (after) + /// CHECK-START: double Main.DoubleAddition() constant_folding_after_inlining (after) /// CHECK-NOT: Add public static double DoubleAddition() { double a, b, c; - a = 1D; - b = 2D; + a = $inline$double(1D); + b = $inline$double(2D); c = a + b; return c; } @@ -318,86 +331,86 @@ public class Main { * Exercise constant folding on subtraction. */ - /// CHECK-START: int Main.IntSubtraction() constant_folding (before) + /// CHECK-START: int Main.IntSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 6 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntSubtraction() constant_folding (after) + /// CHECK-START: int Main.IntSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntSubtraction() constant_folding (after) + /// CHECK-START: int Main.IntSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static int IntSubtraction() { int a, b, c; - a = 6; - b = 2; + a = $inline$int(6); + b = $inline$int(2); c = a - b; return c; } - /// CHECK-START: long Main.LongSubtraction() constant_folding (before) + /// CHECK-START: long Main.LongSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 6 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongSubtraction() constant_folding (after) + /// CHECK-START: long Main.LongSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongSubtraction() constant_folding (after) + /// CHECK-START: long Main.LongSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static long LongSubtraction() { long a, b, c; - a = 6L; - b = 2L; + a = $inline$long(6L); + b = $inline$long(2L); c = a - b; return c; } - /// CHECK-START: float Main.FloatSubtraction() constant_folding (before) + /// CHECK-START: float Main.FloatSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 6 /// CHECK-DAG: <> FloatConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-START: float Main.FloatSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatSubtraction() constant_folding (after) + /// CHECK-START: float Main.FloatSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static float FloatSubtraction() { float a, b, c; - a = 6F; - b = 2F; + a = $inline$float(6F); + b = $inline$float(2F); c = a - b; return c; } - /// CHECK-START: double Main.DoubleSubtraction() constant_folding (before) + /// CHECK-START: double Main.DoubleSubtraction() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 6 /// CHECK-DAG: <> DoubleConstant 2 /// CHECK-DAG: <> Sub [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-START: double Main.DoubleSubtraction() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after) + /// CHECK-START: double Main.DoubleSubtraction() constant_folding_after_inlining (after) /// CHECK-NOT: Sub public static double DoubleSubtraction() { double a, b, c; - a = 6D; - b = 2D; + a = $inline$double(6D); + b = $inline$double(2D); c = a - b; return c; } @@ -407,86 +420,86 @@ public class Main { * Exercise constant folding on multiplication. */ - /// CHECK-START: int Main.IntMultiplication() constant_folding (before) + /// CHECK-START: int Main.IntMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 7 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntMultiplication() constant_folding (after) + /// CHECK-START: int Main.IntMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntMultiplication() constant_folding (after) + /// CHECK-START: int Main.IntMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static int IntMultiplication() { int a, b, c; - a = 7; - b = 3; + a = $inline$int(7); + b = $inline$int(3); c = a * b; return c; } - /// CHECK-START: long Main.LongMultiplication() constant_folding (before) + /// CHECK-START: long Main.LongMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 7 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongMultiplication() constant_folding (after) + /// CHECK-START: long Main.LongMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongMultiplication() constant_folding (after) + /// CHECK-START: long Main.LongMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static long LongMultiplication() { long a, b, c; - a = 7L; - b = 3L; + a = $inline$long(7L); + b = $inline$long(3L); c = a * b; return c; } - /// CHECK-START: float Main.FloatMultiplication() constant_folding (before) + /// CHECK-START: float Main.FloatMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 7 /// CHECK-DAG: <> FloatConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-START: float Main.FloatMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatMultiplication() constant_folding (after) + /// CHECK-START: float Main.FloatMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static float FloatMultiplication() { float a, b, c; - a = 7F; - b = 3F; + a = $inline$float(7F); + b = $inline$float(3F); c = a * b; return c; } - /// CHECK-START: double Main.DoubleMultiplication() constant_folding (before) + /// CHECK-START: double Main.DoubleMultiplication() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 7 /// CHECK-DAG: <> DoubleConstant 3 /// CHECK-DAG: <> Mul [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-START: double Main.DoubleMultiplication() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 21 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after) + /// CHECK-START: double Main.DoubleMultiplication() constant_folding_after_inlining (after) /// CHECK-NOT: Mul public static double DoubleMultiplication() { double a, b, c; - a = 7D; - b = 3D; + a = $inline$double(7D); + b = $inline$double(3D); c = a * b; return c; } @@ -496,90 +509,90 @@ public class Main { * Exercise constant folding on division. */ - /// CHECK-START: int Main.IntDivision() constant_folding (before) + /// CHECK-START: int Main.IntDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 8 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: int Main.IntDivision() constant_folding (after) + /// CHECK-START: int Main.IntDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntDivision() constant_folding (after) + /// CHECK-START: int Main.IntDivision() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Div public static int IntDivision() { int a, b, c; - a = 8; - b = 3; + a = $inline$int(8); + b = $inline$int(3); c = a / b; return c; } - /// CHECK-START: long Main.LongDivision() constant_folding (before) + /// CHECK-START: long Main.LongDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 8 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: long Main.LongDivision() constant_folding (after) + /// CHECK-START: long Main.LongDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongDivision() constant_folding (after) + /// CHECK-START: long Main.LongDivision() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Div public static long LongDivision() { long a, b, c; - a = 8L; - b = 3L; + a = $inline$long(8L); + b = $inline$long(3L); c = a / b; return c; } - /// CHECK-START: float Main.FloatDivision() constant_folding (before) + /// CHECK-START: float Main.FloatDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> FloatConstant 2.5 /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-START: float Main.FloatDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 3.2 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatDivision() constant_folding (after) + /// CHECK-START: float Main.FloatDivision() constant_folding_after_inlining (after) /// CHECK-NOT: Div public static float FloatDivision() { float a, b, c; - a = 8F; - b = 2.5F; + a = $inline$float(8F); + b = $inline$float(2.5F); c = a / b; return c; } - /// CHECK-START: double Main.DoubleDivision() constant_folding (before) + /// CHECK-START: double Main.DoubleDivision() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 8 /// CHECK-DAG: <> DoubleConstant 2.5 /// CHECK-DAG: <> Div [<>,<>] /// CHECK-DAG: Return [<
>] - /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-START: double Main.DoubleDivision() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 3.2 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleDivision() constant_folding (after) + /// CHECK-START: double Main.DoubleDivision() constant_folding_after_inlining (after) /// CHECK-NOT: Div public static double DoubleDivision() { double a, b, c; - a = 8D; - b = 2.5D; + a = $inline$double(8D); + b = $inline$double(2.5D); c = a / b; return c; } @@ -589,90 +602,90 @@ public class Main { * Exercise constant folding on remainder. */ - /// CHECK-START: int Main.IntRemainder() constant_folding (before) + /// CHECK-START: int Main.IntRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 8 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntRemainder() constant_folding (after) + /// CHECK-START: int Main.IntRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.IntRemainder() constant_folding (after) + /// CHECK-START: int Main.IntRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Rem public static int IntRemainder() { int a, b, c; - a = 8; - b = 3; + a = $inline$int(8); + b = $inline$int(3); c = a % b; return c; } - /// CHECK-START: long Main.LongRemainder() constant_folding (before) + /// CHECK-START: long Main.LongRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 8 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> DivZeroCheck [<>] /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongRemainder() constant_folding (after) + /// CHECK-START: long Main.LongRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.LongRemainder() constant_folding (after) + /// CHECK-START: long Main.LongRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: DivZeroCheck /// CHECK-NOT: Rem public static long LongRemainder() { long a, b, c; - a = 8L; - b = 3L; + a = $inline$long(8L); + b = $inline$long(3L); c = a % b; return c; } - /// CHECK-START: float Main.FloatRemainder() constant_folding (before) + /// CHECK-START: float Main.FloatRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 8 /// CHECK-DAG: <> FloatConstant 2.5 /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-START: float Main.FloatRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 0.5 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.FloatRemainder() constant_folding (after) + /// CHECK-START: float Main.FloatRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: Rem public static float FloatRemainder() { float a, b, c; - a = 8F; - b = 2.5F; + a = $inline$float(8F); + b = $inline$float(2.5F); c = a % b; return c; } - /// CHECK-START: double Main.DoubleRemainder() constant_folding (before) + /// CHECK-START: double Main.DoubleRemainder() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 8 /// CHECK-DAG: <> DoubleConstant 2.5 /// CHECK-DAG: <> Rem [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-START: double Main.DoubleRemainder() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 0.5 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.DoubleRemainder() constant_folding (after) + /// CHECK-START: double Main.DoubleRemainder() constant_folding_after_inlining (after) /// CHECK-NOT: Rem public static double DoubleRemainder() { double a, b, c; - a = 8D; - b = 2.5D; + a = $inline$double(8D); + b = $inline$double(2.5D); c = a % b; return c; } @@ -682,42 +695,42 @@ public class Main { * Exercise constant folding on left shift. */ - /// CHECK-START: int Main.ShlIntLong() constant_folding (before) + /// CHECK-START: int Main.ShlIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Shl [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShlIntLong() constant_folding (after) + /// CHECK-START: int Main.ShlIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 4 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShlIntLong() constant_folding (after) + /// CHECK-START: int Main.ShlIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Shl public static int ShlIntLong() { - int lhs = 1; - long rhs = 2; + int lhs = $inline$int(1); + long rhs = $inline$long(2L); return lhs << rhs; } - /// CHECK-START: long Main.ShlLongInt() constant_folding (before) + /// CHECK-START: long Main.ShlLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Shl [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShlLongInt() constant_folding (after) + /// CHECK-START: long Main.ShlLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 12 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShlLongInt() constant_folding (after) + /// CHECK-START: long Main.ShlLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Shl public static long ShlLongInt() { - long lhs = 3; - int rhs = 2; + long lhs = $inline$long(3L); + int rhs = $inline$int(2); return lhs << rhs; } @@ -726,42 +739,42 @@ public class Main { * Exercise constant folding on right shift. */ - /// CHECK-START: int Main.ShrIntLong() constant_folding (before) + /// CHECK-START: int Main.ShrIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 7 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Shr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShrIntLong() constant_folding (after) + /// CHECK-START: int Main.ShrIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ShrIntLong() constant_folding (after) + /// CHECK-START: int Main.ShrIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Shr public static int ShrIntLong() { - int lhs = 7; - long rhs = 2; + int lhs = $inline$int(7); + long rhs = $inline$long(2L); return lhs >> rhs; } - /// CHECK-START: long Main.ShrLongInt() constant_folding (before) + /// CHECK-START: long Main.ShrLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 9 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> Shr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShrLongInt() constant_folding (after) + /// CHECK-START: long Main.ShrLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ShrLongInt() constant_folding (after) + /// CHECK-START: long Main.ShrLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Shr public static long ShrLongInt() { - long lhs = 9; - int rhs = 2; + long lhs = $inline$long(9); + int rhs = $inline$int(2); return lhs >> rhs; } @@ -770,42 +783,42 @@ public class Main { * Exercise constant folding on unsigned right shift. */ - /// CHECK-START: int Main.UShrIntLong() constant_folding (before) + /// CHECK-START: int Main.UShrIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant -7 /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> UShr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.UShrIntLong() constant_folding (after) + /// CHECK-START: int Main.UShrIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 1073741822 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.UShrIntLong() constant_folding (after) + /// CHECK-START: int Main.UShrIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: UShr public static int UShrIntLong() { - int lhs = -7; - long rhs = 2; + int lhs = $inline$int(-7); + long rhs = $inline$long(2L); return lhs >>> rhs; } - /// CHECK-START: long Main.UShrLongInt() constant_folding (before) + /// CHECK-START: long Main.UShrLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant -9 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> UShr [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.UShrLongInt() constant_folding (after) + /// CHECK-START: long Main.UShrLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 4611686018427387901 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.UShrLongInt() constant_folding (after) + /// CHECK-START: long Main.UShrLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: UShr public static long UShrLongInt() { - long lhs = -9; - int rhs = 2; + long lhs = $inline$long(-9); + int rhs = $inline$int(2); return lhs >>> rhs; } @@ -814,43 +827,43 @@ public class Main { * Exercise constant folding on logical and. */ - /// CHECK-START: long Main.AndIntLong() constant_folding (before) + /// CHECK-START: long Main.AndIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> And [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndIntLong() constant_folding (after) + /// CHECK-START: long Main.AndIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndIntLong() constant_folding (after) + /// CHECK-START: long Main.AndIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: And public static long AndIntLong() { - int lhs = 10; - long rhs = 3; + int lhs = $inline$int(10); + long rhs = $inline$long(3L); return lhs & rhs; } - /// CHECK-START: long Main.AndLongInt() constant_folding (before) + /// CHECK-START: long Main.AndLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 10 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> And [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndLongInt() constant_folding (after) + /// CHECK-START: long Main.AndLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 2 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.AndLongInt() constant_folding (after) + /// CHECK-START: long Main.AndLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: And public static long AndLongInt() { - long lhs = 10; - int rhs = 3; + long lhs = $inline$long(10L); + int rhs = $inline$int(3); return lhs & rhs; } @@ -859,43 +872,43 @@ public class Main { * Exercise constant folding on logical or. */ - /// CHECK-START: long Main.OrIntLong() constant_folding (before) + /// CHECK-START: long Main.OrIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Or [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrIntLong() constant_folding (after) + /// CHECK-START: long Main.OrIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 11 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrIntLong() constant_folding (after) + /// CHECK-START: long Main.OrIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Or public static long OrIntLong() { - int lhs = 10; - long rhs = 3; + int lhs = $inline$int(10); + long rhs = $inline$long(3L); return lhs | rhs; } - /// CHECK-START: long Main.OrLongInt() constant_folding (before) + /// CHECK-START: long Main.OrLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 10 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Or [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrLongInt() constant_folding (after) + /// CHECK-START: long Main.OrLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 11 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.OrLongInt() constant_folding (after) + /// CHECK-START: long Main.OrLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Or public static long OrLongInt() { - long lhs = 10; - int rhs = 3; + long lhs = $inline$long(10L); + int rhs = $inline$int(3); return lhs | rhs; } @@ -904,43 +917,43 @@ public class Main { * Exercise constant folding on logical exclusive or. */ - /// CHECK-START: long Main.XorIntLong() constant_folding (before) + /// CHECK-START: long Main.XorIntLong() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 10 /// CHECK-DAG: <> LongConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Xor [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorIntLong() constant_folding (after) + /// CHECK-START: long Main.XorIntLong() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 9 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorIntLong() constant_folding (after) + /// CHECK-START: long Main.XorIntLong() constant_folding_after_inlining (after) /// CHECK-NOT: Xor public static long XorIntLong() { - int lhs = 10; - long rhs = 3; + int lhs = $inline$int(10); + long rhs = $inline$long(3L); return lhs ^ rhs; } - /// CHECK-START: long Main.XorLongInt() constant_folding (before) + /// CHECK-START: long Main.XorLongInt() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 10 /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: <> Xor [<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorLongInt() constant_folding (after) + /// CHECK-START: long Main.XorLongInt() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 9 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.XorLongInt() constant_folding (after) + /// CHECK-START: long Main.XorLongInt() constant_folding_after_inlining (after) /// CHECK-NOT: Xor public static long XorLongInt() { - long lhs = 10; - int rhs = 3; + long lhs = $inline$long(10L); + int rhs = $inline$int(3); return lhs ^ rhs; } @@ -949,23 +962,23 @@ public class Main { * Exercise constant folding on constant (static) condition. */ - /// CHECK-START: int Main.StaticCondition() constant_folding (before) + /// CHECK-START: int Main.StaticCondition() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 7 /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] - /// CHECK-DAG: If [<>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-START: int Main.StaticCondition() constant_folding (after) + /// CHECK-START: int Main.StaticCondition() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: If [<>] + /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<>] - /// CHECK-START: int Main.StaticCondition() constant_folding (after) + /// CHECK-START: int Main.StaticCondition() constant_folding_after_inlining (after) /// CHECK-NOT: GreaterThanOrEqual public static int StaticCondition() { int a, b, c; - a = 7; - b = 2; + a = $inline$int(7); + b = $inline$int(2); if (a < b) c = a + b; else @@ -1010,28 +1023,30 @@ public class Main { * (forward) post-order traversal of the the dominator tree. */ - /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before) + /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding_after_inlining (before) + /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 2 /// CHECK-DAG: <> IntConstant 5 /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: <> Sub [<>,<>] - /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after) + /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding_after_inlining (after) + /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 3 /// CHECK-DAG: <> IntConstant 7 - /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after) + /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding_after_inlining (after) /// CHECK-NOT: Add /// CHECK-NOT: Sub public static int JumpsAndConditionals(boolean cond) { int a, b, c; - a = 5; - b = 2; + a = $inline$int(5); + b = $inline$int(2); if (cond) c = a + b; else @@ -1310,204 +1325,204 @@ public class Main { * Exercise constant folding on type conversions. */ - /// CHECK-START: int Main.ReturnInt33() constant_folding (before) + /// CHECK-START: int Main.ReturnInt33() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt33() constant_folding (after) + /// CHECK-START: int Main.ReturnInt33() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt33() constant_folding (after) + /// CHECK-START: int Main.ReturnInt33() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static int ReturnInt33() { - long imm = 33L; + long imm = $inline$long(33L); return (int) imm; } - /// CHECK-START: int Main.ReturnIntMax() constant_folding (before) + /// CHECK-START: int Main.ReturnIntMax() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 1e+34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnIntMax() constant_folding (after) + /// CHECK-START: int Main.ReturnIntMax() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 2147483647 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnIntMax() constant_folding (after) + /// CHECK-START: int Main.ReturnIntMax() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static int ReturnIntMax() { - float imm = 1.0e34f; + float imm = $inline$float(1.0e34f); return (int) imm; } - /// CHECK-START: int Main.ReturnInt0() constant_folding (before) + /// CHECK-START: int Main.ReturnInt0() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant nan /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt0() constant_folding (after) + /// CHECK-START: int Main.ReturnInt0() constant_folding_after_inlining (after) /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: Return [<>] - /// CHECK-START: int Main.ReturnInt0() constant_folding (after) + /// CHECK-START: int Main.ReturnInt0() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static int ReturnInt0() { - double imm = Double.NaN; + double imm = $inline$double(Double.NaN); return (int) imm; } - /// CHECK-START: long Main.ReturnLong33() constant_folding (before) + /// CHECK-START: long Main.ReturnLong33() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong33() constant_folding (after) + /// CHECK-START: long Main.ReturnLong33() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 33 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong33() constant_folding (after) + /// CHECK-START: long Main.ReturnLong33() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static long ReturnLong33() { - int imm = 33; + int imm = $inline$int(33); return (long) imm; } - /// CHECK-START: long Main.ReturnLong34() constant_folding (before) + /// CHECK-START: long Main.ReturnLong34() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong34() constant_folding (after) + /// CHECK-START: long Main.ReturnLong34() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 34 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong34() constant_folding (after) + /// CHECK-START: long Main.ReturnLong34() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static long ReturnLong34() { - float imm = 34.0f; + float imm = $inline$float(34.0f); return (long) imm; } - /// CHECK-START: long Main.ReturnLong0() constant_folding (before) + /// CHECK-START: long Main.ReturnLong0() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant nan /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong0() constant_folding (after) + /// CHECK-START: long Main.ReturnLong0() constant_folding_after_inlining (after) /// CHECK-DAG: <> LongConstant 0 /// CHECK-DAG: Return [<>] - /// CHECK-START: long Main.ReturnLong0() constant_folding (after) + /// CHECK-START: long Main.ReturnLong0() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static long ReturnLong0() { - double imm = -Double.NaN; + double imm = $inline$double(-Double.NaN); return (long) imm; } - /// CHECK-START: float Main.ReturnFloat33() constant_folding (before) + /// CHECK-START: float Main.ReturnFloat33() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat33() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat33() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 33 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat33() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat33() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static float ReturnFloat33() { - int imm = 33; + int imm = $inline$int(33); return (float) imm; } - /// CHECK-START: float Main.ReturnFloat34() constant_folding (before) + /// CHECK-START: float Main.ReturnFloat34() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat34() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat34() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 34 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat34() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat34() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static float ReturnFloat34() { - long imm = 34L; + long imm = $inline$long(34L); return (float) imm; } - /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (before) + /// CHECK-START: float Main.ReturnFloat99P25() constant_folding_after_inlining (before) /// CHECK-DAG: <> DoubleConstant 99.25 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat99P25() constant_folding_after_inlining (after) /// CHECK-DAG: <> FloatConstant 99.25 /// CHECK-DAG: Return [<>] - /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after) + /// CHECK-START: float Main.ReturnFloat99P25() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static float ReturnFloat99P25() { - double imm = 99.25; + double imm = $inline$double(99.25); return (float) imm; } - /// CHECK-START: double Main.ReturnDouble33() constant_folding (before) + /// CHECK-START: double Main.ReturnDouble33() constant_folding_after_inlining (before) /// CHECK-DAG: <> IntConstant 33 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble33() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble33() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 33 /// CHECK-DAG: Return [<>] public static double ReturnDouble33() { - int imm = 33; + int imm = $inline$int(33); return (double) imm; } - /// CHECK-START: double Main.ReturnDouble34() constant_folding (before) + /// CHECK-START: double Main.ReturnDouble34() constant_folding_after_inlining (before) /// CHECK-DAG: <> LongConstant 34 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble34() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble34() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 34 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble34() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble34() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static double ReturnDouble34() { - long imm = 34L; + long imm = $inline$long(34L); return (double) imm; } - /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (before) + /// CHECK-START: double Main.ReturnDouble99P25() constant_folding_after_inlining (before) /// CHECK-DAG: <> FloatConstant 99.25 /// CHECK-DAG: <> TypeConversion [<>] /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble99P25() constant_folding_after_inlining (after) /// CHECK-DAG: <> DoubleConstant 99.25 /// CHECK-DAG: Return [<>] - /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after) + /// CHECK-START: double Main.ReturnDouble99P25() constant_folding_after_inlining (after) /// CHECK-NOT: TypeConversion public static double ReturnDouble99P25() { - float imm = 99.25f; + float imm = $inline$float(99.25f); return (double) imm; } diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 31bb94cb8c8237ce84644c119f392a0011d00077..66e1d92cc20a84eefd7de6fa54d4896fe4ab8e5c 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -137,20 +137,16 @@ public class Main { /// CHECK: ArraySet /// CHECK: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArraySet /// CHECK-START: void Main.$opt$noinline$constantIndexing2(int[]) BCE (after) - /// CHECK-NOT: Deoptimize - /// CHECK: BoundsCheck - /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK: Deoptimize + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - /// CHECK: BoundsCheck + /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet static void $opt$noinline$constantIndexing2(int[] array) { @@ -158,8 +154,7 @@ public class Main { array[2] = 1; array[3] = 1; array[4] = 1; - array[-1] = 1; // prevents the whole opt on [-1:4] - if (array[1] == 1) { + if (array[1] != 1) { throw new Error(""); } } @@ -173,8 +168,41 @@ public class Main { /// CHECK: ArraySet /// CHECK: BoundsCheck /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet /// CHECK-START: void Main.constantIndexing2b(int[]) BCE (after) + /// CHECK-NOT: Deoptimize + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + static void constantIndexing2b(int[] array) { + array[0] = 6; + array[1] = 6; + array[2] = 6; + array[3] = 6; + array[-1] = 1; // prevents the whole opt on [-1:4] + } + + /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (before) + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + /// CHECK: BoundsCheck + /// CHECK: ArraySet + + /// CHECK-START: void Main.constantIndexing2c(int[]) BCE (after) /// CHECK: Deoptimize /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet @@ -185,7 +213,7 @@ public class Main { /// CHECK-NOT: BoundsCheck /// CHECK: ArraySet - static void constantIndexing2b(int[] array) { + static void constantIndexing2c(int[] array) { array[0] = 7; array[1] = 7; array[2] = 7; @@ -391,6 +419,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 }; @@ -410,31 +468,37 @@ public class Main { System.out.println("constant indices 1 failed!"); } + $opt$noinline$constantIndexing2(a6); + if (a6[0] != 0 || a6[1] != 1 || a6[2] != 1 || + a6[3] != 1 || a6[4] != 1 || a6[5] != 11) { + System.out.println("constant indices 2 failed!"); + } + caught = false; try { - $opt$noinline$constantIndexing2(a6); + constantIndexing2b(a6); } catch (ArrayIndexOutOfBoundsException e) { caught = true; } - if (!caught || a6[0] != 0 || a6[1] != 1 || a6[2] != 1 || - a6[3] != 1 || a6[4] != 1 || a6[5] != 11) { - System.out.println("constant indices 2 failed!"); + if (!caught || a6[0] != 6 || a6[1] != 6 || a6[2] != 6 || + a6[3] != 6 || a6[4] != 1 || a6[5] != 11) { + System.out.println("constant indices 2b failed!"); } caught = false; try { - constantIndexing2b(a1); + constantIndexing2c(a1); } catch (ArrayIndexOutOfBoundsException e) { caught = true; } if (!caught || a1[0] != 7) { - System.out.println("constant indices 2b failed!"); + System.out.println("constant indices 2c failed!"); } - constantIndexing2b(a6); + constantIndexing2c(a6); if (a6[0] != 7 || a6[1] != 7 || a6[2] != 7 || a6[3] != 7 || a6[4] != 1 || a6[5] != 11) { - System.out.println("constant indices 2b failed!"); + System.out.println("constant indices 2c failed!"); } int[] b4 = new int[4]; @@ -502,6 +566,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/450-checker-types/smali/SmaliTests.smali b/test/450-checker-types/smali/SmaliTests.smali new file mode 100644 index 0000000000000000000000000000000000000000..6a3122e41b6902ab374efba654fdbd458372a627 --- /dev/null +++ b/test/450-checker-types/smali/SmaliTests.smali @@ -0,0 +1,120 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LSmaliTests; +.super Ljava/lang/Object; + +## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: Equal [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_EQ0_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x0 + instance-of v1, p0, LSubclassC; + if-eq v1, v0, :return + + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + + :return + return-void + +.end method + +## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: Equal [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_EQ1_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x1 + instance-of v1, p0, LSubclassC; + if-eq v1, v0, :invoke + return-void + + :invoke + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + return-void + +.end method + +## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: NotEqual [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_NE0_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x0 + instance-of v1, p0, LSubclassC; + if-ne v1, v0, :invoke + return-void + + :invoke + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + return-void + +.end method + +## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) builder (after) +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> InstanceOf +## CHECK-DAG: NotEqual [<>,<>] + +## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) instruction_simplifier (before) +## CHECK: CheckCast + +## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) instruction_simplifier (after) +## CHECK-NOT: CheckCast + +.method public static testInstanceOf_NE1_NotInlined(Ljava/lang/Object;)V + .registers 3 + + const v0, 0x1 + instance-of v1, p0, LSubclassC; + if-ne v1, v0, :return + + check-cast p0, LSubclassC; + invoke-virtual {p0}, LSubclassC;->$noinline$g()V + + :return + return-void + +.end method diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 027a9d948717774a9329459f23d1abac9d68ced2..08b6cec35f79841b838b2525eab3ec904ea38fc8 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -205,58 +205,6 @@ public class Main { public static boolean $inline$InstanceofSubclassB(Object o) { return o instanceof SubclassB; } public static boolean $inline$InstanceofSubclassC(Object o) { return o instanceof SubclassC; } - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) builder (after) - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: NotEqual [<>,<>] - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: Equal [<>,<>] - - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before) - /// CHECK: CheckCast - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testInstanceOf_NotInlined(Object o) { - if ((o instanceof SubclassC) == true) { - ((SubclassC)o).$noinline$g(); - } - if ((o instanceof SubclassB) != false) { - ((SubclassB)o).$noinline$g(); - } - } - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) builder (after) - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: Equal [<>,<>] - /// CHECK-DAG: <> InstanceOf - /// CHECK-DAG: NotEqual [<>,<>] - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before) - /// CHECK: CheckCast - /// CHECK: CheckCast - /// CHECK-NOT: CheckCast - - /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after) - /// CHECK-NOT: CheckCast - public void testNotInstanceOf_NotInlined(Object o) { - if ((o instanceof SubclassC) != true) { - // Empty branch to flip the condition. - } else { - ((SubclassC)o).$noinline$g(); - } - if ((o instanceof SubclassB) == false) { - // Empty branch to flip the condition. - } else { - ((SubclassB)o).$noinline$g(); - } - } - /// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) inliner (after) /// CHECK-DAG: <> InstanceOf /// CHECK-DAG: If [<>] diff --git a/test/458-checker-instruction-simplification/smali/SmaliTests.smali b/test/458-checker-instruction-simplification/smali/SmaliTests.smali new file mode 100644 index 0000000000000000000000000000000000000000..ede599b2139753417ea2af4019e1faed467cf6b8 --- /dev/null +++ b/test/458-checker-instruction-simplification/smali/SmaliTests.smali @@ -0,0 +1,193 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LSmaliTests; +.super Ljava/lang/Object; + +## CHECK-START: int SmaliTests.EqualTrueRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualTrueRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualTrueRhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x5 + if-eq p0, v0, :return + const v1, 0x3 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.EqualTrueLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualTrueLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualTrueLhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x5 + if-eq v0, p0, :return + const v1, 0x3 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.EqualFalseRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualFalseRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualFalseRhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x3 + if-eq p0, v0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.EqualFalseLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> Equal [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.EqualFalseLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static EqualFalseLhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x3 + if-eq v0, p0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualTrueRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualTrueRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualTrueRhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x3 + if-ne p0, v0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualTrueLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 1 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualTrueLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualTrueLhs(Z)I + .registers 3 + + const v0, 0x1 + const v1, 0x3 + if-ne v0, p0, :return + const v1, 0x5 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualFalseRhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualFalseRhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualFalseRhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x5 + if-ne p0, v0, :return + const v1, 0x3 + :return + return v1 + +.end method + +## CHECK-START: int SmaliTests.NotEqualFalseLhs(boolean) instruction_simplifier (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: <> IntConstant 0 +## CHECK-DAG: <> NotEqual [<>,<>] +## CHECK-DAG: If [<>] + +## CHECK-START: int SmaliTests.NotEqualFalseLhs(boolean) instruction_simplifier (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: If [<>] + +.method public static NotEqualFalseLhs(Z)I + .registers 3 + + const v0, 0x0 + const v1, 0x5 + if-ne v0, p0, :return + const v1, 0x3 + :return + return v1 + +.end method + diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 8d6bb653f446af1527234d9e7c204ee4a19aa5e0..dd4ffe45e4f8b9d139eb07101c84a5e75a66f406 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + public class Main { public static void assertBooleanEquals(boolean expected, boolean result) { @@ -826,17 +828,16 @@ public class Main { /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> LongConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: Return [<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: Return [<>] /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: Return [<>] /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) - /// CHECK-NOT: Xor + /// CHECK-NOT: Not public static long NotNot1(long arg) { return ~~arg; @@ -844,10 +845,9 @@ public class Main { /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (before) /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant -1 - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Xor [<>,<>] - /// CHECK-DAG: <> Add [<>,<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: <> Not [<>] + /// CHECK-DAG: <> Add [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) @@ -857,7 +857,8 @@ public class Main { /// CHECK-DAG: Return [<>] /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) - /// CHECK-NOT: Xor + /// CHECK: Not + /// CHECK-NOT: Not public static int NotNot2(int arg) { int temp = ~arg; @@ -965,174 +966,6 @@ public class Main { return res; } - /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualTrueRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int EqualTrueLhs(boolean arg) { - return (true != arg) ? 3 : 5; - } - - /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.EqualFalseRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int EqualFalseLhs(boolean arg) { - return (false != arg) ? 3 : 5; - } - - /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualTrueRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int NotEqualTrueLhs(boolean arg) { - return (true == arg) ? 3 : 5; - } - - /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (before) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 0 - /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.NotEqualFalseRhs(boolean) instruction_simplifier_before_codegen (after) - /// CHECK-DAG: <> ParameterValue - /// CHECK-DAG: <> IntConstant 3 - /// CHECK-DAG: <> IntConstant 5 - /// CHECK-DAG: <> Select [<>,<>,<>] - /// CHECK-DAG: Return [<>] - - public static int NotEqualFalseLhs(boolean arg) { - return (false == arg) ? 3 : 5; - } - /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <> ParameterValue /// CHECK-DAG: <> IntConstant 0 @@ -1307,17 +1140,16 @@ public class Main { return arg * 31; } - /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier (before) + /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_after_bce (before) /// CHECK-DAG: <> IntConstant 1 + /// CHECK-DAG: <> IntConstant 13 + /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: <> NotEqual [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier (after) - /// CHECK-DAG: <> StaticFieldGet - /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] public static int booleanFieldNotEqualOne() { - return (booleanField == true) ? 13 : 54; + return (booleanField == $inline$true()) ? 13 : 54; } - /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier (before) + /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_after_bce (before) /// CHECK-DAG: <> IntConstant 0 + /// CHECK-DAG: <> IntConstant 13 + /// CHECK-DAG: <> IntConstant 54 /// CHECK-DAG: <> StaticFieldGet - /// CHECK-DAG: <> Equal [<>,<>] - /// CHECK-DAG: If [<>] - - /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier (after) - /// CHECK-DAG: <> StaticFieldGet - /// CHECK-DAG: If [<>] + /// CHECK-DAG: <> Equal [<>,<>] + /// CHECK-DAG: <> Select [<>,<>,<>] + /// CHECK-DAG: Return [<>] public static int booleanFieldEqualZero() { - return (booleanField != false) ? 13 : 54; + return (booleanField != $inline$false()) ? 13 : 54; } /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (before) @@ -1374,7 +1205,7 @@ public class Main { // LessThanOrEqual instructions. public static int intConditionNotEqualOne(int i) { - return ((i > 42) == true) ? 13 : 54; + return ((i > 42) == $inline$true()) ? 13 : 54; } /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (before) @@ -1402,7 +1233,7 @@ public class Main { // LessThanOrEqual instructions. public static int intConditionEqualZero(int i) { - return ((i > 42) != false) ? 13 : 54; + return ((i > 42) != $inline$false()) ? 13 : 54; } // Test that conditions on float/double are not flipped. @@ -1770,7 +1601,45 @@ public class Main { return (short) (value & 0x17fff); } - public static void main(String[] args) { + /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (before) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] + + /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (after) + /// CHECK-DAG: <> ParameterValue + /// CHECK-DAG: <> IntConstant 42 + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] + + public static int intReverseCondition(int i) { + return (42 > i) ? 13 : 54; + } + + /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (before) + /// CHECK-DAG: <> DoubleConstant 42 + /// CHECK-DAG: <> InvokeStaticOrDirect + /// CHECK-DAG: <> Compare [<>,<>] + + /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (after) + /// CHECK-DAG: <> DoubleConstant 42 + /// CHECK-DAG: <> InvokeStaticOrDirect + /// CHECK-DAG: <> Equal [<>,<>] + + public static int intReverseConditionNaN(int i) { + return (42 != Math.sqrt(i)) ? 13 : 54; + } + + public static int runSmaliTest(String name, boolean input) { + try { + Class c = Class.forName("SmaliTests"); + Method m = c.getMethod(name, new Class[] { boolean.class }); + return (Integer) m.invoke(null, input); + } catch (Exception ex) { + throw new Error(ex); + } + } + +public static void main(String[] args) { int arg = 123456; assertLongEquals(Add0(arg), arg); @@ -1804,14 +1673,6 @@ public class Main { assertIntEquals(SubNeg1(arg, arg + 1), -(arg + arg + 1)); assertIntEquals(SubNeg2(arg, arg + 1), -(arg + arg + 1)); assertLongEquals(SubNeg3(arg, arg + 1), -(2 * arg + 1)); - assertIntEquals(EqualTrueRhs(true), 5); - assertIntEquals(EqualTrueLhs(true), 5); - assertIntEquals(EqualFalseRhs(true), 3); - assertIntEquals(EqualFalseLhs(true), 3); - assertIntEquals(NotEqualTrueRhs(true), 3); - assertIntEquals(NotEqualTrueLhs(true), 3); - assertIntEquals(NotEqualFalseRhs(true), 5); - assertIntEquals(NotEqualFalseLhs(true), 5); assertBooleanEquals(EqualBoolVsIntConst(true), true); assertBooleanEquals(EqualBoolVsIntConst(true), true); assertBooleanEquals(NotEqualBoolVsIntConst(false), false); @@ -1906,7 +1767,23 @@ public class Main { assertIntEquals(intAnd0x17fffToShort(0x88888888), 0x0888); assertIntEquals(intAnd0x17fffToShort(Integer.MIN_VALUE), 0); assertIntEquals(intAnd0x17fffToShort(Integer.MAX_VALUE), Short.MAX_VALUE); + + assertIntEquals(intReverseCondition(41), 13); + assertIntEquals(intReverseConditionNaN(-5), 13); + + for (String condition : new String[] { "Equal", "NotEqual" }) { + for (String constant : new String[] { "True", "False" }) { + for (String side : new String[] { "Rhs", "Lhs" }) { + String name = condition + constant + side; + assertIntEquals(runSmaliTest(name, true), 5); + assertIntEquals(runSmaliTest(name, false), 3); + } + } + } } + private static boolean $inline$true() { return true; } + private static boolean $inline$false() { return false; } + public static boolean booleanField; } diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java index 682f12641fc50969d278c3bfcd333e5031f67f21..f0fe1b172f4d945ae37382e6017bae9418f6b733 100644 --- a/test/463-checker-boolean-simplifier/src/Main.java +++ b/test/463-checker-boolean-simplifier/src/Main.java @@ -42,7 +42,7 @@ public class Main { /// CHECK-DAG: <> IntConstant 0 /// CHECK-DAG: <> IntConstant 1 /// CHECK-DAG: If [<>] - /// CHECK-DAG: <> Phi [<>,<>] + /// CHECK-DAG: <> Phi [<>,<>] /// CHECK-DAG: Return [<>] /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) @@ -185,11 +185,7 @@ public class Main { /// CHECK-NOT: BooleanNot public static int NegatedCondition(boolean x) { - if (x != false) { - return 42; - } else { - return 43; - } + return (x != false) ? 42 : 43; } /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) @@ -253,13 +249,7 @@ public class Main { /// CHECK-DAG: Return [<>] public static int ThreeBlocks(boolean x, boolean y) { - if (x) { - return 1; - } else if (y) { - return 2; - } else { - return 3; - } + return x ? 1 : (y ? 2 : 3); } /// CHECK-START: int Main.MultiplePhis() select_generator (before) @@ -292,8 +282,10 @@ public class Main { while (y++ < 10) { if (y > 1) { x = 13; + continue; } else { x = 42; + continue; } } return x; diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index deff279f7786601c5d3ed51b9215c0521f74aff1..2e5fd2534a0a09c6c32fa32df9c18e153ed18094 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -394,6 +394,34 @@ public class Main { return result; } + /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearForNEArrayLengthUp(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int linearForNEArrayLengthUp(int[] x) { + int result = 0; + for (int i = 0; i != x.length; i++) { + result += x[i]; + } + return result; + } + + /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (before) + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearForNEArrayLengthDown(int[]) BCE (after) + /// CHECK-NOT: BoundsCheck + /// CHECK-NOT: Deoptimize + private static int linearForNEArrayLengthDown(int[] x) { + int result = 0; + for (int i = x.length - 1; i != -1; i--) { + result += x[i]; + } + return result; + } + /// CHECK-START: int Main.linearDoWhileUp() BCE (before) /// CHECK-DAG: BoundsCheck // @@ -471,7 +499,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 +541,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 +556,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 +584,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,592 +633,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:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck 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:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck 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:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck 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:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck 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:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck 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:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: BoundsCheck 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:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: BoundsCheck 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:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - // Order matters: - /// CHECK: Deoptimize loop:<> - // CHECK-NOT: Goto loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> - /// CHECK: Goto 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:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: ArrayGet loop:<> - // For brevity, just test occurrence of at least one of each in the loop: - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-NOT: ArrayGet 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:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - /// CHECK-DAG: ArrayGet loop:<> - /// CHECK-DAG: NullCheck loop:<> - /// CHECK-DAG: ArrayLength loop:<> - /// CHECK-DAG: BoundsCheck loop:<> - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-DAG: ArrayGet loop:<> - /// 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. // @@ -1232,6 +702,8 @@ public class Main { // Special forms. expectEquals(55, linearForNEUp()); expectEquals(55, linearForNEDown()); + expectEquals(55, linearForNEArrayLengthUp(x)); + expectEquals(55, linearForNEArrayLengthDown(x)); expectEquals(55, linearDoWhileUp()); expectEquals(55, linearDoWhileDown()); expectEquals(55, linearShort()); @@ -1239,191 +711,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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..f5d334d011cc60ef9ebd9d4aa929f2ea9c674dc9 --- /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 0000000000000000000000000000000000000000..64be1a2be4db7546408d3fd4fb39bdfa7d846eb1 --- /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:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck 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:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck 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:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck 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:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck 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:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck 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:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: BoundsCheck 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:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: BoundsCheck 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:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: If loop:<> + /// CHECK-DAG: If loop:<> + /// CHECK-EVAL: "<>" != "<>" + // + /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: Deoptimize loop:<> + /// CHECK-EVAL: "<>" != "<>" + // + /// 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:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + // Order matters: + /// CHECK: Deoptimize loop:<> + // CHECK-NOT: Goto loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<> + /// CHECK: Goto 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:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: ArrayGet loop:<> + // For brevity, just test occurrence of at least one of each in the loop: + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-NOT: ArrayGet 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:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + /// CHECK-DAG: ArrayGet loop:<> + /// CHECK-DAG: NullCheck loop:<> + /// CHECK-DAG: ArrayLength loop:<> + /// CHECK-DAG: BoundsCheck loop:<> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<> + /// 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/537-checker-inline-and-unverified/src/Main.java b/test/537-checker-inline-and-unverified/src/Main.java index bdc14b027c8164eda6ca42c5575c6416856a962f..b9d5fc98cc8ea88c4a15e7d36b8fab0412c7ef87 100644 --- a/test/537-checker-inline-and-unverified/src/Main.java +++ b/test/537-checker-inline-and-unverified/src/Main.java @@ -45,12 +45,14 @@ public class Main { } public static boolean $opt$noinline$testNoInline() { + boolean result = true; try { - return null instanceof InaccessibleClass; + result = (null instanceof InaccessibleClass); + throw new Error("Unreachable"); } catch (IllegalAccessError e) { // expected } - return false; + return result; } public static boolean $opt$inline$testInline() { diff --git a/test/537-checker-jump-over-jump/src/Main.java b/test/537-checker-jump-over-jump/src/Main.java index cf9a69d28ebab22e5b02cf623d6b5b77caa993b4..7a58e8b1ace208c8b3a10e80b638648cd1de9599 100644 --- a/test/537-checker-jump-over-jump/src/Main.java +++ b/test/537-checker-jump-over-jump/src/Main.java @@ -24,7 +24,7 @@ public class Main { // /// CHECK: If /// CHECK-NEXT: cmp - /// CHECK-NEXT: jnl/ge + /// CHECK-NEXT: jle/ng // /// CHECK-DAG: <> StaticFieldGet /// CHECK-DAG: NullCheck [<>] diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java index 2d0688d57e81d67f0153bb49d83147af50f953fd..09376a20548b9fdfd4cb728a9b2095ff3ffb2764 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 { @@ -47,7 +47,7 @@ public class Main { /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm64 (after) @@ -57,6 +57,28 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Add + + /// CHECK-START-ARM: int Main.$opt$noinline$mulAdd(int, int, int) disassembly (after) + /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} + public static int $opt$noinline$mulAdd(int acc, int left, int right) { if (doThrow) throw new Error(); return acc + left * right; @@ -78,7 +100,7 @@ public class Main { /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm64 (after) @@ -88,6 +110,17 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulSub(long, long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} + /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Sub [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulSub(long, long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + public static long $opt$noinline$mulSub(long acc, long left, long right) { if (doThrow) throw new Error(); return acc - left * right; @@ -117,7 +150,28 @@ public class Main { /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64MultiplyAccumulate + /// CHECK-NOT: MultiplyAccumulate + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses1(int, int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static int $opt$noinline$multipleUses1(int acc, int left, int right) { if (doThrow) throw new Error(); @@ -151,7 +205,30 @@ public class Main { /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm64 (after) - /// CHECK-NOT: Arm64MultiplyAccumulate + /// CHECK-NOT: MultiplyAccumulate + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Sub [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Sub [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$multipleUses2(long, long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$multipleUses2(long acc, long left, long right) { @@ -176,7 +253,7 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add /// CHECK: Return [<>] /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm64 (after) @@ -186,6 +263,27 @@ public class Main { /// CHECK-START-ARM64: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) /// CHECK: madd w{{\d+}}, w{{\d+}}, w{{\d+}}, w{{\d+}} + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant 1 + /// CHECK: <> Add [<>,<>] + /// CHECK: <> Mul [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Add + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Add + + /// CHECK-START-ARM: int Main.$opt$noinline$mulPlusOne(int, int) disassembly (after) + /// CHECK: mla r{{\d+}}, r{{\d+}}, r{{\d+}}, r{{\d+}} + public static int $opt$noinline$mulPlusOne(int acc, int var) { if (doThrow) throw new Error(); return acc * (var + 1); @@ -207,7 +305,7 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> Arm64MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub /// CHECK: Return [<>] /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm64 (after) @@ -217,11 +315,114 @@ public class Main { /// CHECK-START-ARM64: long Main.$opt$noinline$mulMinusOne(long, long) disassembly (after) /// CHECK: msub x{{\d+}}, x{{\d+}}, x{{\d+}}, x{{\d+}} + /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> LongConstant 1 + /// CHECK: <> Sub [<>,<>] + /// CHECK: <> Mul [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulMinusOne(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate public static long $opt$noinline$mulMinusOne(long acc, long var) { if (doThrow) throw new Error(); return acc * (1 - var); } + /** + * Test basic merging of `MUL+NEG` into `MULNEG`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant 0 + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Neg + + /// CHECK-START-ARM64: int Main.$opt$noinline$mulNeg(int, int) disassembly (after) + /// CHECK: mneg w{{\d+}}, w{{\d+}}, w{{\d+}} + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$mulNeg(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + + public static int $opt$noinline$mulNeg(int left, int right) { + if (doThrow) throw new Error(); + return - (left * right); + } + + /** + * Test basic merging of `MUL+NEG` into `MULNEG`. + */ + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> LongConstant 0 + /// CHECK: <> MultiplyAccumulate [<>,<>,<>] kind:Sub + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Mul + /// CHECK-NOT: Neg + + /// CHECK-START-ARM64: long Main.$opt$noinline$mulNeg(long, long) disassembly (after) + /// CHECK: mneg x{{\d+}}, x{{\d+}}, x{{\d+}} + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Mul [<>,<>] + /// CHECK: <> Neg [<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$mulNeg(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: MultiplyAccumulate + + public static long $opt$noinline$mulNeg(long left, long right) { + if (doThrow) throw new Error(); + return - (left * right); + } public static void main(String[] args) { assertIntEquals(7, $opt$noinline$mulAdd(1, 2, 3)); @@ -230,5 +431,7 @@ public class Main { assertLongEquals(20, $opt$noinline$multipleUses2(10, 11, 12)); assertIntEquals(195, $opt$noinline$mulPlusOne(13, 14)); assertLongEquals(-225, $opt$noinline$mulMinusOne(15, 16)); + assertIntEquals(-306, $opt$noinline$mulNeg(17, 18)); + assertLongEquals(-380, $opt$noinline$mulNeg(19, 20)); } } diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index 8d73d69db9532926dbcb5ab9f5c3ade8f7b222e1..9c86154bd4d3a48f55c166942167578ab2090ad2 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/555-checker-regression-x86const/build b/test/555-checker-regression-x86const/build index 09dcc363dd2893fc04e89dea0faabd5bc5c50b04..92ddfc9a583ef40d5ddaad1e8da7013ad9a45052 100644 --- a/test/555-checker-regression-x86const/build +++ b/test/555-checker-regression-x86const/build @@ -27,14 +27,12 @@ mkdir classes-ex mv classes/UnresolvedClass.class classes-ex if [ ${USE_JACK} = "true" ]; then - # Create .jack files from classes generated with javac. - ${JILL} classes --output classes.jack - ${JILL} classes-ex --output classes-ex.jack + jar cf classes.jill.jar -C classes . + jar cf classes-ex.jill.jar -C classes-ex . - # Create DEX files from .jack files. - ${JACK} --import classes.jack --output-dex . + ${JACK} --import classes.jill.jar --output-dex . zip $TEST_NAME.jar classes.dex - ${JACK} --import classes-ex.jack --output-dex . + ${JACK} --import classes-ex.jill.jar --output-dex . zip ${TEST_NAME}-ex.jar classes.dex else if [ ${NEED_DEX} = "true" ]; then diff --git a/test/563-checker-invoke-super/build b/test/563-checker-invoke-super/build index e06193ba78ec2ff74b866cf784a8527a364f2ef4..32f84ef5abdecbba4fb9698bea66590ec9c40db1 100755 --- a/test/563-checker-invoke-super/build +++ b/test/563-checker-invoke-super/build @@ -20,9 +20,5 @@ set -e # Hard-wired use of experimental jack. # TODO: fix this temporary work-around for lambdas, see b/19467889 export USE_JACK=true -export JACK_SERVER=false -export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks" -# e.g. /foo/bar/jack-3.10.ALPHA.jar -> 3.10.ALPHA -export JACK_VERSION="$(find "$JACK_REPOSITORY" -name '*ALPHA*' | sed 's/.*jack-//g' | sed 's/[.]jar//g')" ./default-build "$@" --experimental default-methods diff --git a/test/564-checker-negbitwise/expected.txt b/test/564-checker-negbitwise/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/564-checker-negbitwise/info.txt b/test/564-checker-negbitwise/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..28b9e9e83240ee76a8ef1eae3a3b8ddda9d970a3 --- /dev/null +++ b/test/564-checker-negbitwise/info.txt @@ -0,0 +1 @@ +Test negated bitwise operations simplification on ARM64. diff --git a/test/564-checker-negbitwise/src/Main.java b/test/564-checker-negbitwise/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..ccb8ff4fdff2db272e229368fb912838427cbbac --- /dev/null +++ b/test/564-checker-negbitwise/src/Main.java @@ -0,0 +1,300 @@ +/* + * 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 { + + // A dummy value to defeat inlining of these routines. + static boolean doThrow = false; + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertLongEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + /** + * Test merging of `NOT+AND` into `BIC`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: And + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAnd(int, int) disassembly (after) + /// CHECK: bic w{{\d+}}, w{{\d+}}, w{{\d+}} + + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: Not + /// CHECK-NOT: And + + /// CHECK-START-ARM: int Main.$opt$noinline$notAnd(int, int) disassembly (after) + /// CHECK: bic.w r{{\d+}}, r{{\d+}}, r{{\d+}} + + public static int $opt$noinline$notAnd(int base, int mask) { + if (doThrow) throw new Error(); + return base & ~mask; + } + + /** + * Test merging of `NOT+ORR` into `ORN`. + */ + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:Or + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Or + + /// CHECK-START-ARM64: long Main.$opt$noinline$notOr(long, long) disassembly (after) + /// CHECK: orn x{{\d+}}, x{{\d+}}, x{{\d+}} + + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Or [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:Or + /// CHECK: Return [<>] + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) instruction_simplifier_arm (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Or + + /// CHECK-START-ARM: long Main.$opt$noinline$notOr(long, long) disassembly (after) + /// CHECK: orn.w r{{\d+}}, r{{\d+}}, r{{\d+}} + + public static long $opt$noinline$notOr(long base, long mask) { + if (doThrow) throw new Error(); + return base | ~mask; + } + + /** + * Test merging of `NOT+EOR` into `EON`. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Xor [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:Xor + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: Not + /// CHECK-NOT: Xor + + /// CHECK-START-ARM64: int Main.$opt$noinline$notXor(int, int) disassembly (after) + /// CHECK: eon w{{\d+}}, w{{\d+}}, w{{\d+}} + + + /// CHECK-START-ARM: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Xor [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> Not [<>] + /// CHECK: <> Xor [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notXor(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: BitwiseNegatedRight + + public static int $opt$noinline$notXor(int base, int mask) { + if (doThrow) throw new Error(); + return base ^ ~mask; + } + + /** + * Check that transformation is done when the argument is a constant. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: Return [<>] + + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndConstant(int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> BitwiseNegatedRight [<>,<>] kind:And + /// CHECK: Return [<>] + + public static int $opt$noinline$notAndConstant(int mask) { + if (doThrow) throw new Error(); + return 0xf & ~mask; + } + + /** + * Check that no transformation is done when Not has multiple uses. + */ + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM64: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: BitwiseNegatedRight + + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm (before) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm (after) + /// CHECK: <> ParameterValue + /// CHECK: <> ParameterValue + /// CHECK: <> IntConstant + /// CHECK: <> Not [<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> And [<>,<>] + /// CHECK: <> Add [<>,<>] + /// CHECK: Return [<>] + + /// CHECK-START-ARM: int Main.$opt$noinline$notAndMultipleUses(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: BitwiseNegatedRight + + public static int $opt$noinline$notAndMultipleUses(int base, int mask) { + if (doThrow) throw new Error(); + int tmp = ~mask; + return (tmp & 0x1) + (base & tmp); + } + + /** + * Check that no transformation is done when both inputs are Not's. + */ + + // We don't check the instructions before the pass, since if De Morgan's laws + // have been applied then Not/Not/Or is replaced by And/Not. + + /// CHECK-START-ARM64: int Main.$opt$noinline$deMorganOr(int, int) instruction_simplifier_arm64 (after) + /// CHECK-NOT: BitwiseNegatedRight + + /// CHECK-START-ARM: int Main.$opt$noinline$deMorganOr(int, int) instruction_simplifier_arm (after) + /// CHECK-NOT: BitwiseNegatedRight + + public static int $opt$noinline$deMorganOr(int a, int b) { + if (doThrow) throw new Error(); + return ~a | ~b; + } + + public static void main(String[] args) { + assertIntEquals(0xe, $opt$noinline$notAnd(0xf, 0x1)); + assertLongEquals(~0x0, $opt$noinline$notOr(0xf, 0x1)); + assertIntEquals(~0xe, $opt$noinline$notXor(0xf, 0x1)); + assertIntEquals(0xe, $opt$noinline$notAndConstant(0x1)); + assertIntEquals(0xe, $opt$noinline$notAndMultipleUses(0xf, 0x1)); + assertIntEquals(~0x1, $opt$noinline$deMorganOr(0x3, 0x1)); + } +} diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java index dc4cb76258306625b67ad0714b5cca97d2d1b05e..acfcecdba8121ee98631d7fd0cf3e566e8135cdc 100644 --- a/test/565-checker-condition-liveness/src/Main.java +++ b/test/565-checker-condition-liveness/src/Main.java @@ -28,10 +28,7 @@ public class Main { /// CHECK-EVAL: <> == <> + 1 public static int p(float arg) { - if (arg > 5.0f) { - return 0; - } - return -1; + return (arg > 5.0f) ? 0 : -1; } /// CHECK-START: void Main.main(java.lang.String[]) liveness (after) diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java index 41af97b3b7cafd90ebaf2271a3a8c567dbc97fa1..e426b75bf02a3dfc6714de85395a75a37ffd9a87 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 { @@ -35,14 +35,11 @@ public class Main { * Test transformation of Not/Not/And into Or/Not. */ - // Note: before the instruction_simplifier pass, Xor's are used instead of - // Not's (the simplification happens during the same pass). /// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> IntConstant -1 - /// CHECK: <> Xor [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] + /// CHECK: <> Not [<>] /// CHECK: <> And [<>,<>] /// CHECK: Return [<>] @@ -106,14 +103,11 @@ public class Main { * Test transformation of Not/Not/Or into And/Not. */ - // See note above. - // The second Xor has its arguments reversed for no obvious reason. /// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> LongConstant -1 - /// CHECK: <> Xor [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] + /// CHECK: <> Not [<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] @@ -183,12 +177,11 @@ public class Main { /// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK-DAG: <> IntConstant 1 - /// CHECK-DAG: <> IntConstant -1 + /// CHECK: <> IntConstant 1 /// CHECK: <> Add [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> Add [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> Or [<>,<>] /// CHECK: Return [<>] @@ -226,9 +219,8 @@ public class Main { /// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> IntConstant -1 - /// CHECK: <> Xor [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] + /// CHECK: <> Not [<>] /// CHECK: <> Xor [<>,<>] /// CHECK: Return [<>] @@ -285,11 +277,10 @@ public class Main { /// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before) /// CHECK: <> ParameterValue /// CHECK: <> ParameterValue - /// CHECK: <> IntConstant -1 /// CHECK: <> IntConstant 1 - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> And [<>,<>] - /// CHECK: <> Xor [<>,<>] + /// CHECK: <> Not [<>] /// CHECK: <> And [<>,<>] /// CHECK: <> Add [<>,<>] /// CHECK: Return [<>] diff --git a/test/577-checker-fp2int/expected.txt b/test/577-checker-fp2int/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..b0aad4deb5bb3fb6b422e222ec14bf5e4b99babe --- /dev/null +++ b/test/577-checker-fp2int/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/577-checker-fp2int/info.txt b/test/577-checker-fp2int/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..d22a0eab9d5af0a3b0cbbffe8567705bdd51ea95 --- /dev/null +++ b/test/577-checker-fp2int/info.txt @@ -0,0 +1 @@ +Unit test for float/double to raw bits conversions. diff --git a/test/577-checker-fp2int/src/Main.java b/test/577-checker-fp2int/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..ace956df2f0fbdf5b7372dcaed05a8d338646a82 --- /dev/null +++ b/test/577-checker-fp2int/src/Main.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: int Main.f2int(float) instruction_simplifier (before) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:FloatFloatToIntBits + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: int Main.f2int(float) instruction_simplifier (after) + // Note: The ArtMethod* (typed as int or long) is optional after sharpening. + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] intrinsic:FloatFloatToRawIntBits + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,{{i\d+}},<>] + /// CHECK-DAG: Return [<>] + private static int f2int(float f) { + return Float.floatToIntBits(f); + } + + /// CHECK-START: long Main.d2long(double) instruction_simplifier (before) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:DoubleDoubleToLongBits + /// CHECK-DAG: Return [<>] + // + /// CHECK-START: long Main.d2long(double) instruction_simplifier (after) + // Note: The ArtMethod* (typed as int or long) is optional after sharpening. + /// CHECK-DAG: <> InvokeStaticOrDirect [<>{{(,[ij]\d+)?}}] intrinsic:DoubleDoubleToRawLongBits + /// CHECK-DAG: <> NotEqual [<>,<>] + /// CHECK-DAG: <> Select [<>,{{j\d+}},<>] + /// CHECK-DAG: Return [<>] + private static long d2long(double d) { + return Double.doubleToLongBits(d); + } + + public static void main(String args[]) { + // A few distinct numbers. + expectEquals32(0xff800000, f2int(Float.NEGATIVE_INFINITY)); + expectEquals32(0xbf800000, f2int(-1.0f)); + expectEquals32(0x80000000, f2int(-0.0f)); + expectEquals32(0x00000000, f2int(+0.0f)); + expectEquals32(0x3f800000, f2int(+1.0f)); + expectEquals32(0x7f800000, f2int(Float.POSITIVE_INFINITY)); + + // A few others. + for (int i = 0; i <= 100; i++) { + expectEquals32(i, f2int(Float.intBitsToFloat(i))); + } + + // A few NaN numbers. + float[] fvals = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fvals.length; i++) { + expectEquals32(0x7fc00000, f2int(fvals[i])); + } + + // A few distinct numbers. + expectEquals64(0xfff0000000000000L, d2long(Double.NEGATIVE_INFINITY)); + expectEquals64(0xbff0000000000000L, d2long(-1.0d)); + expectEquals64(0x8000000000000000L, d2long(-0.0d)); + expectEquals64(0x0000000000000000L, d2long(+0.0d)); + expectEquals64(0x3ff0000000000000L, d2long(+1.0d)); + expectEquals64(0x7ff0000000000000L, d2long(Double.POSITIVE_INFINITY)); + + // A few others. + for (long l = 0; l <= 100; l++) { + expectEquals64(l, d2long(Double.longBitsToDouble(l))); + } + + // A few NaN numbers. + double[] dvals = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dvals.length; i++) { + expectEquals64(0x7ff8000000000000L, d2long(dvals[i])); + } + + System.out.println("passed"); + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + + Integer.toHexString(expected) + + ", found: " + + Integer.toHexString(result)); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + + Long.toHexString(expected) + + ", found: " + + Long.toHexString(result)); + } + } +} diff --git a/test/578-bce-visit/expected.txt b/test/578-bce-visit/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..28fca2cc1235c7c0f328d536d97f3614791049d2 --- /dev/null +++ b/test/578-bce-visit/expected.txt @@ -0,0 +1,2 @@ +exception caught +FUZZ result = 1001 16 diff --git a/test/578-bce-visit/info.txt b/test/578-bce-visit/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..2462e1b8b0010389c9086a02c1ca5712a7be4259 --- /dev/null +++ b/test/578-bce-visit/info.txt @@ -0,0 +1 @@ +Fuzz test that exposed bug in bounds check elimination visiting of blocks. diff --git a/test/578-bce-visit/src/Main.java b/test/578-bce-visit/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..b0e920e163f96acaa19dba39b7e55b5879f16bdf --- /dev/null +++ b/test/578-bce-visit/src/Main.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +/** + * Automatically generated fuzz test that exposed bug in the way bounds + * check elimination visits basic blocks. If, after dynamic bce, the same + * block would be visited again, then static length based bce would incorrectly + * feed information back to itself and removed a necessary bounds check. + */ +public class Main { + + private static int[][][] mA = new int[10][10][10]; + + private static int mX = 17; + + private static int doit() { + int l0 = (((++mA[7][2][8]) <= mA[0][1][3]) ? (++mA[9][0][5]) : ((( -mA[0][7][0]) * ((mX == mX) ? 180 : mX)) + (mA[7][8][8]++))); + mA[1][0][4] -= mX; + int l1 = (((l0 >= ( ~mA[6][7][5])) && ((921 <= l0) && (mA[3][9][6] > l0))) ? mX : (l0--)); + int l2 = ( -384); + for (int i0 = 7 - 1; i0 >= 1; i0--) { + mA[6][0][0] -= ((((l0++) == ( -mX)) ? (((mA[3][i0][1] > 503) || (mX <= i0)) ? (--l0) : (l0--)) : mX) - ( ~(mX--))); + int l3 = 24; + int l4 = ((l2--) & mX); + for (int i1 = i0-2 - 1; i1 >= 3; i1--) { + for (int i2 = 2; i2 < i0; i2++) { + mA[i0][4][l3] >>= 1; + } + } + } + return 1; + } + + public static void main(String[] args) { + int k = 1; + for (int i0 = 0; i0 < 10; i0++) + for (int i1 = 0; i1 < 10; i1++) + for (int i2 = 0; i2 < 10; i2++) + mA[i0][i1][i2] = k++; + try { + k = doit(); + } catch (Exception e) { + System.out.println("exception caught"); + } + System.out.println("FUZZ result = " + k + " " + mX); + } +} diff --git a/test/579-inline-infinite/expected.txt b/test/579-inline-infinite/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/579-inline-infinite/info.txt b/test/579-inline-infinite/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..6fb917c22228dfe53a6128724509d25d1c866c95 --- /dev/null +++ b/test/579-inline-infinite/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing. +Inlining of method with infinite loop cause a crash. diff --git a/test/579-inline-infinite/src/Main.java b/test/579-inline-infinite/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..f214ed4ffd932be87c6c0283ac9e9dbfe486a7c0 --- /dev/null +++ b/test/579-inline-infinite/src/Main.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Infinite implements Runnable { + public int field; + + private final void $noinline$infinite() { + while(true) { + field++; + } + } + + public void run() { + $noinline$infinite(); + } +} + +public class Main { + public static void main(String[] args) { + Thread thr = new Thread(new Infinite()); + thr.setDaemon(true); + thr.start(); + // This is a compiler test, so just finish. + } +} diff --git a/test/580-checker-round/expected.txt b/test/580-checker-round/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..b0aad4deb5bb3fb6b422e222ec14bf5e4b99babe --- /dev/null +++ b/test/580-checker-round/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/580-checker-round/info.txt b/test/580-checker-round/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6397fd13db44e027a1cbd021df4a7e553fa9a3a --- /dev/null +++ b/test/580-checker-round/info.txt @@ -0,0 +1 @@ +Unit test for float/double rounding. diff --git a/test/580-checker-round/src/Main.java b/test/580-checker-round/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..9e248ef95af7f68ef4068cb86734429d44852d12 --- /dev/null +++ b/test/580-checker-round/src/Main.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: int Main.round32(float) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathRoundFloat + /// CHECK-DAG: Return [<>] + private static int round32(float f) { + return Math.round(f); + } + + /// CHECK-START: long Main.round64(double) intrinsics_recognition (after) + /// CHECK-DAG: <> InvokeStaticOrDirect intrinsic:MathRoundDouble + /// CHECK-DAG: Return [<>] + private static long round64(double d) { + return Math.round(d); + } + + public static void main(String args[]) { + // A few obvious numbers. + expectEquals32(-2147483648, round32(Float.NEGATIVE_INFINITY)); + expectEquals32(-2, round32(-1.51f)); + expectEquals32(-1, round32(-1.2f)); + expectEquals32(-1, round32(-1.0f)); + expectEquals32(-1, round32(-0.51f)); + expectEquals32(0, round32(-0.2f)); + expectEquals32(0, round32(-0.0f)); + expectEquals32(0, round32(+0.0f)); + expectEquals32(0, round32(+0.2f)); + expectEquals32(1, round32(+0.5f)); + expectEquals32(1, round32(+1.0f)); + expectEquals32(1, round32(+1.2f)); + expectEquals32(2, round32(+1.5f)); + expectEquals32(2147483647, round32(Float.POSITIVE_INFINITY)); + + // Some others. + for (int i = -100; i <= 100; ++i) { + expectEquals32(i - 1, round32((float) i - 0.51f)); + expectEquals32(i, round32((float) i)); + expectEquals32(i + 1, round32((float) i + 0.5f)); + } + for (float f = -1.5f; f <= -1.499f; f = Math.nextAfter(f, Float.POSITIVE_INFINITY)) { + expectEquals32(-1, round32(f)); + } + + // Some harder. + float[] fvals = { + -16777215.5f, + -16777215.0f, + -0.4999f, + 0.4999f, + 16777215.0f, + 16777215.5f + }; + int[] ivals = { + -16777216, + -16777215, + 0, + 0, + 16777215, + 16777216 + }; + for (int i = 0; i < fvals.length; i++) { + expectEquals32(ivals[i], round32(fvals[i])); + } + + // A few NaN numbers. + float[] fnans = { + Float.intBitsToFloat(0x7f800001), + Float.intBitsToFloat(0x7fa00000), + Float.intBitsToFloat(0x7fc00000), + Float.intBitsToFloat(0x7fffffff), + Float.intBitsToFloat(0xff800001), + Float.intBitsToFloat(0xffa00000), + Float.intBitsToFloat(0xffc00000), + Float.intBitsToFloat(0xffffffff) + }; + for (int i = 0; i < fnans.length; i++) { + expectEquals32(0, round32(fnans[i])); + } + + // A few obvious numbers. + expectEquals64(-9223372036854775808L, round64(Double.NEGATIVE_INFINITY)); + expectEquals64(-2L, round64(-1.51d)); + expectEquals64(-1L, round64(-1.2d)); + expectEquals64(-1L, round64(-1.0d)); + expectEquals64(-1L, round64(-0.51d)); + expectEquals64(0L, round64(-0.2d)); + expectEquals64(0L, round64(-0.0d)); + expectEquals64(0L, round64(+0.0d)); + expectEquals64(0L, round64(+0.2d)); + expectEquals64(1L, round64(+0.5d)); + expectEquals64(1L, round64(+1.0d)); + expectEquals64(1L, round64(+1.2d)); + expectEquals64(2L, round64(+1.5d)); + expectEquals64(9223372036854775807L, round64(Double.POSITIVE_INFINITY)); + + // Some others. + for (long l = -100; l <= 100; ++l) { + expectEquals64(l - 1, round64((double) l - 0.51d)); + expectEquals64(l + 1, round64((double) l + 0.5d)); + expectEquals64(l + 1, round64((double) l + 0.5d)); + } + for (double d = -1.5d; d <= -1.49999999999d; d = Math.nextAfter(d, Double.POSITIVE_INFINITY)) { + expectEquals64(-1L, round64(d)); + } + + // Some harder. + double[] dvals = { + -9007199254740991.5d, + -9007199254740991.0d, + -0.49999999999999994d, + 0.49999999999999994d, + 9007199254740991.0d, + 9007199254740991.5d + }; + long[] lvals = { + -9007199254740992L, + -9007199254740991L, + 0L, + 0L, + 9007199254740991L, + 9007199254740992L + }; + for (int i = 0; i < dvals.length; i++) { + expectEquals64(lvals[i], round64(dvals[i])); + } + + // A few NaN numbers. + double[] dnans = { + Double.longBitsToDouble(0x7ff0000000000001L), + Double.longBitsToDouble(0x7ff4000000000000L), + Double.longBitsToDouble(0x7ff8000000000000L), + Double.longBitsToDouble(0x7fffffffffffffffL), + Double.longBitsToDouble(0xfff0000000000001L), + Double.longBitsToDouble(0xfff4000000000000L), + Double.longBitsToDouble(0xfff8000000000000L), + Double.longBitsToDouble(0xffffffffffffffffL) + }; + for (int i = 0; i < dnans.length; i++) { + expectEquals64(0L, round64(dnans[i])); + } + + System.out.println("passed"); + } + + private static void expectEquals32(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static void expectEquals64(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/580-checker-string-factory-intrinsics/expected.txt b/test/580-checker-string-factory-intrinsics/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..86e041dad66a19b9518b83b78865015f62662f75 --- /dev/null +++ b/test/580-checker-string-factory-intrinsics/expected.txt @@ -0,0 +1,3 @@ +foo +bar +baz diff --git a/test/580-checker-string-factory-intrinsics/info.txt b/test/580-checker-string-factory-intrinsics/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..3d01a1964ab1d101ebe5a4834a3c4c6843e29da3 --- /dev/null +++ b/test/580-checker-string-factory-intrinsics/info.txt @@ -0,0 +1 @@ +Ensure java.lang.StringFactory intrinsics are recognized and used. diff --git a/test/580-checker-string-factory-intrinsics/src/Main.java b/test/580-checker-string-factory-intrinsics/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..a2e34bffd0ea18cd9f3b99058016485663e411b0 --- /dev/null +++ b/test/580-checker-string-factory-intrinsics/src/Main.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: void Main.testNewStringFromBytes() builder (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromBytes intrinsic:None + + /// CHECK-START: void Main.testNewStringFromBytes() intrinsics_recognition (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromBytes intrinsic:StringNewStringFromBytes + + public static void testNewStringFromBytes() { + byte[] bytes = { 'f', 'o', 'o' }; + String s = StringFactory.newStringFromBytes(bytes, 0, 0, 3); + System.out.println(s); + } + + // The (native) method + // + // java.lang.StringFactory.newStringFromChars(int offset, int charCount, char[] data) + // + // is recognized as intrinsic StringNewStringFromChars. However, + // because this method is not public, we cannot call it and check + // that the compiler actually intrinsifies it (as it does for the + // StringNewStringFromBytes and StringNewStringFromString + // intrinsics) with Checker. + // + // We can call a public method such as + // + // java.lang.StringFactory.newStringFromChars(char[] data) + // + // which contains a call to the former (non-public) native method. + // However, this call will not be inlined (because it is a method in + // another Dex file and which contains a call, which needs an + // environment), so we cannot use Checker here to ensure the native + // call was intrinsified either. + + /// CHECK-START: void Main.testNewStringFromChars() builder (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None + + /// CHECK-START: void Main.testNewStringFromChars() intrinsics_recognition (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None + + /// CHECK-START: void Main.testNewStringFromChars() inliner (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromChars intrinsic:None + + public static void testNewStringFromChars() { + char[] chars = { 'b', 'a', 'r' }; + String s = StringFactory.newStringFromChars(chars); + System.out.println(s); + } + + /// CHECK-START: void Main.testNewStringFromString() builder (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromString intrinsic:None + + /// CHECK-START: void Main.testNewStringFromString() intrinsics_recognition (after) + /// CHECK-DAG: InvokeStaticOrDirect method_name:java.lang.StringFactory.newStringFromString intrinsic:StringNewStringFromString + + public static void testNewStringFromString() { + String s1 = "baz"; + String s2 = StringFactory.newStringFromString(s1); + System.out.println(s2); + } + + public static void main(String[] args) throws Exception { + testNewStringFromBytes(); + testNewStringFromChars(); + testNewStringFromString(); + } +} diff --git a/test/581-rtp/expected.txt b/test/581-rtp/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/581-rtp/info.txt b/test/581-rtp/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..b57449ae4840a2c0e3f46d7461b60f4777174e38 --- /dev/null +++ b/test/581-rtp/info.txt @@ -0,0 +1,2 @@ +Regression test for the reference type propagation pass +of the optimizing compiler that used to break invariants. diff --git a/test/581-rtp/src/Main.java b/test/581-rtp/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..09f6f6c096b16ee36f82b99c7a8d64e6beb4ac23 --- /dev/null +++ b/test/581-rtp/src/Main.java @@ -0,0 +1,44 @@ +/* + * 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 final class Main { + + /// CHECK-START: void Main.main(String[]) builder (after) + /// CHECK: StaticFieldGet klass:Main[] exact: true + /// CHECK: ArrayGet klass:Main exact:true + /// CHECK: BoundType klass:Main exact:true + public static void main(String[] args) { + Object o = null; + Main f = a[0]; + for (int i = 0; i < 2; ++i) { + // We used to crash in the fixed point iteration of + // the reference type propagation while handling the instanceof: + // we were expecting `o` to get the same exact-ness as the + // `HBoundType` but the typing of the `ArrayGet` used to not + // propagate the exact-ness. + if (o instanceof Main) { + field = o; + } + o = f; + } + if (field != null) { + throw new Error("Expected null"); + } + } + + static Main[] a = new Main[1]; + static Object field; +} diff --git a/test/960-default-smali/build b/test/960-default-smali/build index b72afcdf18f095e60364d110c1fe5b2f87051d23..e8f4ed084a4ff4d0d9dba2300558fa7f5f817ac3 100755 --- a/test/960-default-smali/build +++ b/test/960-default-smali/build @@ -17,27 +17,14 @@ # make us exit on a failure set -e -# Generate the smali Main.smali file or fail -${ANDROID_BUILD_TOP}/art/test/utils/python/generate_smali_main.py ./smali - -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 >& /dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi +# Generate the Main.java file or fail +${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/960-default-smali/smali/A.smali b/test/960-default-smali/smali/A.smali deleted file mode 100644 index e755612fbe0f1d13a610c2ff33e0a08bd0aee18b..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/A.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LA; -.super Ljava/lang/Object; -.implements LGreeter; - -# class A implements Greeter { -# public String SayHi() { -# return "Hi "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .registers 1 - - const-string v0, "Hi " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Attendant.smali b/test/960-default-smali/smali/Attendant.smali deleted file mode 100644 index ab63aeefcb3328c503a236645f85af10a8512562..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/Attendant.smali +++ /dev/null @@ -1,53 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LAttendant; -.super Ljava/lang/Object; - -# public interface Attendant { -# public default String SayHi() { -# return "welcome to " + GetPlace(); -# } -# public default String SayHiTwice() { -# return SayHi() + SayHi(); -# } -# -# public String GetPlace(); -# } - -.method public SayHi()Ljava/lang/String; - .locals 2 - const-string v0, "welcome to " - invoke-interface {p0}, LAttendant;->GetPlace()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SayHiTwice()Ljava/lang/String; - .locals 2 - invoke-interface {p0}, LAttendant;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-interface {p0}, LAttendant;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public abstract GetPlace()Ljava/lang/String; -.end method diff --git a/test/960-default-smali/smali/B.smali b/test/960-default-smali/smali/B.smali deleted file mode 100644 index d847dd12ff3d1a8a0ab58e2c909d7328a0f39969..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/B.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LB; -.super Ljava/lang/Object; -.implements LGreeter2; - -# class B implements Greeter2 { -# public String SayHi() { -# return "Hello "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .registers 1 - - const-string v0, "Hello " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/C.smali b/test/960-default-smali/smali/C.smali deleted file mode 100644 index 08a8508be1a5633aceb9f731db442b4599132cac..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/C.smali +++ /dev/null @@ -1,37 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LC; -.super LA; - -# class C extends A { -# public String SayHiTwice() { -# return "You don't control me"; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - -.method public SayHiTwice()Ljava/lang/String; - .registers 1 - - const-string v0, "You don't control me" - return-object v0 -.end method diff --git a/test/960-default-smali/smali/D.smali b/test/960-default-smali/smali/D.smali deleted file mode 100644 index 32f3b7ec8b028598c5bd11a09519a2d0fb1461ab..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/D.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LD; -.super Ljava/lang/Object; -.implements LGreeter3; - -# class D implements Greeter3 { -# public String GetName() { -# return "Alex "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public GetName()Ljava/lang/String; - .registers 1 - - const-string v0, "Alex " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/E.smali b/test/960-default-smali/smali/E.smali deleted file mode 100644 index bae62504143cf7aa96e1029557a9a6f40433fd6f..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/E.smali +++ /dev/null @@ -1,38 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LE; -.super LA; -.implements LGreeter2; - -# class E extends A implements Greeter2 { -# public String SayHi() { -# return "Hi2 "; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .registers 1 - - const-string v0, "Hi2 " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Extension.smali b/test/960-default-smali/smali/Extension.smali deleted file mode 100644 index 60ffa26ec68433303e2e406288a455e9f1378326..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/Extension.smali +++ /dev/null @@ -1,30 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LExtension; -.super Ljava/lang/Object; - -# public interface Extension { -# public default String SayHi() { -# return "welcome "; -# } -# } - -.method public SayHi()Ljava/lang/String; - .locals 1 - const-string v0, "welcome " - return-object v0 -.end method diff --git a/test/960-default-smali/smali/F.smali b/test/960-default-smali/smali/F.smali deleted file mode 100644 index 3eaa089e1ffdba7fd35589aca12e2a23753d6df8..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/F.smali +++ /dev/null @@ -1,47 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LF; -.super LA; -.implements LAttendant; - -# class F extends A implements Attendant { -# public String GetPlace() { -# return "android"; -# } -# public String SayHiTwice() { -# return "We can override both interfaces"; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHiTwice()Ljava/lang/String; - .registers 1 - - const-string v0, "We can override both interfaces" - return-object v0 -.end method - -.method public GetPlace()Ljava/lang/String; - .registers 1 - const-string v0, "android" - return-object v0 -.end method diff --git a/test/960-default-smali/smali/G.smali b/test/960-default-smali/smali/G.smali deleted file mode 100644 index 446f2a4c646593c0078784471326ffe4f5ebfc88..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/G.smali +++ /dev/null @@ -1,37 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LG; -.super Ljava/lang/Object; -.implements LAttendant; - -# class G implements Attendant { -# public String GetPlace() { -# return "android"; -# } -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public GetPlace()Ljava/lang/String; - .registers 1 - const-string v0, "android" - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Greeter.smali b/test/960-default-smali/smali/Greeter.smali deleted file mode 100644 index 28530ffc6f5f49720405f2777e9f39a7f455ccb8..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/Greeter.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LGreeter; -.super Ljava/lang/Object; - -# public interface Greeter { -# public String SayHi(); -# -# public default String SayHiTwice() { -# return SayHi() + SayHi(); -# } -# } - -.method public abstract SayHi()Ljava/lang/String; -.end method - -.method public SayHiTwice()Ljava/lang/String; - .locals 2 - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Greeter2.smali b/test/960-default-smali/smali/Greeter2.smali deleted file mode 100644 index ace1798bab8f075be4e6f13a3c73073185a6567a..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/Greeter2.smali +++ /dev/null @@ -1,39 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LGreeter2; -.super Ljava/lang/Object; -.implements LGreeter; - -# public interface Greeter2 extends Greeter { -# public default String SayHiTwice() { -# return "I say " + SayHi() + SayHi(); -# } -# } - -.method public SayHiTwice()Ljava/lang/String; - .locals 3 - const-string v0, "I say " - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - invoke-interface {p0}, LGreeter;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/960-default-smali/smali/Greeter3.smali b/test/960-default-smali/smali/Greeter3.smali deleted file mode 100644 index 31fc2e79ff3fecc6599b71a538e16cc70ba49832..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/Greeter3.smali +++ /dev/null @@ -1,40 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public abstract interface LGreeter3; -.super Ljava/lang/Object; -.implements LGreeter; - -# public interface Greeter3 extends Greeter { -# public String GetName(); -# -# public default String SayHi() { -# return "Hello " + GetName(); -# } -# } - -.method public abstract GetName()Ljava/lang/String; -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - const-string v0, "Hello " - invoke-interface {p0}, LGreeter3;->GetName()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/960-default-smali/smali/H.smali b/test/960-default-smali/smali/H.smali deleted file mode 100644 index 82065ea49d7a9109966e95d60c206a1aa7eff493..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/H.smali +++ /dev/null @@ -1,28 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LH; -.super Ljava/lang/Object; -.implements LExtension; - -# class H implements Extension { -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/960-default-smali/smali/I.smali b/test/960-default-smali/smali/I.smali deleted file mode 100644 index 72fb58afe49c028fa04ee91a1330458914a1f849..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/I.smali +++ /dev/null @@ -1,28 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LI; -.super LA; -.implements LGreeter2; - -# class I extends A implements Greeter2 { -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/960-default-smali/smali/J.smali b/test/960-default-smali/smali/J.smali deleted file mode 100644 index 93f3d6231c613b5b9be2d206ab055ef6d2f14f5d..0000000000000000000000000000000000000000 --- a/test/960-default-smali/smali/J.smali +++ /dev/null @@ -1,29 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LJ; -.super LA; - -# class J extends A { -# } - - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - diff --git a/test/960-default-smali/src/A.java b/test/960-default-smali/src/A.java new file mode 100644 index 0000000000000000000000000000000000000000..7664a263f1ab7b4988535bad567530d53364a7cc --- /dev/null +++ b/test/960-default-smali/src/A.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class A implements Greeter { + public String SayHi() { + return "Hi "; + } +} diff --git a/test/960-default-smali/src/Attendant.java b/test/960-default-smali/src/Attendant.java new file mode 100644 index 0000000000000000000000000000000000000000..9f9a58a402f202256ff859309136e494befcc75b --- /dev/null +++ b/test/960-default-smali/src/Attendant.java @@ -0,0 +1,24 @@ +/* + * Copyright 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 interface Attendant { + public default String SayHi() { + return "welcome to " + GetPlace(); + } + public default String SayHiTwice() { + return SayHi() + SayHi(); + } + public String GetPlace(); +} diff --git a/test/960-default-smali/src/B.java b/test/960-default-smali/src/B.java new file mode 100644 index 0000000000000000000000000000000000000000..18aaadea4c7793b6a9997ff5400e75f05f54eea5 --- /dev/null +++ b/test/960-default-smali/src/B.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class B implements Greeter2 { + public String SayHi() { + return "Hello "; + } +} diff --git a/test/960-default-smali/src/C.java b/test/960-default-smali/src/C.java new file mode 100644 index 0000000000000000000000000000000000000000..f0bc185f95968326d8a70572571aae50bc434f67 --- /dev/null +++ b/test/960-default-smali/src/C.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class C extends A { + public String SayHiTwice() { + return "You don't control me"; + } +} diff --git a/test/960-default-smali/src/D.java b/test/960-default-smali/src/D.java new file mode 100644 index 0000000000000000000000000000000000000000..b1697cd865628e0583febe6d21decc8fff122c18 --- /dev/null +++ b/test/960-default-smali/src/D.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class D implements Greeter3 { + public String GetName() { + return "Alex "; + } +} diff --git a/test/960-default-smali/src/E.java b/test/960-default-smali/src/E.java new file mode 100644 index 0000000000000000000000000000000000000000..477cb6727cade871aae445276ef0d03ea4eb23de --- /dev/null +++ b/test/960-default-smali/src/E.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class E extends A implements Greeter2 { + public String SayHi() { + return "Hi2 "; + } +} diff --git a/test/960-default-smali/src/Extension.java b/test/960-default-smali/src/Extension.java new file mode 100644 index 0000000000000000000000000000000000000000..89617ddab66ae0e116695d0ba0533f114435b3cf --- /dev/null +++ b/test/960-default-smali/src/Extension.java @@ -0,0 +1,20 @@ +/* + * Copyright 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 interface Extension { + public default String SayHi() { + return "welcome "; + } +} diff --git a/test/960-default-smali/src/F.java b/test/960-default-smali/src/F.java new file mode 100644 index 0000000000000000000000000000000000000000..0282de7793797d62d3321f237e71443341817b23 --- /dev/null +++ b/test/960-default-smali/src/F.java @@ -0,0 +1,23 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class F extends A implements Attendant { + public String GetPlace() { + return "android"; + } + public String SayHiTwice() { + return "We can override both interfaces"; + } +} diff --git a/test/960-default-smali/src/G.java b/test/960-default-smali/src/G.java new file mode 100644 index 0000000000000000000000000000000000000000..86a140aa43b92f4668f9e8e4843b7820e573e55f --- /dev/null +++ b/test/960-default-smali/src/G.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class G implements Attendant { + public String GetPlace() { + return "android"; + } +} diff --git a/test/960-default-smali/src/Greeter.java b/test/960-default-smali/src/Greeter.java new file mode 100644 index 0000000000000000000000000000000000000000..cee2283acde06cffe4d8c8e3b548d534516a3261 --- /dev/null +++ b/test/960-default-smali/src/Greeter.java @@ -0,0 +1,21 @@ +/* + * Copyright 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 interface Greeter { + public String SayHi(); + public default String SayHiTwice() { + return SayHi() + SayHi(); + } +} diff --git a/test/960-default-smali/src/Greeter2.java b/test/960-default-smali/src/Greeter2.java new file mode 100644 index 0000000000000000000000000000000000000000..07f6c53841ad1f9defb698a9afc4ec07c3316dbe --- /dev/null +++ b/test/960-default-smali/src/Greeter2.java @@ -0,0 +1,20 @@ +/* + * Copyright 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 interface Greeter2 extends Greeter { + public default String SayHiTwice() { + return "I say " + SayHi() + SayHi(); + } +} diff --git a/test/960-default-smali/src/Greeter3.java b/test/960-default-smali/src/Greeter3.java new file mode 100644 index 0000000000000000000000000000000000000000..bbb7171a3688a53b59d08be1db6df9954e78fe87 --- /dev/null +++ b/test/960-default-smali/src/Greeter3.java @@ -0,0 +1,21 @@ +/* + * Copyright 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 interface Greeter3 extends Greeter { + public String GetName(); + public default String SayHi() { + return "Hello " + GetName(); + } +} diff --git a/test/960-default-smali/src/H.java b/test/960-default-smali/src/H.java new file mode 100644 index 0000000000000000000000000000000000000000..d87a6db8f4e66f424b4a8d68bcd3efa840e9c1e1 --- /dev/null +++ b/test/960-default-smali/src/H.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class H implements Extension { } diff --git a/test/960-default-smali/src/I.java b/test/960-default-smali/src/I.java new file mode 100644 index 0000000000000000000000000000000000000000..8d6779cd2774bc3c97709db3f76f7408d61a2bb5 --- /dev/null +++ b/test/960-default-smali/src/I.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class I extends A implements Greeter2 { } diff --git a/test/960-default-smali/src/J.java b/test/960-default-smali/src/J.java new file mode 100644 index 0000000000000000000000000000000000000000..a365e406c6f74a4f420c7c1fbf54eeac591451bb --- /dev/null +++ b/test/960-default-smali/src/J.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class J extends A { } diff --git a/test/960-default-smali/smali/classes.xml b/test/960-default-smali/src/classes.xml similarity index 100% rename from test/960-default-smali/smali/classes.xml rename to test/960-default-smali/src/classes.xml diff --git a/test/961-default-iface-resolution-generated/build b/test/961-default-iface-resolution-generated/build index 005f76c2dccce0a55fcc8babb77b5b626f58cdab..ccebbe4ac99105f1fafce25fdde7a571b54a56ac 100755 --- a/test/961-default-iface-resolution-generated/build +++ b/test/961-default-iface-resolution-generated/build @@ -26,32 +26,19 @@ restore_ulimit() { } trap 'restore_ulimit' ERR -mkdir -p ./smali - -# Generate the smali files and expected.txt or fail -./util-src/generate_smali.py ./smali ./expected.txt - -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ $USE_JACK == "true" ]]; then - if "$JACK" -D jack.java.source.version=1.8 >& /dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi +mkdir -p ./src + +# Generate the smali files and expected.txt or fail +./util-src/generate_java.py ./src ./expected.txt -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods # Reset the ulimit back to its initial value restore_ulimit diff --git a/test/961-default-iface-resolution-generated/util-src/generate_smali.py b/test/961-default-iface-resolution-generated/util-src/generate_java.py similarity index 71% rename from test/961-default-iface-resolution-generated/util-src/generate_smali.py rename to test/961-default-iface-resolution-generated/util-src/generate_java.py index 921a096dd32f78e8f49d1812e650bd158b175554..a205cd6ce06529e8925bfaec20e8f4f706b6c041 100755 --- a/test/961-default-iface-resolution-generated/util-src/generate_smali.py +++ b/test/961-default-iface-resolution-generated/util-src/generate_java.py @@ -15,7 +15,7 @@ # limitations under the License. """ -Generate Smali test files for test 961. +Generate Java test files for test 961. """ import os @@ -43,48 +43,27 @@ import string # every possible interface tree up to 5 layers deep. MAX_IFACE_DEPTH = 5 -class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): +class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin): """ - A Main.smali file containing the Main class and the main function. It will run + A Main.java file containing the Main class and the main function. It will run all the test functions we have. """ MAIN_CLASS_TEMPLATE = """{copyright} - -.class public LMain; -.super Ljava/lang/Object; - -# class Main {{ - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - +class Main {{ {test_groups} - {main_func} - -# }} +}} """ MAIN_FUNCTION_TEMPLATE = """ -# public static void main(String[] args) {{ -.method public static main([Ljava/lang/String;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - + public static void main(String[] args) {{ {test_group_invoke} - - return-void -.end method -# }} + }} """ TEST_GROUP_INVOKE_TEMPLATE = """ -# {test_name}(); - invoke-static {{}}, {test_name}()V + {test_name}(); """ def __init__(self): @@ -114,7 +93,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): def __str__(self): """ - Print the MainClass smali code. + Print the MainClass java code. """ all_tests = sorted(self.tests) test_invoke = "" @@ -125,7 +104,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name()) main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke) - return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("smali"), + return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("java"), test_groups = test_groups, main_func = main_func) @@ -136,49 +115,18 @@ class Func(mixins.Named, mixins.NameComparableMixin): """ TEST_FUNCTION_TEMPLATE = """ -# public static void {fname}() {{ -# try {{ -# {farg} v = new {farg}(); -# System.out.printf("%s calls default method on %s\\n", -# v.CalledClassName(), -# v.CalledInterfaceName()); -# return; -# }} catch (Error e) {{ -# e.printStackTrace(System.out); -# return; -# }} -# }} -.method public static {fname}()V - .locals 7 - :call_{fname}_try_start - new-instance v6, L{farg}; - invoke-direct {{v6}}, L{farg};->()V - - const/4 v0, 2 - new-array v1,v0, [Ljava/lang/Object; - const/4 v0, 0 - invoke-virtual {{v6}}, L{farg};->CalledClassName()Ljava/lang/String; - move-result-object v4 - aput-object v4,v1,v0 - - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v3, "%s calls default method on %s\\n" - - invoke-virtual {{v6}}, L{farg};->CalledInterfaceName()Ljava/lang/String; - move-result-object v4 - const/4 v0, 1 - aput-object v4, v1, v0 - - invoke-virtual {{v2,v3,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; - return-void - :call_{fname}_try_end - .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start - :error_{fname}_start - move-exception v3 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V - return-void -.end method + public static void {fname}() {{ + try {{ + {farg} v = new {farg}(); + System.out.printf("%s calls default method on %s\\n", + v.CalledClassName(), + v.CalledInterfaceName()); + return; + }} catch (Error e) {{ + e.printStackTrace(System.out); + return; + }} + }} """ def __init__(self, farg): @@ -202,38 +150,21 @@ class Func(mixins.Named, mixins.NameComparableMixin): def __str__(self): """ - Print the smali code of this function. + Print the java code of this function. """ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name()) -class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ A class that will be instantiated to test default method resolution order. """ TEST_CLASS_TEMPLATE = """{copyright} - -.class public L{class_name}; -.super Ljava/lang/Object; -.implements L{iface_name}; - -# public class {class_name} implements {iface_name} {{ -# public String CalledClassName() {{ -# return "{tree}"; -# }} -# }} - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - -.method public CalledClassName()Ljava/lang/String; - .locals 1 - const-string v0, "{tree}" - return-object v0 -.end method +public class {class_name} implements {iface_name} {{ + public String CalledClassName() {{ + return "{tree}"; + }} +}} """ def __init__(self, iface): @@ -276,46 +207,30 @@ class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixi def __str__(self): """ - Print the smali code of this class. + Print the java code of this class. """ - return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), + return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'), iface_name = self.iface.get_name(), tree = self.get_tree(), class_name = self.class_name) -class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ An interface that will be used to test default method resolution order. """ TEST_INTERFACE_TEMPLATE = """{copyright} -.class public abstract interface L{class_name}; -.super Ljava/lang/Object; -{implements_spec} - -# public interface {class_name} {extends} {ifaces} {{ -# public String CalledClassName(); -.method public abstract CalledClassName()Ljava/lang/String; -.end method +public interface {class_name} {extends} {ifaces} {{ + public String CalledClassName(); {funcs} - -# }} +}} """ DEFAULT_FUNC_TEMPLATE = """ -# public default String CalledInterfaceName() {{ -# return "{tree}"; -# }} -.method public CalledInterfaceName()Ljava/lang/String; - .locals 1 - const-string v0, "{tree}" - return-object v0 -.end method -""" - - IMPLEMENTS_TEMPLATE = """ -.implements L{iface_name}; + public default String CalledInterfaceName() {{ + return "{tree}"; + }} """ def __init__(self, ifaces, default): @@ -357,12 +272,10 @@ class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, def __str__(self): """ - Print the smali code of this interface. + Print the java code of this interface. """ - s_ifaces = " " j_ifaces = " " for i in self.ifaces: - s_ifaces += self.IMPLEMENTS_TEMPLATE.format(iface_name = i.get_name()) j_ifaces += " {},".format(i.get_name()) j_ifaces = j_ifaces[0:-1] if self.default: @@ -371,8 +284,7 @@ class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, class_name = self.class_name) else: funcs = "" - return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'), - implements_spec = s_ifaces, + return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'), extends = "extends" if len(self.ifaces) else "", ifaces = j_ifaces, funcs = funcs, @@ -451,16 +363,16 @@ def create_all_test_files(): return mc, classes def main(argv): - smali_dir = Path(argv[1]) - if not smali_dir.exists() or not smali_dir.is_dir(): - print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr) + java_dir = Path(argv[1]) + if not java_dir.exists() or not java_dir.is_dir(): + print("{} is not a valid java dir".format(java_dir), file=sys.stderr) sys.exit(1) expected_txt = Path(argv[2]) mainclass, all_files = create_all_test_files() with expected_txt.open('w') as out: print(mainclass.get_expected(), file=out) for f in all_files: - f.dump(smali_dir) + f.dump(java_dir) if __name__ == '__main__': main(sys.argv) diff --git a/test/962-iface-static/build b/test/962-iface-static/build index e17272f769b2ca5c00041fff3309c5ae87ed7cf4..0dd8573f542d0876859ffe9053e01df1bbd14055 100755 --- a/test/962-iface-static/build +++ b/test/962-iface-static/build @@ -17,24 +17,11 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/962-iface-static/smali/Displayer.smali b/test/962-iface-static/smali/Displayer.smali deleted file mode 100644 index ed4c013d3b89a284ad0d94ef6b247fec1905a4bd..0000000000000000000000000000000000000000 --- a/test/962-iface-static/smali/Displayer.smali +++ /dev/null @@ -1,45 +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. -# */ -# -# public class Displayer { -# static { -# System.out.println("init"); -# } -# -# public Displayer() { -# System.out.println("constructor"); -# } -# } - -.class public LDisplayer; -.super Ljava/lang/Object; - -.method static constructor ()V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "init" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - return-void -.end method - -.method public constructor ()V - .locals 2 - invoke-direct {p0}, Ljava/lang/Object;->()V - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "constructor" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - return-void -.end method diff --git a/test/962-iface-static/smali/Main.smali b/test/962-iface-static/smali/Main.smali deleted file mode 100644 index 72fa5e0e6e66de947234856ca2628b19d0720197..0000000000000000000000000000000000000000 --- a/test/962-iface-static/smali/Main.smali +++ /dev/null @@ -1,40 +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. -# */ -# -# class Main { -# public static void main(String[] args) { -# System.out.println(iface.SayHi()); -# } -# } -.class public LMain; -.super Ljava/lang/Object; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - invoke-static {}, Liface;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/962-iface-static/smali/iface.smali b/test/962-iface-static/smali/iface.smali deleted file mode 100644 index 5b9c03ec46aac45684570251ab790c23feebc3a7..0000000000000000000000000000000000000000 --- a/test/962-iface-static/smali/iface.smali +++ /dev/null @@ -1,43 +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. -# */ -# -# public interface iface { -# public static final Displayer f = new Displayer(); -# -# public static String SayHi() { -# return "Hello"; -# } -# } - -.class public abstract interface Liface; -.super Ljava/lang/Object; - -.field public final static f:LDisplayer; - -.method static constructor ()V - .locals 3 - new-instance v1, LDisplayer; - invoke-direct {v1}, LDisplayer;->()V - sput-object v1, Liface;->f:LDisplayer; - return-void -.end method - -.method public static SayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hello" - return-object v0 -.end method - diff --git a/test/962-iface-static/src/Displayer.java b/test/962-iface-static/src/Displayer.java new file mode 100644 index 0000000000000000000000000000000000000000..5b28b3f86d3d8874b6aad3301e7d97940b87ff05 --- /dev/null +++ b/test/962-iface-static/src/Displayer.java @@ -0,0 +1,23 @@ +/* + * 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 Displayer { + static { + System.out.println("init"); + } + public Displayer() { + System.out.println("constructor"); + } +} diff --git a/test/962-iface-static/src/Iface.java b/test/962-iface-static/src/Iface.java new file mode 100644 index 0000000000000000000000000000000000000000..82c7808a313fd0485d20f801aa540302e6e15f70 --- /dev/null +++ b/test/962-iface-static/src/Iface.java @@ -0,0 +1,21 @@ +/* + * 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 interface Iface { + public static final Displayer f = new Displayer(); + public static String SayHi() { + return "Hello"; + } +} diff --git a/test/962-iface-static/src/Main.java b/test/962-iface-static/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..7cb8eb7f7bbb2abb8cfd38066afe3b6a3fd17883 --- /dev/null +++ b/test/962-iface-static/src/Main.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main { + public static void main(String[] args) { + System.out.println(Iface.SayHi()); + } +} diff --git a/test/963-default-range-smali/build b/test/963-default-range-smali/build index e17272f769b2ca5c00041fff3309c5ae87ed7cf4..0dd8573f542d0876859ffe9053e01df1bbd14055 100755 --- a/test/963-default-range-smali/build +++ b/test/963-default-range-smali/build @@ -17,24 +17,11 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/963-default-range-smali/smali/A.smali b/test/963-default-range-smali/smali/A.smali deleted file mode 100644 index b3d91dd76b36bf94e8d46f1227c82016f53efb20..0000000000000000000000000000000000000000 --- a/test/963-default-range-smali/smali/A.smali +++ /dev/null @@ -1,29 +0,0 @@ -# /* -# * Copyright 2015 The Android Open Source Project -# * -# * Licensed under the Apache License, Version 2.0 (the "License"); -# * you may not use this file except in compliance with the License. -# * You may obtain a copy of the License at -# * -# * http://www.apache.org/licenses/LICENSE-2.0 -# * -# * Unless required by applicable law or agreed to in writing, software -# * distributed under the License is distributed on an "AS IS" BASIS, -# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# * See the License for the specific language governing permissions and -# * limitations under the License. -# */ - -.class public LA; -.super Ljava/lang/Object; -.implements Liface; - -# class A implements iface { -# } - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - diff --git a/test/963-default-range-smali/smali/Main.smali b/test/963-default-range-smali/smali/Main.smali deleted file mode 100644 index 400fba72d9fc791822f2cfdcf425052825b94906..0000000000000000000000000000000000000000 --- a/test/963-default-range-smali/smali/Main.smali +++ /dev/null @@ -1,77 +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. -# */ -# -# class Main { -# public static void main(String[] args) { -# A a = new A(); -# System.out.println(a.SayHi("a string 0", -# "a string 1", -# "a string 2", -# "a string 3", -# "a string 4", -# "a string 5", -# "a string 6", -# "a string 7", -# "a string 8", -# "a string 9")); -# iface b = (iface)a; -# System.out.println(b.SayHi("a string 0", -# "a string 1", -# "a string 2", -# "a string 3", -# "a string 4", -# "a string 5", -# "a string 6", -# "a string 7", -# "a string 8", -# "a string 9")); -# } -# } -.class public LMain; -.super Ljava/lang/Object; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 15 - sget-object v12, Ljava/lang/System;->out:Ljava/io/PrintStream; - - new-instance v1, LA; - invoke-direct {v1}, LA;->()V - const-string v2, "a string 0" - const-string v3, "a string 1" - const-string v4, "a string 2" - const-string v5, "a string 3" - const-string v6, "a string 4" - const-string v7, "a string 5" - const-string v8, "a string 6" - const-string v9, "a string 7" - const-string v10, "a string 8" - const-string v11, "a string 9" - invoke-virtual/range {v1 .. v11}, LA;->SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - invoke-virtual {v12,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface/range {v1 .. v11}, Liface;->SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - invoke-virtual {v12,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/963-default-range-smali/smali/iface.smali b/test/963-default-range-smali/smali/iface.smali deleted file mode 100644 index c2c3ce69a77324079236864aea50c474ae1c1d0e..0000000000000000000000000000000000000000 --- a/test/963-default-range-smali/smali/iface.smali +++ /dev/null @@ -1,40 +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. -# */ -# -# public interface iface { -# public default String SayHi(String n1, -# String n2, -# String n3, -# String n4, -# String n5, -# String n6, -# String n7, -# String n8, -# String n9, -# String n0) { -# return "Hello"; -# } -# } - -.class public abstract interface Liface; -.super Ljava/lang/Object; - -.method public SayHi(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; - .locals 1 - const-string v0, "Hello" - return-object v0 -.end method - diff --git a/test/963-default-range-smali/src/A.java b/test/963-default-range-smali/src/A.java new file mode 100644 index 0000000000000000000000000000000000000000..617eccba496d6eafc07a176c72e85bf6c886d47c --- /dev/null +++ b/test/963-default-range-smali/src/A.java @@ -0,0 +1,16 @@ +/* + * Copyright 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class A implements Iface { } diff --git a/test/963-default-range-smali/src/Iface.java b/test/963-default-range-smali/src/Iface.java new file mode 100644 index 0000000000000000000000000000000000000000..7556209b54babe3dfc904f08eed2611daa8073c6 --- /dev/null +++ b/test/963-default-range-smali/src/Iface.java @@ -0,0 +1,29 @@ +/* + * 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 interface Iface { + public default String SayHi(String n1, + String n2, + String n3, + String n4, + String n5, + String n6, + String n7, + String n8, + String n9, + String n0) { + return "Hello"; + } +} diff --git a/test/963-default-range-smali/src/Main.java b/test/963-default-range-smali/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..841842dc1234b2df5e454844bb9ff6c06c2aa5cd --- /dev/null +++ b/test/963-default-range-smali/src/Main.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main { + public static void main(String[] args) { + A a = new A(); + System.out.println(a.SayHi("a string 0", + "a string 1", + "a string 2", + "a string 3", + "a string 4", + "a string 5", + "a string 6", + "a string 7", + "a string 8", + "a string 9")); + Iface b = a; + System.out.println(b.SayHi("a string 0", + "a string 1", + "a string 2", + "a string 3", + "a string 4", + "a string 5", + "a string 6", + "a string 7", + "a string 8", + "a string 9")); + } +} diff --git a/test/964-default-iface-init-generated/build b/test/964-default-iface-init-generated/build index 0780da14e206eb529cfd7e31d0b933f59a1f5da8..ccebbe4ac99105f1fafce25fdde7a571b54a56ac 100755 --- a/test/964-default-iface-init-generated/build +++ b/test/964-default-iface-init-generated/build @@ -26,30 +26,19 @@ restore_ulimit() { } trap 'restore_ulimit' ERR -# Generate the smali files and expected.txt or fail -./util-src/generate_smali.py ./smali ./expected.txt - -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi +mkdir -p ./src + +# Generate the smali files and expected.txt or fail +./util-src/generate_java.py ./src ./expected.txt -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods # Reset the ulimit back to its initial value restore_ulimit diff --git a/test/964-default-iface-init-generated/smali/Displayer.smali b/test/964-default-iface-init-generated/smali/Displayer.smali deleted file mode 100644 index 91280a8a42f4912bd917dae43f84da279bca125c..0000000000000000000000000000000000000000 --- a/test/964-default-iface-init-generated/smali/Displayer.smali +++ /dev/null @@ -1,45 +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. -# */ -# -# // This class is b/c java does not allow static {} blocks in interfaces. -# public class Displayer { -# public Displayer(String type) { -# System.out.println("initialization of " + type); -# } -# public void touch() { -# return; -# } -# } - -.class public LDisplayer; -.super Ljava/lang/Object; - -.method public constructor (Ljava/lang/String;)V - .locals 2 - invoke-direct {p0}, Ljava/lang/Object;->()V - const-string v0, "initialization of " - invoke-virtual {v0, p1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - 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 touch()V - .locals 0 - return-void -.end method - diff --git a/test/964-default-iface-init-generated/src/Displayer.java b/test/964-default-iface-init-generated/src/Displayer.java new file mode 100644 index 0000000000000000000000000000000000000000..4be0ab27326e2833157e21d973387bd90e9e01fb --- /dev/null +++ b/test/964-default-iface-init-generated/src/Displayer.java @@ -0,0 +1,24 @@ +/* + * 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. + */ +// This class is b/c java does not allow static {} blocks in interfaces. +public class Displayer { + public Displayer(String type) { + System.out.println("initialization of " + type); + } + public void touch() { + return; + } +} diff --git a/test/964-default-iface-init-generated/util-src/generate_smali.py b/test/964-default-iface-init-generated/util-src/generate_java.py similarity index 68% rename from test/964-default-iface-init-generated/util-src/generate_smali.py rename to test/964-default-iface-init-generated/util-src/generate_java.py index c0ba157109caa162e96ab78520b12079c4da1e5b..b2df49f70ee6c81d09702f29968638480463bcdb 100755 --- a/test/964-default-iface-init-generated/util-src/generate_smali.py +++ b/test/964-default-iface-init-generated/util-src/generate_java.py @@ -15,7 +15,7 @@ # limitations under the License. """ -Generate Smali test files for test 964. +Generate java test files for test 964. """ import os @@ -40,47 +40,27 @@ import string # The max depth the tree can have. MAX_IFACE_DEPTH = 3 -class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): +class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin): """ - A Main.smali file containing the Main class and the main function. It will run + A Main.java file containing the Main class and the main function. It will run all the test functions we have. """ MAIN_CLASS_TEMPLATE = """{copyright} - -.class public LMain; -.super Ljava/lang/Object; - -# class Main {{ - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - +class Main {{ {test_groups} - {main_func} - -# }} +}} """ MAIN_FUNCTION_TEMPLATE = """ -# public static void main(String[] args) {{ -.method public static main([Ljava/lang/String;)V - .locals 2 - + public static void main(String[] args) {{ {test_group_invoke} - - return-void -.end method -# }} + }} """ TEST_GROUP_INVOKE_TEMPLATE = """ -# {test_name}(); - invoke-static {{}}, {test_name}()V + {test_name}(); """ def __init__(self): @@ -110,7 +90,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): def __str__(self): """ - Print the smali code for this test. + Print the java code for this test. """ all_tests = sorted(self.tests) test_invoke = "" @@ -121,7 +101,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name()) main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke) - return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), + return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('java'), test_groups = test_groups, main_func = main_func) @@ -132,46 +112,19 @@ class Func(mixins.Named, mixins.NameComparableMixin): """ TEST_FUNCTION_TEMPLATE = """ -# public static void {fname}() {{ -# try {{ -# System.out.println("About to initialize {tree}"); -# {farg} v = new {farg}(); -# System.out.println("Initialized {tree}"); -# v.touchAll(); -# System.out.println("All of {tree} hierarchy initialized"); -# return; -# }} catch (Error e) {{ -# e.printStackTrace(System.out); -# return; -# }} -# }} -.method public static {fname}()V - .locals 7 - :call_{fname}_try_start - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v3, "About to initialize {tree}" - invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v6, L{farg}; - invoke-direct {{v6}}, L{farg};->()V - - const-string v3, "Initialized {tree}" - invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {{v6}}, L{farg};->touchAll()V - - const-string v3, "All of {tree} hierarchy initialized" - invoke-virtual {{v2, v3}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void - :call_{fname}_try_end - .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start - :error_{fname}_start - move-exception v3 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V - return-void -.end method + public static void {fname}() {{ + try {{ + System.out.println("About to initialize {tree}"); + {farg} v = new {farg}(); + System.out.println("Initialized {tree}"); + v.touchAll(); + System.out.println("All of {tree} hierarchy initialized"); + return; + }} catch (Error e) {{ + e.printStackTrace(System.out); + return; + }} + }} """ OUTPUT_FORMAT = """ @@ -190,7 +143,7 @@ All of {tree} hierarchy initialized def __str__(self): """ - Print the smali code for this test function. + Print the java code for this test function. """ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name(), @@ -211,57 +164,26 @@ All of {tree} hierarchy initialized initialize_output = self.farg.get_initialize_output().strip(), touch_output = self.farg.get_touch_output().strip()) -class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ A class that will be instantiated to test interface initialization order. """ TEST_CLASS_TEMPLATE = """{copyright} - -.class public L{class_name}; -.super Ljava/lang/Object; -{implements_spec} - -# public class {class_name} implements {ifaces} {{ -# -# public {class_name}() {{ -# }} -.method public constructor ()V - .locals 2 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - -# public void marker() {{ -# return; -# }} -.method public marker()V - .locals 0 - return-void -.end method - -# public void touchAll() {{ -.method public touchAll()V - .locals 2 - sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; - {touch_calls} - return-void -.end method -# }} -# }} -""" - - IMPLEMENTS_TEMPLATE = """ -.implements L{iface_name}; +public class {class_name} implements {ifaces} {{ + public void marker() {{ + return; + }} + + public void touchAll() {{ +{touch_calls} + }} +}} """ TOUCH_CALL_TEMPLATE = """ -# System.out.println("{class_name} touching {iface_name}"); -# {iface_name}.field.touch(); - const-string v1, "{class_name} touching {iface_name}" - invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - sget-object v1, L{iface_name};->field:LDisplayer; - invoke-virtual {{v1}}, LDisplayer;->touch()V + System.out.println("{class_name} touching {iface_name}"); + {iface_name}.field.touch(); """ TOUCH_OUTPUT_TEMPLATE = """ @@ -306,63 +228,32 @@ class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixi def __str__(self): """ - Print the smali code for this class. + Print the java code for this class. """ - s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()), - self.ifaces)) j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces)) touches = '\n'.join(map(lambda a: self.TOUCH_CALL_TEMPLATE.format(class_name = self.class_name, iface_name = a.get_name()), self.get_all_interfaces())) - return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), - implements_spec = s_ifaces, + return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'), ifaces = j_ifaces, class_name = self.class_name, touch_calls = touches) -class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): +class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin): """ An interface that will be used to test default method resolution order. """ TEST_INTERFACE_TEMPLATE = """{copyright} -.class public abstract interface L{class_name}; -.super Ljava/lang/Object; -{implements_spec} - -# public interface {class_name} {extends} {ifaces} {{ -# public static final Displayer field = new Displayer("{tree}"); -.field public final static field:LDisplayer; - -.method static constructor ()V - .locals 3 - const-string v2, "{tree}" - new-instance v1, LDisplayer; - invoke-direct {{v1, v2}}, LDisplayer;->(Ljava/lang/String;)V - sput-object v1, L{class_name};->field:LDisplayer; - return-void -.end method - -# public void marker(); -.method public abstract marker()V -.end method - +public interface {class_name} {extends} {ifaces} {{ + public static final Displayer field = new Displayer("{tree}"); + public void marker(); {funcs} - -# }} +}} """ DEFAULT_FUNC_TEMPLATE = """ -# public default void {class_name}_DEFAULT_FUNC() {{ -# return; -# }} -.method public {class_name}_DEFAULT_FUNC()V - .locals 0 - return-void -.end method -""" - IMPLEMENTS_TEMPLATE = """ -.implements L{iface_name}; + public default void {class_name}_DEFAULT_FUNC() {{ return; }} """ OUTPUT_TEMPLATE = "initialization of {tree}" @@ -429,17 +320,14 @@ class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, def __str__(self): """ - Print the smali code for this interface. + Print the java code for this interface. """ - s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()), - self.ifaces)) j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces)) if self.default: funcs = self.DEFAULT_FUNC_TEMPLATE.format(class_name = self.class_name) else: funcs = "" - return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'), - implements_spec = s_ifaces, + return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'), extends = "extends" if len(self.ifaces) else "", ifaces = j_ifaces, funcs = funcs, @@ -516,16 +404,16 @@ def create_all_test_files(): return mc, classes def main(argv): - smali_dir = Path(argv[1]) - if not smali_dir.exists() or not smali_dir.is_dir(): - print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr) + java_dir = Path(argv[1]) + if not java_dir.exists() or not java_dir.is_dir(): + print("{} is not a valid java dir".format(java_dir), file=sys.stderr) sys.exit(1) expected_txt = Path(argv[2]) mainclass, all_files = create_all_test_files() with expected_txt.open('w') as out: print(mainclass.get_expected(), file=out) for f in all_files: - f.dump(smali_dir) + f.dump(java_dir) if __name__ == '__main__': main(sys.argv) diff --git a/test/965-default-verify/build b/test/965-default-verify/build index 5ba54380dfaf111966c1b6b99c10a21b2cb002e8..0dd8573f542d0876859ffe9053e01df1bbd14055 100755 --- a/test/965-default-verify/build +++ b/test/965-default-verify/build @@ -17,32 +17,11 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling Java code, create it. - mkdir -p src - mkdir -p src2 - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Move build-src to src and the src copies to src2. This is needed because of - # how our default build script works and we wanted the java and smali code - # to be the same in the smali files. - for f in `find ./build-src -type f -name "*.java" | xargs -i basename \{\}`; do - mv ./src/$f ./src2/$f - mv ./build-src/$f ./src/$f - done - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/965-default-verify/smali/Iface.smali b/test/965-default-verify/smali/Iface.smali deleted file mode 100644 index 74799a6cf34ee6dde3c60bf0882052aeae040584..0000000000000000000000000000000000000000 --- a/test/965-default-verify/smali/Iface.smali +++ /dev/null @@ -1,40 +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. -# */ -# -# public interface Iface { -# public default String sayHi() { -# return "Hello"; -# } -# -# public default void verificationSoftFail() { -# Statics.nonexistantFunction(); -# } -# } - -.class public abstract interface LIface; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hello" - return-object v0 -.end method - -.method public verificationSoftFail()V - .locals 1 - invoke-static {}, LStatics;->nonexistantFunction()V - return-void -.end method diff --git a/test/965-default-verify/smali/Main.smali b/test/965-default-verify/smali/Main.smali deleted file mode 100644 index 8e9070692d9ee2fd92af63a4c3ef710646bb8d0c..0000000000000000000000000000000000000000 --- a/test/965-default-verify/smali/Main.smali +++ /dev/null @@ -1,179 +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. -# */ -# -# class Main implements Iface { -# public static void main(String[] args) { -# System.out.println("Create Main instance"); -# Main m = new Main(); -# System.out.println("Calling functions on concrete Main"); -# callMain(m); -# System.out.println("Calling functions on interface Iface"); -# callIface(m); -# } -# -# public static void callMain(Main m) { -# System.out.println("Calling verifiable function on Main"); -# System.out.println(m.sayHi()); -# System.out.println("Calling unverifiable function on Main"); -# try { -# m.verificationSoftFail(); -# System.out.println("Unexpected no error Thrown on Main"); -# } catch (NoSuchMethodError e) { -# System.out.println("Expected NSME Thrown on Main"); -# } catch (Throwable e) { -# System.out.println("Unexpected Error Thrown on Main"); -# e.printStackTrace(System.out); -# } -# System.out.println("Calling verifiable function on Main"); -# System.out.println(m.sayHi()); -# return; -# } -# -# public static void callIface(Iface m) { -# System.out.println("Calling verifiable function on Iface"); -# System.out.println(m.sayHi()); -# System.out.println("Calling unverifiable function on Iface"); -# try { -# m.verificationSoftFail(); -# System.out.println("Unexpected no error Thrown on Iface"); -# } catch (NoSuchMethodError e) { -# System.out.println("Expected NSME Thrown on Iface"); -# } catch (Throwable e) { -# System.out.println("Unexpected Error Thrown on Iface"); -# e.printStackTrace(System.out); -# } -# System.out.println("Calling verifiable function on Iface"); -# System.out.println(m.sayHi()); -# return; -# } -# } - -.class public LMain; -.super Ljava/lang/Object; -.implements LIface; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - const-string v0, "Create Main instance" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v2, LMain; - invoke-direct {v2}, LMain;->()V - - const-string v0, "Calling functions on concrete Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callMain(LMain;)V - - const-string v0, "Calling functions on interface Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface(LIface;)V - - return-void -.end method - -.method public static callIface(LIface;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling verifiable function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling unverifiable function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface;->verificationSoftFail()V - - const-string v0, "Unexpected no error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/NoSuchMethodError; {:try_start .. :try_end} :NSME_error_start - .catch Ljava/lang/Throwable; {:try_start .. :try_end} :other_error_start - :NSME_error_start - const-string v0, "Expected NSME Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :other_error_start - move-exception v2 - const-string v0, "Unexpected Error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-virtual {v2,v1}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V - goto :error_end - :error_end - const-string v0, "Calling verifiable function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method - -.method public static callMain(LMain;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling verifiable function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling unverifiable function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-virtual {p0}, LMain;->verificationSoftFail()V - - const-string v0, "Unexpected no error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/NoSuchMethodError; {:try_start .. :try_end} :NSME_error_start - .catch Ljava/lang/Throwable; {:try_start .. :try_end} :other_error_start - :NSME_error_start - const-string v0, "Expected NSME Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :other_error_start - move-exception v2 - const-string v0, "Unexpected Error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-virtual {v2,v1}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V - goto :error_end - :error_end - const-string v0, "Calling verifiable function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/965-default-verify/smali/Statics.smali b/test/965-default-verify/smali/Statics.smali deleted file mode 100644 index 1e8cac034ac9b92bccdfef7b37e24126590e1850..0000000000000000000000000000000000000000 --- a/test/965-default-verify/smali/Statics.smali +++ /dev/null @@ -1,30 +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. -# */ -# -# class Statics { -# // public static void nonexistantFunction() { -# // System.out.println("I don't exist"); -# // } -# } -# -.class public LStatics; -.super Ljava/lang/Object; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/965-default-verify/src/Iface.java b/test/965-default-verify/src/Iface.java new file mode 100644 index 0000000000000000000000000000000000000000..180fba2833d3f30afbf831d6a40d81c67a613f08 --- /dev/null +++ b/test/965-default-verify/src/Iface.java @@ -0,0 +1,23 @@ +/* + * 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 interface Iface { + public default String sayHi() { + return "Hello"; + } + public default void verificationSoftFail() { + Statics.nonexistantFunction(); + } +} diff --git a/test/965-default-verify/src/Main.java b/test/965-default-verify/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..6374cb5aa0b47ae77dcc7890b0967fc010fb58a9 --- /dev/null +++ b/test/965-default-verify/src/Main.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main implements Iface { + public static void main(String[] args) { + System.out.println("Create Main instance"); + Main m = new Main(); + System.out.println("Calling functions on concrete Main"); + callMain(m); + System.out.println("Calling functions on interface Iface"); + callIface(m); + } + + public static void callMain(Main m) { + System.out.println("Calling verifiable function on Main"); + System.out.println(m.sayHi()); + System.out.println("Calling unverifiable function on Main"); + try { + m.verificationSoftFail(); + System.out.println("Unexpected no error Thrown on Main"); + } catch (NoSuchMethodError e) { + System.out.println("Expected NSME Thrown on Main"); + } catch (Throwable e) { + System.out.println("Unexpected Error Thrown on Main"); + e.printStackTrace(System.out); + } + System.out.println("Calling verifiable function on Main"); + System.out.println(m.sayHi()); + return; + } + + public static void callIface(Iface m) { + System.out.println("Calling verifiable function on Iface"); + System.out.println(m.sayHi()); + System.out.println("Calling unverifiable function on Iface"); + try { + m.verificationSoftFail(); + System.out.println("Unexpected no error Thrown on Iface"); + } catch (NoSuchMethodError e) { + System.out.println("Expected NSME Thrown on Iface"); + } catch (Throwable e) { + System.out.println("Unexpected Error Thrown on Iface"); + e.printStackTrace(System.out); + } + System.out.println("Calling verifiable function on Iface"); + System.out.println(m.sayHi()); + return; + } +} diff --git a/test/965-default-verify/build-src/Statics.java b/test/965-default-verify/src/Statics.java similarity index 94% rename from test/965-default-verify/build-src/Statics.java rename to test/965-default-verify/src/Statics.java index 300aeecca70eb5b61e7f747426f43e27e97b847d..2e17ba4174cd68f739df984ed81df632232467c7 100644 --- a/test/965-default-verify/build-src/Statics.java +++ b/test/965-default-verify/src/Statics.java @@ -16,7 +16,7 @@ class Statics { public static void nonexistantFunction() { - System.out.println("I don't exist"); + System.out.println("I don't exist"); } } diff --git a/test/965-default-verify/src2/Statics.java b/test/965-default-verify/src2/Statics.java new file mode 100644 index 0000000000000000000000000000000000000000..7899ca9c5e3b2a38d5e837c6636b875bf9c73f20 --- /dev/null +++ b/test/965-default-verify/src2/Statics.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Statics { + // public static void nonexistantFunction() { + // System.out.println("I don't exist"); + // } +} diff --git a/test/966-default-conflict/build b/test/966-default-conflict/build index e66e8409c669ef6f69f3ca679779c492eb9ad9bb..0dd8573f542d0876859ffe9053e01df1bbd14055 100755 --- a/test/966-default-conflict/build +++ b/test/966-default-conflict/build @@ -17,18 +17,11 @@ # make us exit on a failure set -e -# TODO: Support running with jack. - -if [[ $@ == *"--jvm"* ]]; then - # Build the Java files if we are running a --jvm test - mkdir -p src - mkdir -p classes - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Build with the non-conflicting version - ${JAVAC} -implicit:none -d classes src/Iface.java build-src/Iface2.java src/Main.java - rm classes/Iface2.class - # Build with the conflicting version - ${JAVAC} -implicit:none -cp classes -d classes src/Iface2.java -else - ./default-build "$@" --experimental default-methods +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi + +./default-build "$@" --experimental default-methods diff --git a/test/966-default-conflict/smali/Iface.smali b/test/966-default-conflict/smali/Iface.smali deleted file mode 100644 index e996b3a4f4728bc758b4d7ff881f026873a37be5..0000000000000000000000000000000000000000 --- a/test/966-default-conflict/smali/Iface.smali +++ /dev/null @@ -1,39 +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. -# */ -# -# public interface Iface { -# public default String sayHi() { -# return "Hi"; -# } -# public default String charge() { -# return "CHARGE"; -# } -# } - -.class public abstract interface LIface; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hi" - return-object v0 -.end method - -.method public charge()Ljava/lang/String; - .locals 1 - const-string v0, "CHARGE" - return-object v0 -.end method diff --git a/test/966-default-conflict/smali/Iface2.smali b/test/966-default-conflict/smali/Iface2.smali deleted file mode 100644 index 82fa547deaad8609c73126f18b31a7985bdfaa65..0000000000000000000000000000000000000000 --- a/test/966-default-conflict/smali/Iface2.smali +++ /dev/null @@ -1,31 +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. -# */ -# -# public interface Iface2 { -# public default String sayHi() { -# return "hello"; -# } -# } - -.class public abstract interface LIface2; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "hello" - return-object v0 -.end method - diff --git a/test/966-default-conflict/smali/Main.smali b/test/966-default-conflict/smali/Main.smali deleted file mode 100644 index ce974d813566433f5d2f93ca8774f35c6eea2470..0000000000000000000000000000000000000000 --- a/test/966-default-conflict/smali/Main.smali +++ /dev/null @@ -1,227 +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. -# */ -# -# class Main implements Iface, Iface2 { -# public static void main(String[] args) { -# System.out.println("Create Main instance"); -# Main m = new Main(); -# System.out.println("Calling functions on concrete Main"); -# callMain(m); -# System.out.println("Calling functions on interface Iface"); -# callIface(m); -# System.out.println("Calling functions on interface Iface2"); -# callIface2(m); -# } -# -# public static void callMain(Main m) { -# System.out.println("Calling non-conflicting function on Main"); -# System.out.println(m.charge()); -# System.out.println("Calling conflicting function on Main"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Main"); -# } catch (AbstractMethodError e) { -# System.out.println("Unexpected AME Thrown on Main"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Expected ICCE Thrown on Main"); -# } -# System.out.println("Calling non-conflicting function on Main"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface(Iface m) { -# System.out.println("Calling non-conflicting function on Iface"); -# System.out.println(m.charge()); -# System.out.println("Calling conflicting function on Iface"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface"); -# } catch (AbstractMethodError e) { -# System.out.println("Unexpected AME Thrown on Iface"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Expected ICCE Thrown on Iface"); -# } -# System.out.println("Calling non-conflicting function on Iface"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface2(Iface2 m) { -# System.out.println("Calling conflicting function on Iface2"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface2"); -# } catch (AbstractMethodError e) { -# System.out.println("Unexpected AME Thrown on Iface2"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Expected ICCE Thrown on Iface2"); -# } -# return; -# } -# } - -.class public LMain; -.super Ljava/lang/Object; -.implements LIface; -.implements LIface2; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - const-string v0, "Create Main instance" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v2, LMain; - invoke-direct {v2}, LMain;->()V - - const-string v0, "Calling functions on concrete Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callMain(LMain;)V - - const-string v0, "Calling functions on interface Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface(LIface;)V - - const-string v0, "Calling functions on interface Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface2(LIface2;)V - - return-void -.end method - -.method public static callIface(LIface;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-conflicting function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling conflicting function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Unexpected AME Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Expected ICCE Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-conflicting function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method - -.method public static callIface2(LIface2;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling conflicting function on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface2;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Unexpected AME Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Expected ICCE Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - - return-void -.end method - -.method public static callMain(LMain;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-conflicting function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling conflicting function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Unexpected AME Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Expected ICCE Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-conflicting function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/966-default-conflict/src/Iface.java b/test/966-default-conflict/src/Iface.java new file mode 100644 index 0000000000000000000000000000000000000000..2131ed878d80a03b3481d6a3bba3868fd805057f --- /dev/null +++ b/test/966-default-conflict/src/Iface.java @@ -0,0 +1,23 @@ +/* + * 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 interface Iface { + public default String sayHi() { + return "Hi"; + } + public default String charge() { + return "CHARGE"; + } +} diff --git a/test/966-default-conflict/build-src/Iface2.java b/test/966-default-conflict/src/Iface2.java similarity index 100% rename from test/966-default-conflict/build-src/Iface2.java rename to test/966-default-conflict/src/Iface2.java diff --git a/test/966-default-conflict/src/Main.java b/test/966-default-conflict/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..ce8cb4720920bec7ac3bc45e026e8e82db26aa62 --- /dev/null +++ b/test/966-default-conflict/src/Main.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main implements Iface, Iface2 { + public static void main(String[] args) { + System.out.println("Create Main instance"); + Main m = new Main(); + System.out.println("Calling functions on concrete Main"); + callMain(m); + System.out.println("Calling functions on interface Iface"); + callIface(m); + System.out.println("Calling functions on interface Iface2"); + callIface2(m); + } + public static void callMain(Main m) { + System.out.println("Calling non-conflicting function on Main"); + System.out.println(m.charge()); + System.out.println("Calling conflicting function on Main"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Main"); + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME Thrown on Main"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE Thrown on Main"); + } + System.out.println("Calling non-conflicting function on Main"); + System.out.println(m.charge()); + return; + } + public static void callIface(Iface m) { + System.out.println("Calling non-conflicting function on Iface"); + System.out.println(m.charge()); + System.out.println("Calling conflicting function on Iface"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface"); + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME Thrown on Iface"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE Thrown on Iface"); + } + System.out.println("Calling non-conflicting function on Iface"); + System.out.println(m.charge()); + return; + } + public static void callIface2(Iface2 m) { + System.out.println("Calling conflicting function on Iface2"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface2"); + } catch (AbstractMethodError e) { + System.out.println("Unexpected AME Thrown on Iface2"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Expected ICCE Thrown on Iface2"); + } + return; + } +} diff --git a/test/966-default-conflict/src2/Iface2.java b/test/966-default-conflict/src2/Iface2.java new file mode 100644 index 0000000000000000000000000000000000000000..d29033cd934d3f2a9761f3bf44788b879c882daa --- /dev/null +++ b/test/966-default-conflict/src2/Iface2.java @@ -0,0 +1,20 @@ +/* + * 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 interface Iface2 { + public default String sayHi() { + return "hello"; + } +} diff --git a/test/967-default-ame/build b/test/967-default-ame/build index 53001a9ad23e7ba6616c0e9f4ea2995e5d85f0d8..0dd8573f542d0876859ffe9053e01df1bbd14055 100755 --- a/test/967-default-ame/build +++ b/test/967-default-ame/build @@ -17,19 +17,11 @@ # make us exit on a failure set -e -# TODO: Support running with jack. - -if [[ $@ == *"--jvm"* ]]; then - # Build the Java files if we are running a --jvm test - mkdir -p src - mkdir -p classes - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Build with the non-conflicting version - ${JAVAC} -implicit:none -d classes src/Iface.java build-src/Iface2.java build-src/Iface3.java src/Main.java - rm classes/Iface2.class - rm classes/Iface3.class - # Build with the conflicting version - ${JAVAC} -implicit:none -cp classes -d classes src/Iface2.java src/Iface3.java -else - ./default-build "$@" --experimental default-methods +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi + +./default-build "$@" --experimental default-methods diff --git a/test/967-default-ame/smali/Iface.smali b/test/967-default-ame/smali/Iface.smali deleted file mode 100644 index e996b3a4f4728bc758b4d7ff881f026873a37be5..0000000000000000000000000000000000000000 --- a/test/967-default-ame/smali/Iface.smali +++ /dev/null @@ -1,39 +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. -# */ -# -# public interface Iface { -# public default String sayHi() { -# return "Hi"; -# } -# public default String charge() { -# return "CHARGE"; -# } -# } - -.class public abstract interface LIface; -.super Ljava/lang/Object; - -.method public sayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hi" - return-object v0 -.end method - -.method public charge()Ljava/lang/String; - .locals 1 - const-string v0, "CHARGE" - return-object v0 -.end method diff --git a/test/967-default-ame/smali/Iface2.smali b/test/967-default-ame/smali/Iface2.smali deleted file mode 100644 index a21a8ddbc7dcef40b0672597f37b368cf1f46785..0000000000000000000000000000000000000000 --- a/test/967-default-ame/smali/Iface2.smali +++ /dev/null @@ -1,27 +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. -# */ -# -# public interface Iface2 extends Iface { -# public String sayHi(); -# } - -.class public abstract interface LIface2; -.super Ljava/lang/Object; -.implements LIface; - -.method public abstract sayHi()Ljava/lang/String; -.end method - diff --git a/test/967-default-ame/smali/Iface3.smali b/test/967-default-ame/smali/Iface3.smali deleted file mode 100644 index 874e96d069da24781af36d5ce0952a67d640534b..0000000000000000000000000000000000000000 --- a/test/967-default-ame/smali/Iface3.smali +++ /dev/null @@ -1,26 +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. -# */ -# -# public interface Iface3 { -# public String charge(); -# } - -.class public abstract interface LIface3; -.super Ljava/lang/Object; - -.method public abstract charge()Ljava/lang/String; -.end method - diff --git a/test/967-default-ame/smali/Main.smali b/test/967-default-ame/smali/Main.smali deleted file mode 100644 index e4d63cfa24778456bb7ceffb70da96ad3d71ad33..0000000000000000000000000000000000000000 --- a/test/967-default-ame/smali/Main.smali +++ /dev/null @@ -1,228 +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. -# */ -# -# class Main implements Iface, Iface2, Iface3 { -# public static void main(String[] args) { -# System.out.println("Create Main instance"); -# Main m = new Main(); -# System.out.println("Calling functions on concrete Main"); -# callMain(m); -# System.out.println("Calling functions on interface Iface"); -# callIface(m); -# System.out.println("Calling functions on interface Iface2"); -# callIface2(m); -# } -# -# public static void callMain(Main m) { -# System.out.println("Calling non-abstract function on Main"); -# System.out.println(m.charge()); -# System.out.println("Calling abstract function on Main"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Main"); -# } catch (AbstractMethodError e) { -# System.out.println("Expected AME Thrown on Main"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Unexpected ICCE Thrown on Main"); -# } -# System.out.println("Calling non-abstract function on Main"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface(Iface m) { -# System.out.println("Calling non-abstract function on Iface"); -# System.out.println(m.charge()); -# System.out.println("Calling abstract function on Iface"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface"); -# } catch (AbstractMethodError e) { -# System.out.println("Expected AME Thrown on Iface"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Unexpected ICCE Thrown on Iface"); -# } -# System.out.println("Calling non-abstract function on Iface"); -# System.out.println(m.charge()); -# return; -# } -# -# public static void callIface2(Iface2 m) { -# System.out.println("Calling abstract function on Iface2"); -# try { -# System.out.println(m.sayHi()); -# System.out.println("Unexpected no error Thrown on Iface2"); -# } catch (AbstractMethodError e) { -# System.out.println("Expected AME Thrown on Iface2"); -# } catch (IncompatibleClassChangeError e) { -# System.out.println("Unexpected ICCE Thrown on Iface2"); -# } -# return; -# } -# } - -.class public LMain; -.super Ljava/lang/Object; -.implements LIface; -.implements LIface2; -.implements LIface3; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public static main([Ljava/lang/String;)V - .locals 3 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - - const-string v0, "Create Main instance" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - new-instance v2, LMain; - invoke-direct {v2}, LMain;->()V - - const-string v0, "Calling functions on concrete Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callMain(LMain;)V - - const-string v0, "Calling functions on interface Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface(LIface;)V - - const-string v0, "Calling functions on interface Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - invoke-static {v2}, LMain;->callIface2(LIface2;)V - - return-void -.end method - -.method public static callIface(LIface;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-abstract function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling abstract function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Expected AME Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Unexpected ICCE Thrown on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-abstract function on Iface" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-interface {p0}, LIface;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method - -.method public static callIface2(LIface2;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling abstract function on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-interface {p0}, LIface2;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Expected AME Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Unexpected ICCE Thrown on Iface2" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - - return-void -.end method - -.method public static callMain(LMain;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Calling non-abstract function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Calling abstract function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - :try_start - invoke-virtual {p0}, LMain;->sayHi()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "Unexpected no error Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - goto :error_end - :try_end - .catch Ljava/lang/AbstractMethodError; {:try_start .. :try_end} :AME_error_start - .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :ICCE_error_start - :AME_error_start - const-string v0, "Expected AME Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :ICCE_error_start - const-string v0, "Unexpected ICCE Thrown on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - goto :error_end - :error_end - const-string v0, "Calling non-abstract function on Main" - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - invoke-virtual {p0}, LMain;->charge()Ljava/lang/String; - move-result-object v0 - invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - return-void -.end method diff --git a/test/967-default-ame/src/Iface.java b/test/967-default-ame/src/Iface.java new file mode 100644 index 0000000000000000000000000000000000000000..2131ed878d80a03b3481d6a3bba3868fd805057f --- /dev/null +++ b/test/967-default-ame/src/Iface.java @@ -0,0 +1,23 @@ +/* + * 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 interface Iface { + public default String sayHi() { + return "Hi"; + } + public default String charge() { + return "CHARGE"; + } +} diff --git a/test/967-default-ame/build-src/Iface2.java b/test/967-default-ame/src/Iface2.java similarity index 100% rename from test/967-default-ame/build-src/Iface2.java rename to test/967-default-ame/src/Iface2.java diff --git a/test/967-default-ame/build-src/Iface3.java b/test/967-default-ame/src/Iface3.java similarity index 100% rename from test/967-default-ame/build-src/Iface3.java rename to test/967-default-ame/src/Iface3.java diff --git a/test/967-default-ame/src/Main.java b/test/967-default-ame/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..3e48062abaf9b4ab28bcccecebeeacdf3afce182 --- /dev/null +++ b/test/967-default-ame/src/Main.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Main implements Iface, Iface2, Iface3 { + public static void main(String[] args) { + System.out.println("Create Main instance"); + Main m = new Main(); + System.out.println("Calling functions on concrete Main"); + callMain(m); + System.out.println("Calling functions on interface Iface"); + callIface(m); + System.out.println("Calling functions on interface Iface2"); + callIface2(m); + } + public static void callMain(Main m) { + System.out.println("Calling non-abstract function on Main"); + System.out.println(m.charge()); + System.out.println("Calling abstract function on Main"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Main"); + } catch (AbstractMethodError e) { + System.out.println("Expected AME Thrown on Main"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Unexpected ICCE Thrown on Main"); + } + System.out.println("Calling non-abstract function on Main"); + System.out.println(m.charge()); + return; + } + public static void callIface(Iface m) { + System.out.println("Calling non-abstract function on Iface"); + System.out.println(m.charge()); + System.out.println("Calling abstract function on Iface"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface"); + } catch (AbstractMethodError e) { + System.out.println("Expected AME Thrown on Iface"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Unexpected ICCE Thrown on Iface"); + } + System.out.println("Calling non-abstract function on Iface"); + System.out.println(m.charge()); + return; + } + public static void callIface2(Iface2 m) { + System.out.println("Calling abstract function on Iface2"); + try { + System.out.println(m.sayHi()); + System.out.println("Unexpected no error Thrown on Iface2"); + } catch (AbstractMethodError e) { + System.out.println("Expected AME Thrown on Iface2"); + } catch (IncompatibleClassChangeError e) { + System.out.println("Unexpected ICCE Thrown on Iface2"); + } + return; + } +} diff --git a/test/967-default-ame/src2/Iface.java b/test/967-default-ame/src2/Iface.java new file mode 100644 index 0000000000000000000000000000000000000000..2131ed878d80a03b3481d6a3bba3868fd805057f --- /dev/null +++ b/test/967-default-ame/src2/Iface.java @@ -0,0 +1,23 @@ +/* + * 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 interface Iface { + public default String sayHi() { + return "Hi"; + } + public default String charge() { + return "CHARGE"; + } +} diff --git a/test/967-default-ame/src2/Iface2.java b/test/967-default-ame/src2/Iface2.java new file mode 100644 index 0000000000000000000000000000000000000000..0e4fb5f2aa7734c1081c2f7ff700e9816a79480d --- /dev/null +++ b/test/967-default-ame/src2/Iface2.java @@ -0,0 +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. + */ +public interface Iface2 extends Iface { + public String sayHi(); +} diff --git a/test/967-default-ame/src2/Iface3.java b/test/967-default-ame/src2/Iface3.java new file mode 100644 index 0000000000000000000000000000000000000000..70fc33ba93dab6046422237c08abc083dca9e8ad --- /dev/null +++ b/test/967-default-ame/src2/Iface3.java @@ -0,0 +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. + */ +public interface Iface3 { + public String charge(); +} diff --git a/test/969-iface-super/build b/test/969-iface-super/build index b1ef320d05f9ed8e6a3645c137c67c8dad6e24d2..e8f4ed084a4ff4d0d9dba2300558fa7f5f817ac3 100755 --- a/test/969-iface-super/build +++ b/test/969-iface-super/build @@ -17,27 +17,14 @@ # make us exit on a failure set -e -# Should we compile with Java source code. By default we will use Smali. -USES_JAVA_SOURCE="false" -if [[ $@ == *"--jvm"* ]]; then - USES_JAVA_SOURCE="true" -elif [[ "$USE_JACK" == "true" ]]; then - if $JACK -D jack.java.source.version=1.8 2>/dev/null; then - USES_JAVA_SOURCE="true" - else - echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2 - fi +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm + # Hard-wired use of experimental jack. + # TODO: fix this temporary work-around for default-methods, see b/19467889 + export USE_JACK=true fi -# Generate the smali Main.smali file or fail -${ANDROID_BUILD_TOP}/art/test/utils/python/generate_smali_main.py ./smali +# Generate the Main.java file or fail +${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src -if [[ "$USES_JAVA_SOURCE" == "true" ]]; then - # We are compiling java code, create it. - mkdir -p src - ${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src - # Ignore the smali directory. - EXTRA_ARGS="--no-smali" -fi - -./default-build "$@" "$EXTRA_ARGS" --experimental default-methods +./default-build "$@" --experimental default-methods diff --git a/test/969-iface-super/expected.txt b/test/969-iface-super/expected.txt index f310d10e1215bac8a1f8a89e4e7d950a34ddf98d..f7a63d6b08b8fd4e070aeb04269b1eebb48ab1e5 100644 --- a/test/969-iface-super/expected.txt +++ b/test/969-iface-super/expected.txt @@ -1,39 +1,39 @@ Testing for type A A-virtual A.SayHi()='Hello ' -A-interface iface.SayHi()='Hello ' +A-interface Iface.SayHi()='Hello ' End testing for type A Testing for type B B-virtual B.SayHi()='Hello Hello ' -B-interface iface.SayHi()='Hello Hello ' -B-interface iface2.SayHi()='Hello Hello ' +B-interface Iface.SayHi()='Hello Hello ' +B-interface Iface2.SayHi()='Hello Hello ' End testing for type B Testing for type C C-virtual C.SayHi()='Hello and welcome ' -C-interface iface.SayHi()='Hello and welcome ' +C-interface Iface.SayHi()='Hello and welcome ' End testing for type C Testing for type D D-virtual D.SayHi()='Hello Hello and welcome ' -D-interface iface.SayHi()='Hello Hello and welcome ' -D-interface iface2.SayHi()='Hello Hello and welcome ' +D-interface Iface.SayHi()='Hello Hello and welcome ' +D-interface Iface2.SayHi()='Hello Hello and welcome ' End testing for type D Testing for type E E-virtual E.SayHi()='Hello there!' -E-interface iface.SayHi()='Hello there!' -E-interface iface3.SayHi()='Hello there!' +E-interface Iface.SayHi()='Hello there!' +E-interface Iface3.SayHi()='Hello there!' End testing for type E Testing for type F F-virtual E.SayHi()='Hello there!' F-virtual F.SayHi()='Hello there!' -F-interface iface.SayHi()='Hello there!' -F-interface iface3.SayHi()='Hello there!' +F-interface Iface.SayHi()='Hello there!' +F-interface Iface3.SayHi()='Hello there!' F-virtual F.SaySurprisedHi()='Hello there!!' End testing for type F Testing for type G G-virtual E.SayHi()='Hello there!?' G-virtual F.SayHi()='Hello there!?' G-virtual G.SayHi()='Hello there!?' -G-interface iface.SayHi()='Hello there!?' -G-interface iface3.SayHi()='Hello there!?' +G-interface Iface.SayHi()='Hello there!?' +G-interface Iface3.SayHi()='Hello there!?' G-virtual F.SaySurprisedHi()='Hello there!!' G-virtual G.SaySurprisedHi()='Hello there!!' G-virtual G.SayVerySurprisedHi()='Hello there!!!' @@ -42,6 +42,6 @@ Testing for type H H-virtual H.SayConfusedHi()='Hello ?!' H-virtual A.SayHi()='Hello ?' H-virtual H.SayHi()='Hello ?' -H-interface iface.SayHi()='Hello ?' +H-interface Iface.SayHi()='Hello ?' H-virtual H.SaySurprisedHi()='Hello !' End testing for type H diff --git a/test/969-iface-super/smali/A.smali b/test/969-iface-super/smali/A.smali deleted file mode 100644 index e7760a106287302a7952ef04d9ea321211bcb493..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/A.smali +++ /dev/null @@ -1,28 +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. -# */ -# -# public class A implements iface { -# } - -.class public LA; -.super Ljava/lang/Object; -.implements Liface; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/969-iface-super/smali/B.smali b/test/969-iface-super/smali/B.smali deleted file mode 100644 index e529d0534fda71b955ceb8de5b89c4abca579a13..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/B.smali +++ /dev/null @@ -1,28 +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. -# */ -# -# public class B implements iface2 { -# } - -.class public LB; -.super Ljava/lang/Object; -.implements Liface2; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method diff --git a/test/969-iface-super/smali/C.smali b/test/969-iface-super/smali/C.smali deleted file mode 100644 index 6fbb0c4b0ef0fb91888df6c1b7870e1ed22fac9a..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/C.smali +++ /dev/null @@ -1,41 +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. -# */ -# -# public class C implements iface { -# public String SayHi() { -# return iface.super.SayHi() + " and welcome "; -# } -# } - -.class public LC; -.super Ljava/lang/Object; -.implements Liface; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, " and welcome " - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/D.smali b/test/969-iface-super/smali/D.smali deleted file mode 100644 index ecd46295844fa404dc5e56fe884ef6fb9713f83d..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/D.smali +++ /dev/null @@ -1,41 +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. -# */ -# -# public class D implements iface2 { -# public String SayHi() { -# return iface2.super.SayHi() + " and welcome "; -# } -# } - -.class public LD; -.super Ljava/lang/Object; -.implements Liface2; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface2;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, " and welcome " - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/E.smali b/test/969-iface-super/smali/E.smali deleted file mode 100644 index 558aaea5d6213c7bcef2a0d8c700a8a02c9566d9..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/E.smali +++ /dev/null @@ -1,41 +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. -# */ -# -# public class E implements iface3 { -# public String SayHi() { -# return iface3.super.SayHi() + " there!"; -# } -# } - -.class public LE; -.super Ljava/lang/Object; -.implements Liface3; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, Ljava/lang/Object;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface3;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, " there!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/F.smali b/test/969-iface-super/smali/F.smali deleted file mode 100644 index c402d5cdc9f522705197b38e55018fee7d450004..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/F.smali +++ /dev/null @@ -1,40 +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. -# */ -# -# public class F extends E { -# public String SaySurprisedHi() { -# return super.SayHi() + "!"; -# } -# } - -.class public LF; -.super LE; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LE;->()V - return-void -.end method - -.method public SaySurprisedHi()Ljava/lang/String; - .registers 2 - invoke-super {p0}, LE;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/G.smali b/test/969-iface-super/smali/G.smali deleted file mode 100644 index 45705e6d863977d1d827f2e0bce8262d68c677c8..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/G.smali +++ /dev/null @@ -1,53 +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. -# */ -# -# public class G extends F { -# public String SayHi() { -# return super.SayHi() + "?"; -# } -# public String SayVerySurprisedHi() { -# return super.SaySurprisedHi() + "!"; -# } -# } - -.class public LG; -.super LF; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LF;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LF;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "?" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SayVerySurprisedHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LF;->SaySurprisedHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/H.smali b/test/969-iface-super/smali/H.smali deleted file mode 100644 index 12f246b8c35f22a9984bd567e343faa61181852e..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/H.smali +++ /dev/null @@ -1,66 +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. -# */ -# -# public class H extends A { -# public String SayHi() { -# return super.SayHi() + "?"; -# } -# public String SaySurprisedHi() { -# return super.SayHi() + "!"; -# } -# public String SayConfusedHi() { -# return SayHi() + "!"; -# } -# } - -.class public LH; -.super LA; - -.method public constructor ()V - .registers 1 - invoke-direct {p0}, LA;->()V - return-void -.end method - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LA;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "?" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SaySurprisedHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, LA;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method - -.method public SayConfusedHi()Ljava/lang/String; - .locals 2 - invoke-virtual {p0}, LH;->SayHi()Ljava/lang/String; - move-result-object v0 - const-string v1, "!" - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/iface.smali b/test/969-iface-super/smali/iface.smali deleted file mode 100644 index 08bb93dd0cf118917d51741b54e672211c9e84a6..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/iface.smali +++ /dev/null @@ -1,30 +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. -# */ -# -# public interface iface { -# public default String SayHi() { -# return "Hello "; -# } -# } - -.class public abstract interface Liface; -.super Ljava/lang/Object; - -.method public SayHi()Ljava/lang/String; - .locals 1 - const-string v0, "Hello " - return-object v0 -.end method diff --git a/test/969-iface-super/smali/iface2.smali b/test/969-iface-super/smali/iface2.smali deleted file mode 100644 index ce6f86432dd27172daa16470a63a59f9a3d54a34..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/iface2.smali +++ /dev/null @@ -1,36 +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. -# */ -# -# public interface iface2 extends iface { -# public default String SayHi() { -# return iface.super.SayHi() + iface.super.SayHi(); -# } -# } - -.class public abstract interface Liface2; -.super Ljava/lang/Object; -.implements Liface; - -.method public SayHi()Ljava/lang/String; - .locals 2 - invoke-super {p0}, Liface;->SayHi()Ljava/lang/String; - move-result-object v0 - invoke-super {p0}, Liface;->SayHi()Ljava/lang/String; - move-result-object v1 - invoke-virtual {v0, v1}, Ljava/lang/String;->concat(Ljava/lang/String;)Ljava/lang/String; - move-result-object v0 - return-object v0 -.end method diff --git a/test/969-iface-super/smali/iface3.smali b/test/969-iface-super/smali/iface3.smali deleted file mode 100644 index bf200364eca9d88e104413e15ecad3ecf1481a68..0000000000000000000000000000000000000000 --- a/test/969-iface-super/smali/iface3.smali +++ /dev/null @@ -1,22 +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. -# */ -# -# public interface iface3 extends iface { -# } - -.class public abstract interface Liface3; -.super Ljava/lang/Object; -.implements Liface; diff --git a/test/969-iface-super/src/A.java b/test/969-iface-super/src/A.java new file mode 100644 index 0000000000000000000000000000000000000000..47db14ba8440e70351d5285e105a3aad06b8966e --- /dev/null +++ b/test/969-iface-super/src/A.java @@ -0,0 +1,16 @@ +/* + * 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 A implements Iface { } diff --git a/test/969-iface-super/src/B.java b/test/969-iface-super/src/B.java new file mode 100644 index 0000000000000000000000000000000000000000..70f63a237a3093f43c3058bb64ba301363210d2b --- /dev/null +++ b/test/969-iface-super/src/B.java @@ -0,0 +1,16 @@ +/* + * 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 B implements Iface2 { } diff --git a/test/969-iface-super/src/C.java b/test/969-iface-super/src/C.java new file mode 100644 index 0000000000000000000000000000000000000000..0fa0b9280bc3a0f0b4c015c459b70f65c6f5c54d --- /dev/null +++ b/test/969-iface-super/src/C.java @@ -0,0 +1,20 @@ +/* + * 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 C implements Iface { + public String SayHi() { + return Iface.super.SayHi() + " and welcome "; + } +} diff --git a/test/969-iface-super/src/D.java b/test/969-iface-super/src/D.java new file mode 100644 index 0000000000000000000000000000000000000000..8a607c3adf79f309b1d644daf9043f2fb5dec9f0 --- /dev/null +++ b/test/969-iface-super/src/D.java @@ -0,0 +1,20 @@ +/* + * 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 D implements Iface2 { + public String SayHi() { + return Iface2.super.SayHi() + " and welcome "; + } +} diff --git a/test/969-iface-super/src/E.java b/test/969-iface-super/src/E.java new file mode 100644 index 0000000000000000000000000000000000000000..d5942b22b65b15738746b8290135ff77a9d12cc0 --- /dev/null +++ b/test/969-iface-super/src/E.java @@ -0,0 +1,20 @@ +/* + * 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 E implements Iface3 { + public String SayHi() { + return Iface3.super.SayHi() + " there!"; + } +} diff --git a/test/969-iface-super/src/F.java b/test/969-iface-super/src/F.java new file mode 100644 index 0000000000000000000000000000000000000000..610bcb158a2544c87fff739441687120aec148c2 --- /dev/null +++ b/test/969-iface-super/src/F.java @@ -0,0 +1,20 @@ +/* + * 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 F extends E { + public String SaySurprisedHi() { + return super.SayHi() + "!"; + } +} diff --git a/test/969-iface-super/src/G.java b/test/969-iface-super/src/G.java new file mode 100644 index 0000000000000000000000000000000000000000..edaf3a9b11ec1badd96b8e059e6602cd4a97776c --- /dev/null +++ b/test/969-iface-super/src/G.java @@ -0,0 +1,23 @@ +/* + * 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 G extends F { + public String SayHi() { + return super.SayHi() + "?"; + } + public String SayVerySurprisedHi() { + return super.SaySurprisedHi() + "!"; + } +} diff --git a/test/969-iface-super/src/H.java b/test/969-iface-super/src/H.java new file mode 100644 index 0000000000000000000000000000000000000000..744bda6f82d179e0463a8bfeb631c2118050f4e3 --- /dev/null +++ b/test/969-iface-super/src/H.java @@ -0,0 +1,26 @@ +/* + * 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 H extends A { + public String SayHi() { + return super.SayHi() + "?"; + } + public String SaySurprisedHi() { + return super.SayHi() + "!"; + } + public String SayConfusedHi() { + return SayHi() + "!"; + } +} diff --git a/test/969-iface-super/src/Iface.java b/test/969-iface-super/src/Iface.java new file mode 100644 index 0000000000000000000000000000000000000000..ece5e592dee37c948449ca76574e76dfb6baacd9 --- /dev/null +++ b/test/969-iface-super/src/Iface.java @@ -0,0 +1,20 @@ +/* + * 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 interface Iface { + public default String SayHi() { + return "Hello "; + } +} diff --git a/test/969-iface-super/src/Iface2.java b/test/969-iface-super/src/Iface2.java new file mode 100644 index 0000000000000000000000000000000000000000..d74ee6ddf5354081cf5dfc1d6d02b9cc3ca72005 --- /dev/null +++ b/test/969-iface-super/src/Iface2.java @@ -0,0 +1,20 @@ +/* + * 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 interface Iface2 extends Iface { + public default String SayHi() { + return Iface.super.SayHi() + Iface.super.SayHi(); + } +} diff --git a/test/969-iface-super/src/Iface3.java b/test/969-iface-super/src/Iface3.java new file mode 100644 index 0000000000000000000000000000000000000000..10b010cb3bbfdfcd81196cbc60c93455ea7ffdbe --- /dev/null +++ b/test/969-iface-super/src/Iface3.java @@ -0,0 +1,16 @@ +/* + * 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 interface Iface3 extends Iface { } diff --git a/test/969-iface-super/smali/classes.xml b/test/969-iface-super/src/classes.xml similarity index 85% rename from test/969-iface-super/smali/classes.xml rename to test/969-iface-super/src/classes.xml index 4d205bd606b4ef63f5cc5f34b6822dc47c75c3fa..4c3dae4fa039428592986c2612c4d2e699158c06 100644 --- a/test/969-iface-super/smali/classes.xml +++ b/test/969-iface-super/src/classes.xml @@ -18,35 +18,35 @@ - iface + Iface - iface2 + Iface2 - iface + Iface - iface2 + Iface2 - iface3 + Iface3 @@ -75,23 +75,23 @@ - + SayHi - + - iface + Iface - + - iface + Iface diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 167ad859d234864d43abea502f3e93f17f71757a..7036bdcaf5f5bc0fd3cfaba82acb332c32a893a8 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -222,8 +222,14 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), # Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065) +# Disable 137-cfi (b/27391690). +# Disable 536-checker-needs-access-check and 537-checker-inline-and-unverified (b/27425061) +# Disable 577-profile-foreign-dex (b/27454772). TEST_ART_BROKEN_ALL_TARGET_TESTS := \ - 097-duplicate-method + 097-duplicate-method \ + 536-checker-needs-access-check \ + 537-checker-inline-and-unverified \ + 577-profile-foreign-dex \ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ @@ -535,7 +541,9 @@ endif TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := # Tests that should fail in the read barrier configuration with the interpreter. -TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := +# 145: Test sometimes times out in read barrier configuration (b/27467554). +TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := \ + 145-alloc-tracking-stress # Tests that should fail in the read barrier configuration with the default (Quick) compiler (AOT). # Quick has no support for read barriers and punts to the interpreter, so this list is composed of @@ -545,6 +553,7 @@ TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := \ $(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS) # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). +# 145: Test sometimes times out in read barrier configuration (b/27467554). # 484: Baker's fast path based read barrier compiler instrumentation generates code containing # more parallel moves on x86, thus some Checker assertions may fail. # 527: On ARM64, the read barrier instrumentation does not support the HArm64IntermediateAddress @@ -552,12 +561,15 @@ TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := \ # 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are # not yet handled in the read barrier configuration. TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \ + 145-alloc-tracking-stress \ 484-checker-register-hints \ 527-checker-array-access-split \ 537-checker-arraycopy # Tests that should fail in the read barrier configuration with JIT (Optimizing compiler). -TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := +# 145: Test sometimes times out in read barrier configuration (b/27467554). +TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \ + 145-alloc-tracking-stress ifeq ($(ART_USE_READ_BARRIER),true) ifneq (,$(filter interpreter,$(COMPILER_TYPES))) diff --git a/test/run-test b/test/run-test index f1875d71a57a0f4db873234c53d583b2d52f9e9a..d0f93b9231758215a1e938fd1efc0f06cc9d6d4b 100755 --- a/test/run-test +++ b/test/run-test @@ -677,11 +677,7 @@ function arch_supports_read_barrier() { # Tests named '-checker-*' will also have their CFGs verified with # Checker when compiled with Optimizing on host. if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then - # Jack does not necessarily generate the same DEX output than dx. Because these tests depend - # on a particular DEX output, keep building them with dx for now (b/19467889). - USE_JACK="false" - - if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" ]; then + if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$USE_JACK" = "true" ]; then # Optimizing has read barrier support for certain architectures # only. On other architectures, compiling is disabled when read # barriers are enabled, meaning that we do not produce a CFG file diff --git a/test/utils/python/generate_smali_main.py b/test/utils/python/generate_java_main.py similarity index 67% rename from test/utils/python/generate_smali_main.py rename to test/utils/python/generate_java_main.py index d796d313c6b896f1e00fbb8cf02e275b1ebd35ab..f66d0dd37217dc48cd127e84a14aa3a19ea3166a 100755 --- a/test/utils/python/generate_smali_main.py +++ b/test/utils/python/generate_java_main.py @@ -15,7 +15,7 @@ # limitations under the License. """ -Generate Smali Main file from a classes.xml file. +Generate Java Main file from a classes.xml file. """ import os @@ -38,48 +38,27 @@ import itertools import functools import xml.etree.ElementTree as ET -class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): +class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin): """ A mainclass and main method for this test. """ MAIN_CLASS_TEMPLATE = """{copyright} -.class public LMain; -.super Ljava/lang/Object; - -# class Main {{ - -.method public constructor ()V - .registers 1 - invoke-direct {{p0}}, Ljava/lang/Object;->()V - return-void -.end method - +class Main {{ {test_groups} - {test_funcs} - {main_func} - -# }} +}} """ MAIN_FUNCTION_TEMPLATE = """ -# public static void main(String[] args) {{ -.method public static main([Ljava/lang/String;)V - .locals 2 - sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; - + public static void main(String[] args) {{ {test_group_invoke} - - return-void -.end method -# }} + }} """ TEST_GROUP_INVOKE_TEMPLATE = """ -# {test_name}(); - invoke-static {{}}, {test_name}()V + {test_name}(); """ def __init__(self): @@ -123,7 +102,7 @@ class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin): funcs = "" for f in self.global_funcs: funcs += str(f) - return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), + return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright('java'), test_groups=test_groups, main_func=main_func, test_funcs=funcs) @@ -135,33 +114,19 @@ class InstanceTest(mixins.Named, mixins.NameComparableMixin): """ INSTANCE_TEST_TEMPLATE = """ -# public static void {test_name}() {{ -# System.out.println("Testing for type {ty}"); -# String s = "{ty}"; -# {ty} v = new {ty}(); -.method public static {test_name}()V - .locals 3 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v0, "Testing for type {ty}" - invoke-virtual {{v2,v0}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - - const-string v0, "{ty}" - new-instance v1, L{ty}; - invoke-direct {{v1}}, L{ty};->()V + public static void {test_name}() {{ + System.out.println("Testing for type {ty}"); + String s = "{ty}"; + {ty} v = new {ty}(); {invokes} - const-string v0, "End testing for type {ty}" - invoke-virtual {{v2,v0}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V - return-void -.end method -# System.out.println("End testing for type {ty}"); -# }} + System.out.println("End testing for type {ty}"); + }} """ TEST_INVOKE_TEMPLATE = """ -# {fname}(s, v); - invoke-static {{v0, v1}}, {fname}(Ljava/lang/String;L{farg};)V + {fname}(s, v); """ def __init__(self, main, ty): @@ -188,7 +153,7 @@ class InstanceTest(mixins.Named, mixins.NameComparableMixin): def __str__(self): """ - Returns the smali code for this function + Returns the java code for this function """ func_invokes = "" for f in sorted(self.funcs, key=lambda a: (a.func, a.farg)): @@ -204,47 +169,15 @@ class Func(mixins.Named, mixins.NameComparableMixin): """ TEST_FUNCTION_TEMPLATE = """ -# public static void {fname}(String s, {farg} v) {{ -# try {{ -# System.out.printf("%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n", s, v.{callfunc}()); -# return; -# }} catch (Error e) {{ -# System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s); -# e.printStackTrace(System.out); -# }} -# }} -.method public static {fname}(Ljava/lang/String;L{farg};)V - .locals 7 - :call_{fname}_try_start - const/4 v0, 2 - new-array v1,v0, [Ljava/lang/Object; - const/4 v0, 0 - aput-object p0,v1,v0 - - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v3, "%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n" - - invoke-{invoke_type} {{p1}}, L{farg};->{callfunc}()Ljava/lang/String; - move-result-object v4 - const/4 v0, 1 - aput-object v4, v1, v0 - - invoke-virtual {{v2,v3,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; - return-void - :call_{fname}_try_end - .catch Ljava/lang/Error; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start - :error_{fname}_start - move-exception v3 - const/4 v0, 1 - new-array v1,v0, [Ljava/lang/Object; - const/4 v0, 0 - aput-object p0, v1, v0 - sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; - const-string v4, "%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n" - invoke-virtual {{v2,v4,v1}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; - invoke-virtual {{v3,v2}}, Ljava/lang/Error;->printStackTrace(Ljava/io/PrintStream;)V - return-void -.end method + public static void {fname}(String s, {farg} v) {{ + try {{ + System.out.printf("%s-{invoke_type:<9} {farg:>9}.{callfunc}()='%s'\\n", s, v.{callfunc}()); + return; + }} catch (Error e) {{ + System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s); + e.printStackTrace(System.out); + }} + }} """ def __init__(self, func, farg, invoke): @@ -263,7 +196,7 @@ class Func(mixins.Named, mixins.NameComparableMixin): def __str__(self): """ - Get the smali code for this test function + Get the java code for this test function """ return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg, @@ -307,7 +240,7 @@ def flatten_interface_methods(dat, i): def make_main_class(dat): """ - Creates a Main.smali file that runs all the tests + Creates a Main.java file that runs all the tests """ m = MainClass() for c in dat.classes.values(): @@ -365,12 +298,12 @@ def parse_xml(xml): return TestData(classes, ifaces) def main(argv): - smali_dir = Path(argv[1]) - if not smali_dir.exists() or not smali_dir.is_dir(): - print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr) + java_dir = Path(argv[1]) + if not java_dir.exists() or not java_dir.is_dir(): + print("{} is not a valid java dir".format(java_dir), file=sys.stderr) sys.exit(1) - class_data = parse_xml((smali_dir / "classes.xml").open().read()) - make_main_class(class_data).dump(smali_dir) + class_data = parse_xml((java_dir / "classes.xml").open().read()) + make_main_class(class_data).dump(java_dir) if __name__ == '__main__': main(sys.argv) diff --git a/test/valgrind-suppressions.txt b/test/valgrind-suppressions.txt new file mode 100644 index 0000000000000000000000000000000000000000..acab6e5135f2f4a3ac177a4cf17f66956856e511 --- /dev/null +++ b/test/valgrind-suppressions.txt @@ -0,0 +1,15 @@ +{ + b/27596582 + Memcheck:Cond + fun:index + fun:expand_dynamic_string_token + fun:_dl_map_object + fun:map_doit + fun:_dl_catch_error + fun:do_preload + fun:dl_main + fun:_dl_sysdep_start + fun:_dl_start_final + fun:_dl_start + obj:/lib/x86_64-linux-gnu/ld-2.19.so +} diff --git a/tools/Android.mk b/tools/Android.mk index 9a96f7a6e753a18918f2ff332fed4ffe280d1797..bc2fd8c53c9798fcded662be32e872ead119c990 100644 --- a/tools/Android.mk +++ b/tools/Android.mk @@ -19,21 +19,14 @@ LOCAL_PATH := $(call my-dir) # Copy the art shell script to the host's bin directory include $(CLEAR_VARS) LOCAL_IS_HOST_MODULE := true -LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := art -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ +LOCAL_SRC_FILES := art +include $(BUILD_PREBUILT) # Copy the art shell script to the target's bin directory include $(CLEAR_VARS) LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := art -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ +LOCAL_SRC_FILES := art +include $(BUILD_PREBUILT) diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index 6869b04a0b2c16ee78545fd0bd6bbd84c3ae12ac..cfbafde52b1f9d47eb7445abfe174e0d50d947ad 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -35,16 +35,10 @@ include $(BUILD_HOST_JAVA_LIBRARY) # --- ahat script ---------------- include $(CLEAR_VARS) LOCAL_IS_HOST_MODULE := true -LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := ahat -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/ahat $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ - -ahat: $(LOCAL_BUILT_MODULE) +LOCAL_SRC_FILES := ahat +include $(BUILD_PREBUILT) # --- ahat-tests.jar -------------- include $(CLEAR_VARS) diff --git a/tools/checker/common/logger.py b/tools/checker/common/logger.py index 28bb458da72a68b7b07346f6e34741e7ca0603c6..f13eaf6142ddbcffdc42a8b64490ef35b381cfb6 100644 --- a/tools/checker/common/logger.py +++ b/tools/checker/common/logger.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import print_function +import collections import sys class Logger(object): @@ -21,7 +22,7 @@ class Logger(object): NoOutput, Error, Info = range(3) class Color(object): - Default, Blue, Gray, Purple, Red = range(5) + Default, Blue, Gray, Purple, Red, Green = range(6) @staticmethod def terminalCode(color, out=sys.stdout): @@ -35,6 +36,8 @@ class Logger(object): return '\033[95m' elif color == Logger.Color.Red: return '\033[91m' + elif color == Logger.Color.Green: + return '\033[32m' else: return '\033[0m' @@ -52,19 +55,34 @@ class Logger(object): out.flush() @staticmethod - def fail(msg, file=None, line=-1): - location = "" - if file: - location += file + ":" - if line > 0: - location += str(line) + ":" - if location: - location += " " - - Logger.log(location, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr) + def fail(msg, file=None, line=-1, lineText=None, variables=None): Logger.log("error: ", Logger.Level.Error, color=Logger.Color.Red, newLine=False, out=sys.stderr) Logger.log(msg, Logger.Level.Error, out=sys.stderr) - sys.exit(msg) + + if lineText: + loc = "" + if file: + loc += file + ":" + if line > 0: + loc += str(line) + ":" + if loc: + loc += " " + Logger.log(loc, Logger.Level.Error, color=Logger.Color.Gray, newLine=False, out=sys.stderr) + Logger.log(lineText, Logger.Level.Error, out=sys.stderr) + + if variables: + longestName = 0 + for var in variables: + longestName = max(longestName, len(var)) + + for var in collections.OrderedDict(sorted(variables.items())): + padding = ' ' * (longestName - len(var)) + Logger.log(var, Logger.Level.Error, color=Logger.Color.Green, newLine=False, out=sys.stderr) + Logger.log(padding, Logger.Level.Error, newLine=False, out=sys.stderr) + Logger.log(" = ", Logger.Level.Error, newLine=False, out=sys.stderr) + Logger.log(variables[var], Logger.Level.Error, out=sys.stderr) + + sys.exit(1) @staticmethod def startTest(name): @@ -76,6 +94,6 @@ class Logger(object): Logger.log("PASS", color=Logger.Color.Blue) @staticmethod - def testFailed(msg, file=None, line=-1): + def testFailed(msg, assertion, variables): Logger.log("FAIL", color=Logger.Color.Red) - Logger.fail(msg, file, line) + Logger.fail(msg, assertion.fileName, assertion.lineNo, assertion.originalText, variables) diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py index 3ded07482f9e787e7304cf3151209b2614251970..6ff19d5197e11de585612f2a1ff7ab31beec840e 100644 --- a/tools/checker/match/file.py +++ b/tools/checker/match/file.py @@ -23,9 +23,10 @@ MatchScope = namedtuple("MatchScope", ["start", "end"]) MatchInfo = namedtuple("MatchInfo", ["scope", "variables"]) class MatchFailedException(Exception): - def __init__(self, assertion, lineNo): + def __init__(self, assertion, lineNo, variables): self.assertion = assertion self.lineNo = lineNo + self.variables = variables def splitIntoGroups(assertions): """ Breaks up a list of assertions, grouping instructions which should be @@ -58,7 +59,7 @@ def findMatchingLine(assertion, c1Pass, scope, variables, excludeLines=[]): newVariables = MatchLines(assertion, c1Pass.body[i], variables) if newVariables is not None: return MatchInfo(MatchScope(i, i), newVariables) - raise MatchFailedException(assertion, scope.start) + raise MatchFailedException(assertion, scope.start, variables) def matchDagGroup(assertions, c1Pass, scope, variables): """ Attempts to find matching `c1Pass` lines for a group of DAG assertions. @@ -92,12 +93,12 @@ def testNotGroup(assertions, c1Pass, scope, variables): for assertion in assertions: assert assertion.variant == TestAssertion.Variant.Not if MatchLines(assertion, line, variables) is not None: - raise MatchFailedException(assertion, i) + raise MatchFailedException(assertion, i, variables) def testEvalGroup(assertions, scope, variables): for assertion in assertions: if not EvaluateLine(assertion, variables): - raise MatchFailedException(assertion, scope.start) + raise MatchFailedException(assertion, scope.start, variables) def MatchTestCase(testCase, c1Pass): """ Runs a test case against a C1visualizer graph dump. @@ -181,8 +182,8 @@ def MatchFiles(checkerFile, c1File, targetArch, debuggableMode): except MatchFailedException as e: lineNo = c1Pass.startLineNo + e.lineNo if e.assertion.variant == TestAssertion.Variant.Not: - Logger.testFailed("NOT assertion matched line {}".format(lineNo), - e.assertion.fileName, e.assertion.lineNo) + msg = "NOT assertion matched line {}" else: - Logger.testFailed("Assertion could not be matched starting from line {}".format(lineNo), - e.assertion.fileName, e.assertion.lineNo) + msg = "Assertion could not be matched starting from line {}" + msg = msg.format(lineNo) + Logger.testFailed(msg, e.assertion, e.variables) diff --git a/tools/checker/match/line.py b/tools/checker/match/line.py index 08f001f66028d043890885252d0e25a56b908e6d..ed48a5329fc0eac13cb6a2b6ea96aec0817c31c3 100644 --- a/tools/checker/match/line.py +++ b/tools/checker/match/line.py @@ -35,15 +35,13 @@ def getVariable(name, variables, pos): if name in variables: return variables[name] else: - Logger.testFailed("Missing definition of variable \"{}\"".format(name), - pos.fileName, pos.lineNo) + Logger.testFailed("Missing definition of variable \"{}\"".format(name), pos, variables) def setVariable(name, value, variables, pos): if name not in variables: return variables.copyWith(name, value) else: - Logger.testFailed("Multiple definitions of variable \"{}\"".format(name), - pos.fileName, pos.lineNo) + Logger.testFailed("Multiple definitions of variable \"{}\"".format(name), pos, variables) def matchWords(checkerWord, stringWord, variables, pos): """ Attempts to match a list of TestExpressions against a string. diff --git a/tools/dexfuzz/Android.mk b/tools/dexfuzz/Android.mk index 1580bc37fb47aaa71364f0b2fac42af65a6745dc..473f6de3e501ca4e0d873ec07a506a0e25e8d291 100644 --- a/tools/dexfuzz/Android.mk +++ b/tools/dexfuzz/Android.mk @@ -27,14 +27,10 @@ include $(BUILD_HOST_JAVA_LIBRARY) # --- dexfuzz script ---------------- include $(CLEAR_VARS) LOCAL_IS_HOST_MODULE := true -LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := EXECUTABLES LOCAL_MODULE := dexfuzz -include $(BUILD_SYSTEM)/base_rules.mk -$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dexfuzz $(ACP) - @echo "Copy: $(PRIVATE_MODULE) ($@)" - $(copy-file-to-new-target) - $(hide) chmod 755 $@ +LOCAL_SRC_FILES := dexfuzz +include $(BUILD_PREBUILT) # --- dexfuzz script with core image dependencies ---------------- fuzzer: $(LOCAL_BUILT_MODULE) $(HOST_CORE_IMG_OUTS) diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index e6394a999f7a426d8d125a8d59786597fef71f24..46100ae15c6f9927a65e1d96693089e156264cb1 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -207,13 +207,6 @@ "org.apache.harmony.crypto.tests.javax.crypto.spec.SecretKeySpecTest#testGetFormat", "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, diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index f347429ee109ea8036fc6007cca5512cd548f1b7..95f0c2dcf2020d3defea92d6315b07ac650f6b3c 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -16,34 +16,5 @@ names: ["jsr166.LinkedTransferQueueTest#testTransfer2", "jsr166.LinkedTransferQueueTest#testWaitingConsumer"], bug: 25883050 -}, -{ - description: "libcore.java.lang.OldSystemTest#test_gc failure on armv8-concurrent-collector.", - result: EXEC_FAILED, - names: ["libcore.java.lang.OldSystemTest#test_gc"], - 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.GZIPInputStreamTest#testLongMessage", - "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], - names: ["libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", - "libcore.java.text.SimpleDateFormatTest#testLocales", - "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries"], - bug: 26711853 } ] diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index e4af9fa0d740101b060270041021666e805ca782..8422e20823cd0147b084a930ffefb19f8c8c30ba 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -51,7 +51,7 @@ vm_command="--vm-command=$art" image_compiler_option="" debug="no" verbose="no" -image="-Ximage:/data/art-test/core-jit.art" +image="-Ximage:/data/art-test/core-optimizing-pic.art" vm_args="" # By default, we run the whole JDWP test suite. test="org.apache.harmony.jpda.tests.share.AllTests" @@ -70,9 +70,6 @@ while true; do device_dir="" # Vogar knows which VM to use on host. vm_command="" - # We only compile the image on the host. Note that not providing this option - # for target testing puts us below the adb command limit for vogar. - image_compiler_option="--vm-arg -Ximage-compiler-option --vm-arg --debuggable" shift elif [[ $1 == -Ximage:* ]]; then image="$1" diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index d5b89897e5f19ab14da86e7bf2917b8720185a6c..1e9c763534e4c2f8e7d688379a5c61f73573cc27 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -34,6 +34,25 @@ adb shell getprop echo -e "${green}Uptime${nc}" adb shell uptime +echo -e "${green}Battery info${nc}" +adb shell dumpsys battery + +echo -e "${green}Setting adb buffer size to 32MB${nc}" +adb logcat -G 32M +adb logcat -g + +echo -e "${green}Removing adb spam filter${nc}" +adb logcat -P "" +adb logcat -p + 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 +# 'ps' on M can sometimes hang. +timeout 2s adb shell "ps" +if [ $? = 124 ]; then + echo -e "${green}Rebooting device to fix 'ps'${nc}" + adb reboot + adb wait-for-device root +else + processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}') + for i in $processes; do adb shell kill -9 $i; done +fi diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh new file mode 100755 index 0000000000000000000000000000000000000000..8dc4e27885597fb2fe94b4c050714ec026f16932 --- /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