diff options
536 files changed, 24561 insertions, 3465 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index eec471e457..dc5385309a 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -318,6 +318,10 @@ ifeq ($(ART_USE_READ_BARRIER),true) art_cflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1 art_asflags += -DART_USE_READ_BARRIER=1 art_asflags += -DART_READ_BARRIER_TYPE_IS_$(ART_READ_BARRIER_TYPE)=1 + + # Temporarily override -fstack-protector-strong with -fstack-protector to avoid a major + # slowdown with the read barrier config. b/26744236. + art_cflags += -fstack-protector endif ifeq ($(ART_USE_TLAB),true) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index af64470a17..e3f0c24414 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -39,6 +39,7 @@ GTEST_DEX_DIRECTORIES := \ NonStaticLeafMethods \ ProtoCompare \ ProtoCompare2 \ + ProfileTestMultiDex \ StaticLeafMethods \ Statics \ StaticsFromCode \ @@ -65,7 +66,7 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) # Dex file dependencies for each gtest. ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode -ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods +ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle @@ -78,6 +79,8 @@ ART_GTEST_oat_test_DEX_DEPS := Main ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY ART_GTEST_proxy_test_DEX_DEPS := Interfaces ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods +ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex +ART_GTEST_profile_compilation_info_test_DEX_DEPS := ProfileTestMultiDex ART_GTEST_stub_test_DEX_DEPS := AllFields ART_GTEST_transaction_test_DEX_DEPS := Transaction ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup @@ -191,13 +194,12 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ runtime/gc/collector/immune_spaces_test.cc \ runtime/gc/heap_test.cc \ runtime/gc/reference_queue_test.cc \ - runtime/gc/space/dlmalloc_space_base_test.cc \ runtime/gc/space/dlmalloc_space_static_test.cc \ runtime/gc/space/dlmalloc_space_random_test.cc \ - runtime/gc/space/rosalloc_space_base_test.cc \ + runtime/gc/space/large_object_space_test.cc \ runtime/gc/space/rosalloc_space_static_test.cc \ runtime/gc/space/rosalloc_space_random_test.cc \ - runtime/gc/space/large_object_space_test.cc \ + runtime/gc/space/space_create_test.cc \ runtime/gc/task_processor_test.cc \ runtime/gtest_test.cc \ runtime/handle_scope_test.cc \ @@ -208,6 +210,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ runtime/interpreter/safe_math_test.cc \ runtime/interpreter/unstarted_runtime_test.cc \ runtime/java_vm_ext_test.cc \ + runtime/jit/profile_compilation_info_test.cc \ runtime/lambda/closure_test.cc \ runtime/lambda/shorty_field_type_test.cc \ runtime/leb128_test.cc \ @@ -267,6 +270,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/optimizing/ssa_test.cc \ compiler/optimizing/stack_map_test.cc \ compiler/optimizing/suspend_check_test.cc \ + compiler/profile_assistant_test.cc \ compiler/utils/arena_allocator_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/swap_space_test.cc \ diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc index fe83ba9e14..dc2c9c954a 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -535,7 +535,7 @@ TEST_F(CmdlineParserTest, TestProfilerOptions) { /* -Xexperimental:_ */ TEST_F(CmdlineParserTest, TestExperimentalFlags) { - // Off by default + // Default EXPECT_SINGLE_PARSE_DEFAULT_VALUE(ExperimentalFlags::kNone, "", M::Experimental); @@ -549,16 +549,6 @@ TEST_F(CmdlineParserTest, TestExperimentalFlags) { EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kLambdas, "-Xexperimental:lambdas", M::Experimental); - // Enabled explicitly - EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kDefaultMethods, - "-Xexperimental:default-methods", - M::Experimental); - - // Enabled both - EXPECT_SINGLE_PARSE_VALUE(ExperimentalFlags::kDefaultMethods | ExperimentalFlags::kLambdas, - "-Xexperimental:default-methods " - "-Xexperimental:lambdas", - M::Experimental); } // -Xverify:_ diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h index 6c0a0e1f4f..740199d541 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -616,6 +616,8 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> { log_verbosity.threads = true; } else if (verbose_options[j] == "verifier") { log_verbosity.verifier = true; + } else if (verbose_options[j] == "image") { + log_verbosity.image = true; } else { return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]); } @@ -845,11 +847,9 @@ template<> struct CmdlineType<ExperimentalFlags> : CmdlineTypeParser<ExperimentalFlags> { Result ParseAndAppend(const std::string& option, ExperimentalFlags& existing) { if (option == "none") { - existing = existing | ExperimentalFlags::kNone; + existing = ExperimentalFlags::kNone; } else if (option == "lambdas") { existing = existing | ExperimentalFlags::kLambdas; - } else if (option == "default-methods") { - existing = existing | ExperimentalFlags::kDefaultMethods; } else { return Result::Failure(std::string("Unknown option '") + option + "'"); } diff --git a/compiler/Android.mk b/compiler/Android.mk index 458973684e..6f32e07ce1 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -258,9 +258,9 @@ define build-libart-compiler ifeq ($$(art_ndebug_or_debug),ndebug) LOCAL_MODULE := libart-compiler ifeq ($$(art_static_or_shared), static) - LOCAL_STATIC_LIBRARIES += libart liblz4 + LOCAL_STATIC_LIBRARIES += libart liblz4 liblzma else - LOCAL_SHARED_LIBRARIES += libart liblz4 + LOCAL_SHARED_LIBRARIES += libart liblz4 liblzma endif ifeq ($$(art_target_or_host),target) LOCAL_FDO_SUPPORT := true @@ -268,9 +268,9 @@ define build-libart-compiler else # debug LOCAL_MODULE := libartd-compiler ifeq ($$(art_static_or_shared), static) - LOCAL_STATIC_LIBRARIES += libartd liblz4 + LOCAL_STATIC_LIBRARIES += libartd liblz4 liblzma else - LOCAL_SHARED_LIBRARIES += libartd liblz4 + LOCAL_SHARED_LIBRARIES += libartd liblz4 liblzma endif endif diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 9594dce029..afc8463878 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -168,6 +168,12 @@ std::unordered_set<std::string>* CommonCompilerTest::GetCompiledMethods() { return nullptr; } +// Get ProfileCompilationInfo that should be passed to the driver. +ProfileCompilationInfo* CommonCompilerTest::GetProfileCompilationInfo() { + // Null, profile information will not be taken into account. + return nullptr; +} + void CommonCompilerTest::SetUp() { CommonRuntimeTest::SetUp(); { @@ -207,7 +213,7 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe timer_.get(), -1, /* dex_to_oat_map */ nullptr, - /* profile_compilation_info */ nullptr)); + 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 b491946dc3..7e0fbabff8 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -23,6 +23,7 @@ #include "common_runtime_test.h" #include "compiler.h" +#include "jit/offline_profiling_info.h" #include "oat_file.h" namespace art { @@ -75,6 +76,8 @@ class CommonCompilerTest : public CommonRuntimeTest { // driver assumes ownership of the set, so the test should properly release the set. virtual std::unordered_set<std::string>* GetCompiledMethods(); + virtual ProfileCompilationInfo* GetProfileCompilationInfo(); + virtual void TearDown(); void CompileClass(mirror::ClassLoader* class_loader, const char* class_name) diff --git a/compiler/dex/mir_method_info.cc b/compiler/dex/mir_method_info.cc index 658e7d67a0..c250bd9fd2 100644 --- a/compiler/dex/mir_method_info.cc +++ b/compiler/dex/mir_method_info.cc @@ -100,8 +100,12 @@ void MirMethodLoweringInfo::Resolve(CompilerDriver* compiler_driver, } else { // The method index is actually the dex PC in this case. // Calculate the proper dex file and target method idx. + + // We must be in JIT mode if we get here. CHECK(use_jit); - CHECK_EQ(invoke_type, kVirtual); + + // The invoke type better be virtual, except for the string init special case above. + CHECK_EQ(invoke_type, string_init ? kDirect : kVirtual); // Don't devirt if we are in a different dex file since we can't have direct invokes in // another dex file unless we always put a direct / patch pointer. devirt_target = nullptr; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index d68835a9cf..af6f91f21d 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1076,7 +1076,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena pc_rel_temp_(nullptr), dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()), cfi_(&last_lir_insn_, - cu->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo(), + cu->compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo(), arena), in_to_reg_storage_mapping_(arena) { switch_tables_.reserve(4); diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index 4617668ee8..22b178ce7d 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -37,12 +37,21 @@ namespace { // anonymous namespace static constexpr bool kIntrinsicIsStatic[] = { true, // kIntrinsicDoubleCvt true, // kIntrinsicFloatCvt + true, // kIntrinsicFloatIsInfinite + true, // kIntrinsicDoubleIsInfinite + true, // kIntrinsicFloatIsNaN + true, // kIntrinsicDoubleIsNaN true, // kIntrinsicReverseBits true, // kIntrinsicReverseBytes + true, // kIntrinsicBitCount + true, // kIntrinsicCompare, + true, // kIntrinsicHighestOneBit + true, // kIntrinsicLowestOneBit true, // kIntrinsicNumberOfLeadingZeros true, // kIntrinsicNumberOfTrailingZeros true, // kIntrinsicRotateRight true, // kIntrinsicRotateLeft + true, // kIntrinsicSignum true, // kIntrinsicAbsInt true, // kIntrinsicAbsLong true, // kIntrinsicAbsFloat @@ -97,14 +106,23 @@ 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[kIntrinsicFloatIsInfinite], "FloatIsInfinite must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsInfinite], "DoubleIsInfinite must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicFloatIsNaN], "FloatIsNaN must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicDoubleIsNaN], "DoubleIsNaN must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicReverseBits], "ReverseBits must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicBitCount], "BitCount must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicCompare], "Compare must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicHighestOneBit], "HighestOneBit must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicLowestOneBit], "LowestOneBit must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfLeadingZeros], "NumberOfLeadingZeros must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfTrailingZeros], "NumberOfTrailingZeros must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicRotateRight], "RotateRight must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicRotateLeft], "RotateLeft must be static"); +static_assert(kIntrinsicIsStatic[kIntrinsicSignum], "Signum must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static"); static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static"); @@ -259,6 +277,8 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "equals", // kNameCacheEquals "getCharsNoCheck", // kNameCacheGetCharsNoCheck "isEmpty", // kNameCacheIsEmpty + "isInfinite", // kNameCacheIsInfinite + "isNaN", // kNameCacheIsNaN "indexOf", // kNameCacheIndexOf "length", // kNameCacheLength "<init>", // kNameCacheInit @@ -293,10 +313,15 @@ const char* const DexFileMethodInliner::kNameCacheNames[] = { "putObjectVolatile", // kNameCachePutObjectVolatile "putOrderedObject", // kNameCachePutOrderedObject "arraycopy", // kNameCacheArrayCopy + "bitCount", // kNameCacheBitCount + "compare", // kNameCacheCompare + "highestOneBit", // kNameCacheHighestOneBit + "lowestOneBit", // kNameCacheLowestOneBit "numberOfLeadingZeros", // kNameCacheNumberOfLeadingZeros "numberOfTrailingZeros", // kNameCacheNumberOfTrailingZeros "rotateRight", // kNameCacheRotateRight "rotateLeft", // kNameCacheRotateLeft + "signum", // kNameCacheSignum }; const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { @@ -316,10 +341,14 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { { kClassCacheFloat, 2, { kClassCacheFloat, kClassCacheFloat } }, // kProtoCacheD_J { kClassCacheLong, 1, { kClassCacheDouble } }, + // kProtoCacheD_Z + { kClassCacheBoolean, 1, { kClassCacheDouble } }, // kProtoCacheJ_D { kClassCacheDouble, 1, { kClassCacheLong } }, // kProtoCacheF_I { kClassCacheInt, 1, { kClassCacheFloat } }, + // kProtoCacheF_Z + { kClassCacheBoolean, 1, { kClassCacheFloat } }, // kProtoCacheI_F { kClassCacheFloat, 1, { kClassCacheInt } }, // kProtoCacheII_I @@ -348,6 +377,8 @@ const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = { { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheInt } }, // kProtoCacheJJ_J { kClassCacheLong, 2, { kClassCacheLong, kClassCacheLong } }, + // kProtoCacheJJ_I + { kClassCacheInt, 2, { kClassCacheLong, kClassCacheLong } }, // kProtoCacheJJ_V { kClassCacheVoid, 2, { kClassCacheLong, kClassCacheLong } }, // kProtoCacheJS_V @@ -441,16 +472,31 @@ const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods INTRINSIC(JavaLangFloat, FloatToRawIntBits, F_I, kIntrinsicFloatCvt, 0), INTRINSIC(JavaLangFloat, IntBitsToFloat, I_F, kIntrinsicFloatCvt, kIntrinsicFlagToFloatingPoint), + INTRINSIC(JavaLangFloat, IsInfinite, F_Z, kIntrinsicFloatIsInfinite, 0), + INTRINSIC(JavaLangDouble, IsInfinite, D_Z, kIntrinsicDoubleIsInfinite, 0), + INTRINSIC(JavaLangFloat, IsNaN, F_Z, kIntrinsicFloatIsNaN, 0), + INTRINSIC(JavaLangDouble, IsNaN, D_Z, kIntrinsicDoubleIsNaN, 0), + INTRINSIC(JavaLangInteger, ReverseBytes, I_I, kIntrinsicReverseBytes, k32), INTRINSIC(JavaLangLong, ReverseBytes, J_J, kIntrinsicReverseBytes, k64), INTRINSIC(JavaLangShort, ReverseBytes, S_S, kIntrinsicReverseBytes, kSignedHalf), INTRINSIC(JavaLangInteger, Reverse, I_I, kIntrinsicReverseBits, k32), INTRINSIC(JavaLangLong, Reverse, J_J, kIntrinsicReverseBits, k64), + INTRINSIC(JavaLangInteger, BitCount, I_I, kIntrinsicBitCount, k32), + INTRINSIC(JavaLangLong, BitCount, J_I, kIntrinsicBitCount, k64), + INTRINSIC(JavaLangInteger, Compare, II_I, kIntrinsicCompare, k32), + INTRINSIC(JavaLangLong, Compare, JJ_I, kIntrinsicCompare, k64), + INTRINSIC(JavaLangInteger, HighestOneBit, I_I, kIntrinsicHighestOneBit, k32), + INTRINSIC(JavaLangLong, HighestOneBit, J_J, kIntrinsicHighestOneBit, k64), + INTRINSIC(JavaLangInteger, LowestOneBit, I_I, kIntrinsicLowestOneBit, k32), + INTRINSIC(JavaLangLong, LowestOneBit, J_J, kIntrinsicLowestOneBit, k64), INTRINSIC(JavaLangInteger, NumberOfLeadingZeros, I_I, kIntrinsicNumberOfLeadingZeros, k32), INTRINSIC(JavaLangLong, NumberOfLeadingZeros, J_I, kIntrinsicNumberOfLeadingZeros, k64), INTRINSIC(JavaLangInteger, NumberOfTrailingZeros, I_I, kIntrinsicNumberOfTrailingZeros, k32), INTRINSIC(JavaLangLong, NumberOfTrailingZeros, J_I, kIntrinsicNumberOfTrailingZeros, k64), + INTRINSIC(JavaLangInteger, Signum, I_I, kIntrinsicSignum, k32), + INTRINSIC(JavaLangLong, Signum, J_I, kIntrinsicSignum, k64), INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0), INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0), @@ -745,10 +791,19 @@ bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) { intrinsic.d.data & kIntrinsicFlagIsOrdered); case kIntrinsicSystemArrayCopyCharArray: return backend->GenInlinedArrayCopyCharArray(info); + case kIntrinsicFloatIsInfinite: + case kIntrinsicDoubleIsInfinite: + case kIntrinsicFloatIsNaN: + case kIntrinsicDoubleIsNaN: + case kIntrinsicBitCount: + case kIntrinsicCompare: + case kIntrinsicHighestOneBit: + case kIntrinsicLowestOneBit: case kIntrinsicNumberOfLeadingZeros: case kIntrinsicNumberOfTrailingZeros: case kIntrinsicRotateRight: case kIntrinsicRotateLeft: + case kIntrinsicSignum: 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 ac70577b48..59b8a533ae 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, + kNameCacheIsInfinite, + kNameCacheIsNaN, kNameCacheIndexOf, kNameCacheLength, kNameCacheInit, @@ -224,10 +226,15 @@ class DexFileMethodInliner { kNameCachePutObjectVolatile, kNameCachePutOrderedObject, kNameCacheArrayCopy, + kNameCacheBitCount, + kNameCacheCompare, + kNameCacheHighestOneBit, + kNameCacheLowestOneBit, kNameCacheNumberOfLeadingZeros, kNameCacheNumberOfTrailingZeros, kNameCacheRotateRight, kNameCacheRotateLeft, + kNameCacheSignum, kNameCacheLast }; @@ -246,8 +253,10 @@ class DexFileMethodInliner { kProtoCacheF_F, kProtoCacheFF_F, kProtoCacheD_J, + kProtoCacheD_Z, kProtoCacheJ_D, kProtoCacheF_I, + kProtoCacheF_Z, kProtoCacheI_F, kProtoCacheII_I, kProtoCacheI_C, @@ -262,6 +271,7 @@ class DexFileMethodInliner { kProtoCacheJB_V, kProtoCacheJI_V, kProtoCacheJJ_J, + kProtoCacheJJ_I, kProtoCacheJJ_V, kProtoCacheJS_V, kProtoCacheObject_Z, diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index ebc9a2c9ea..027290f9b1 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -486,11 +486,6 @@ static const size_t kUnsupportedOpcodesSize[] = { static_assert(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t), "kUnsupportedOpcodesSize unexpected"); -static bool IsUnsupportedExperimentalLambdasOnly(size_t i) { - DCHECK_LE(i, arraysize(kUnsupportedOpcodes)); - return kUnsupportedOpcodes[i] == kUnsupportedLambdaOpcodes; -} - // The maximum amount of Dalvik register in a method for which we will start compiling. Tries to // avoid an abort when we need to manage more SSA registers than we can. static constexpr size_t kMaxAllowedDalvikRegisters = INT16_MAX / 2; @@ -513,36 +508,6 @@ static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set) return true; } -// If the ISA has unsupported opcodes, should we skip scanning over them? -// -// Most of the time we're compiling non-experimental files, so scanning just slows -// performance down by as much as 6% with 4 threads. -// In the rare cases we compile experimental opcodes, the runtime has an option to enable it, -// which will force scanning for any unsupported opcodes. -static bool SkipScanningUnsupportedOpcodes(InstructionSet instruction_set) { - Runtime* runtime = Runtime::Current(); - if (UNLIKELY(runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods))) { - // Always need to scan opcodes if we have default methods since invoke-super for interface - // methods is never going to be supported in the quick compiler. - return false; - } else if (UNLIKELY(kUnsupportedOpcodesSize[instruction_set] == 0U)) { - // All opcodes are supported no matter what. Usually not the case - // since experimental opcodes are not implemented in the quick compiler. - return true; - } else if (LIKELY(!Runtime::Current()-> - AreExperimentalFlagsEnabled(ExperimentalFlags::kLambdas))) { - // Experimental opcodes are disabled. - // - // If all unsupported opcodes are experimental we don't need to do scanning. - return IsUnsupportedExperimentalLambdasOnly(instruction_set); - } else { - // Experimental opcodes are enabled. - // - // Do the opcode scanning if the ISA has any unsupported opcodes. - return false; - } -} - bool QuickCompiler::CanCompileInstruction(const MIR* mir, const DexFile& dex_file) const { switch (mir->dalvikInsn.opcode) { @@ -572,11 +537,8 @@ bool QuickCompiler::CanCompileMethod(uint32_t method_idx, return false; } - // Check whether we do have limitations at all. - if (kSupportedTypes[cu->instruction_set] == nullptr && - SkipScanningUnsupportedOpcodes(cu->instruction_set)) { - return true; - } + // Since the quick compiler doesn't (and never will) support default methods we always need to + // scan opcodes. // Check if we can compile the prototype. const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx)); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index d0215255e8..818d50a994 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -730,8 +730,8 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const De bool CompilerDriver::IsImageClass(const char* descriptor) const { if (!IsBootImage()) { - // NOTE: Currently unreachable, all callers check IsImage(). - return false; + // NOTE: Currently only reachable from InitImageMethodVisitor for the app image case. + return true; } else { return image_classes_->find(descriptor) != image_classes_->end(); } diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index 82c0e86b25..4c03e5ddfe 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -31,6 +31,7 @@ #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" #include "handle_scope-inl.h" +#include "jit/offline_profiling_info.h" #include "scoped_thread_state_change.h" namespace art { @@ -240,6 +241,94 @@ TEST_F(CompilerDriverMethodsTest, Selection) { EXPECT_TRUE(expected->empty()); } +class CompilerDriverProfileTest : public CompilerDriverTest { + protected: + ProfileCompilationInfo* GetProfileCompilationInfo() OVERRIDE { + ScopedObjectAccess soa(Thread::Current()); + std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex"); + + ProfileCompilationInfo info; + for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { + std::cout << std::string(dex_file->GetLocation()); + profile_info_.AddData(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 1); + profile_info_.AddData(dex_file->GetLocation(), dex_file->GetLocationChecksum(), 2); + } + return &profile_info_; + } + + std::unordered_set<std::string> GetExpectedMethodsForClass(const std::string& clazz) { + if (clazz == "Main") { + return std::unordered_set<std::string>({ + "java.lang.String Main.getA()", + "java.lang.String Main.getB()"}); + } else if (clazz == "Second") { + return std::unordered_set<std::string>({ + "java.lang.String Second.getX()", + "java.lang.String Second.getY()"}); + } else { + return std::unordered_set<std::string>(); + } + } + + void CheckCompiledMethods(jobject class_loader, + const std::string& clazz, + const std::unordered_set<std::string>& expected_methods) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_loader(hs.NewHandle( + reinterpret_cast<mirror::ClassLoader*>(self->DecodeJObject(class_loader)))); + mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + ASSERT_NE(klass, nullptr); + + const auto pointer_size = class_linker->GetImagePointerSize(); + size_t number_of_compiled_methods = 0; + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + std::string name = PrettyMethod(&m, true); + const void* code = m.GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); + ASSERT_NE(code, nullptr); + if (expected_methods.find(name) != expected_methods.end()) { + number_of_compiled_methods++; + EXPECT_FALSE(class_linker->IsQuickToInterpreterBridge(code)); + } else { + EXPECT_TRUE(class_linker->IsQuickToInterpreterBridge(code)); + } + } + EXPECT_EQ(expected_methods.size(), number_of_compiled_methods); + } + + private: + ProfileCompilationInfo profile_info_; +}; + +TEST_F(CompilerDriverProfileTest, ProfileGuidedCompilation) { + TEST_DISABLED_FOR_HEAP_REFERENCE_POISONING_WITH_QUICK(); + TEST_DISABLED_FOR_READ_BARRIER_WITH_QUICK(); + TEST_DISABLED_FOR_READ_BARRIER_WITH_OPTIMIZING_FOR_UNSUPPORTED_INSTRUCTION_SETS(); + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Need to enable dex-file writability. Methods rejected to be compiled will run through the + // dex-to-dex compiler. + ProfileCompilationInfo info; + for (const DexFile* dex_file : GetDexFiles(class_loader)) { + ASSERT_TRUE(dex_file->EnableWrite()); + } + + CompileAll(class_loader); + + std::unordered_set<std::string> m = GetExpectedMethodsForClass("Main"); + std::unordered_set<std::string> s = GetExpectedMethodsForClass("Second"); + CheckCompiledMethods(class_loader, "LMain;", m); + CheckCompiledMethods(class_loader, "LSecond;", s); +} + // TODO: need check-cast test (when stub complete & we can throw/catch } // namespace art diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 2644528e56..e0b1d8b2b2 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -37,6 +37,7 @@ CompilerOptions::CompilerOptions() debuggable_(false), native_debuggable_(kDefaultNativeDebuggable), generate_debug_info_(kDefaultGenerateDebugInfo), + generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo), implicit_null_checks_(true), implicit_so_checks_(true), implicit_suspend_checks_(false), @@ -91,6 +92,7 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter, debuggable_(debuggable), native_debuggable_(kDefaultNativeDebuggable), generate_debug_info_(generate_debug_info), + generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo), implicit_null_checks_(implicit_null_checks), implicit_so_checks_(implicit_so_checks), implicit_suspend_checks_(implicit_suspend_checks), @@ -215,6 +217,10 @@ bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usa generate_debug_info_ = true; } else if (option == "--no-generate-debug-info") { generate_debug_info_ = false; + } else if (option == "--generate-mini-debug-info") { + generate_mini_debug_info_ = true; + } else if (option == "--no-generate-mini-debug-info") { + generate_mini_debug_info_ = false; } else if (option == "--debuggable") { debuggable_ = true; } else if (option == "--native-debuggable") { diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index d47fc2ad4b..5294af8b2c 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -51,6 +51,7 @@ class CompilerOptions FINAL { static constexpr double kDefaultTopKProfileThreshold = 90.0; static const bool kDefaultNativeDebuggable = false; static const bool kDefaultGenerateDebugInfo = false; + static const bool kDefaultGenerateMiniDebugInfo = false; static const bool kDefaultIncludePatchInformation = false; static const size_t kDefaultInlineDepthLimit = 3; static const size_t kDefaultInlineMaxCodeUnits = 32; @@ -170,10 +171,20 @@ class CompilerOptions FINAL { return native_debuggable_; } + // This flag controls whether the compiler collects debugging information. + // The other flags control how the information is written to disk. + bool GenerateAnyDebugInfo() const { + return GetGenerateDebugInfo() || GetGenerateMiniDebugInfo(); + } + bool GetGenerateDebugInfo() const { return generate_debug_info_; } + bool GetGenerateMiniDebugInfo() const { + return generate_mini_debug_info_; + } + bool GetImplicitNullChecks() const { return implicit_null_checks_; } @@ -266,6 +277,7 @@ class CompilerOptions FINAL { bool debuggable_; bool native_debuggable_; bool generate_debug_info_; + bool generate_mini_debug_info_; bool implicit_null_checks_; bool implicit_so_checks_; bool implicit_suspend_checks_; diff --git a/compiler/dwarf/debug_frame_opcode_writer.h b/compiler/dwarf/debug_frame_opcode_writer.h index 5a9964178a..f74f37ce8a 100644 --- a/compiler/dwarf/debug_frame_opcode_writer.h +++ b/compiler/dwarf/debug_frame_opcode_writer.h @@ -248,7 +248,7 @@ class DebugFrameOpCodeWriter : private Writer<Vector> { } } - void ALWAYS_INLINE DefCFAExpression(void * expr, int expr_size) { + void ALWAYS_INLINE DefCFAExpression(uint8_t* expr, int expr_size) { if (UNLIKELY(enabled_)) { ImplicitlyAdvancePC(); uses_dwarf3_features_ = true; @@ -258,7 +258,7 @@ class DebugFrameOpCodeWriter : private Writer<Vector> { } } - void ALWAYS_INLINE Expression(Reg reg, void * expr, int expr_size) { + void ALWAYS_INLINE Expression(Reg reg, uint8_t* expr, int expr_size) { if (UNLIKELY(enabled_)) { ImplicitlyAdvancePC(); uses_dwarf3_features_ = true; @@ -269,7 +269,7 @@ class DebugFrameOpCodeWriter : private Writer<Vector> { } } - void ALWAYS_INLINE ValExpression(Reg reg, void * expr, int expr_size) { + void ALWAYS_INLINE ValExpression(Reg reg, uint8_t* expr, int expr_size) { if (UNLIKELY(enabled_)) { ImplicitlyAdvancePC(); uses_dwarf3_features_ = true; diff --git a/compiler/dwarf/debug_info_entry_writer.h b/compiler/dwarf/debug_info_entry_writer.h index a551e4b495..e5bbed3c8e 100644 --- a/compiler/dwarf/debug_info_entry_writer.h +++ b/compiler/dwarf/debug_info_entry_writer.h @@ -22,6 +22,7 @@ #include "base/casts.h" #include "dwarf/dwarf_constants.h" +#include "dwarf/expression.h" #include "dwarf/writer.h" #include "leb128.h" @@ -106,16 +107,16 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { } } - void WriteBlock(Attribute attrib, const void* ptr, size_t num_bytes) { + void WriteBlock(Attribute attrib, const uint8_t* ptr, size_t num_bytes) { AddAbbrevAttribute(attrib, DW_FORM_block); this->PushUleb128(num_bytes); this->PushData(ptr, num_bytes); } - void WriteExprLoc(Attribute attrib, const void* ptr, size_t num_bytes) { + void WriteExprLoc(Attribute attrib, const Expression& expr) { AddAbbrevAttribute(attrib, DW_FORM_exprloc); - this->PushUleb128(dchecked_integral_cast<uint32_t>(num_bytes)); - this->PushData(ptr, num_bytes); + this->PushUleb128(dchecked_integral_cast<uint32_t>(expr.size())); + this->PushData(expr.data()); } void WriteData1(Attribute attrib, uint8_t value) { diff --git a/compiler/dwarf/dwarf_constants.h b/compiler/dwarf/dwarf_constants.h index 3b570e572a..0d7951b73a 100644 --- a/compiler/dwarf/dwarf_constants.h +++ b/compiler/dwarf/dwarf_constants.h @@ -200,11 +200,11 @@ enum Attribute { DW_AT_data_bit_offset = 0x6b, DW_AT_const_expr = 0x6c, DW_AT_enum_class = 0x6d, + DW_AT_linkage_name = 0x6e, #ifdef INCLUDE_DWARF5_VALUES // Values to be added in Dwarf 5. Final value not yet specified. Values listed // may be different than other implementations. Use with caution. // TODO Update these values when Dwarf 5 is released. - DW_AT_linkage_name = 0x6e, DW_AT_call_site_value = 0x6f, DW_AT_call_site_data_value = 0x70, DW_AT_call_site_target = 0x71, diff --git a/compiler/dwarf/expression.h b/compiler/dwarf/expression.h new file mode 100644 index 0000000000..1503d03d4f --- /dev/null +++ b/compiler/dwarf/expression.h @@ -0,0 +1,121 @@ +/* + * 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_DWARF_EXPRESSION_H_ +#define ART_COMPILER_DWARF_EXPRESSION_H_ + +#include <cstddef> +#include <cstdint> + +#include "dwarf/dwarf_constants.h" +#include "dwarf/writer.h" + +namespace art { +namespace dwarf { + +// Writer for DWARF expressions which are used in .debug_info and .debug_loc sections. +// See the DWARF specification for the precise meaning of the opcodes. +// If multiple equivalent encodings are possible, it will choose the most compact one. +// The writer is not exhaustive - it only implements opcodes we have needed so far. +class Expression : private Writer<> { + public: + using Writer<>::data; + using Writer<>::size; + + // Push signed integer on the stack. + void WriteOpConsts(int32_t value) { + if (0 <= value && value < 32) { + PushUint8(DW_OP_lit0 + value); + } else { + PushUint8(DW_OP_consts); + PushSleb128(value); + } + } + + // Push unsigned integer on the stack. + void WriteOpConstu(uint32_t value) { + if (value < 32) { + PushUint8(DW_OP_lit0 + value); + } else { + PushUint8(DW_OP_constu); + PushUleb128(value); + } + } + + // Variable is stored in given register. + void WriteOpReg(uint32_t dwarf_reg_num) { + if (dwarf_reg_num < 32) { + PushUint8(DW_OP_reg0 + dwarf_reg_num); + } else { + PushUint8(DW_OP_regx); + PushUleb128(dwarf_reg_num); + } + } + + // Variable is stored on stack. Also see DW_AT_frame_base. + void WriteOpFbreg(int32_t stack_offset) { + PushUint8(DW_OP_fbreg); + PushSleb128(stack_offset); + } + + // The variable is stored in multiple locations (pieces). + void WriteOpPiece(uint32_t num_bytes) { + PushUint8(DW_OP_piece); + PushUleb128(num_bytes); + } + + // Loads 32-bit or 64-bit value depending on architecture. + void WriteOpDeref() { PushUint8(DW_OP_deref); } + + // Loads value of given byte size. + void WriteOpDerefSize(uint8_t num_bytes) { + PushUint8(DW_OP_deref_size); + PushUint8(num_bytes); + } + + // Pop two values and push their sum. + void WriteOpPlus() { PushUint8(DW_OP_plus); } + + // Add constant value to value on top of stack. + void WriteOpPlusUconst(uint32_t offset) { + PushUint8(DW_OP_plus_uconst); + PushUleb128(offset); + } + + // Negate top of stack. + void WriteOpNeg() { PushUint8(DW_OP_neg); } + + // Pop two values and push their bitwise-AND. + void WriteOpAnd() { PushUint8(DW_OP_and); } + + // Push stack base pointer as determined from .debug_frame. + void WriteOpCallFrameCfa() { PushUint8(DW_OP_call_frame_cfa); } + + // Push address of the variable we are working with. + void WriteOpPushObjectAddress() { PushUint8(DW_OP_push_object_address); } + + // Return the top stack as the value of the variable. + // Otherwise, the top of stack is the variable's location. + void WriteOpStackValue() { PushUint8(DW_OP_stack_value); } + + explicit Expression(std::vector<uint8_t>* buffer) : Writer<>(buffer) { + buffer->clear(); + } +}; +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_EXPRESSION_H_ diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h index f76f76f7b6..137c5668f2 100644 --- a/compiler/dwarf/headers.h +++ b/compiler/dwarf/headers.h @@ -70,7 +70,7 @@ void WriteCIE(bool is64bit, writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata4); // R: Pointer encoding. } } - writer.PushData(*opcodes.data()); + writer.PushData(opcodes.data()); writer.Pad(is64bit ? 8 : 4); writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4); } @@ -117,7 +117,7 @@ void WriteFDE(bool is64bit, writer.PushUint32(code_size); } writer.PushUleb128(0); // Augmentation data size. - writer.PushData(opcodes); + writer.PushData(opcodes.data(), opcodes.size()); writer.Pad(is64bit ? 8 : 4); writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4); } @@ -139,7 +139,7 @@ void WriteDebugInfoCU(uint32_t debug_abbrev_offset, writer.PushUint8(entries.Is64bit() ? 8 : 4); size_t entries_offset = writer.data()->size(); DCHECK_EQ(entries_offset, DebugInfoEntryWriter<Vector>::kCompilationUnitHeaderSize); - writer.PushData(*entries.data()); + writer.PushData(entries.data()); writer.UpdateUint32(start, writer.data()->size() - start - 4); // Copy patch locations and make them relative to .debug_info section. for (uintptr_t patch_location : entries.GetPatchLocations()) { @@ -193,7 +193,7 @@ void WriteDebugLineTable(const std::vector<std::string>& include_directories, writer.PushUint8(0); // Terminate file list. writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4); size_t opcodes_offset = writer.data()->size(); - writer.PushData(*opcodes.data()); + writer.PushData(opcodes.data()); writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4); // Copy patch locations and make them relative to .debug_line section. for (uintptr_t patch_location : opcodes.GetPatchLocations()) { diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h index d2add7f026..74acf07abe 100644 --- a/compiler/dwarf/writer.h +++ b/compiler/dwarf/writer.h @@ -114,16 +114,16 @@ class Writer { data_->insert(data_->end(), value, value + strlen(value) + 1); } - void PushData(const void* ptr, size_t num_bytes) { - const char* p = reinterpret_cast<const char*>(ptr); - data_->insert(data_->end(), p, p + num_bytes); + void PushData(const uint8_t* ptr, size_t num_bytes) { + data_->insert(data_->end(), ptr, ptr + num_bytes); } - template<typename Vector2> - void PushData(const Vector2& buffer) { - static_assert(std::is_same<typename std::add_const<typename Vector::value_type>::type, - const uint8_t>::value, "Invalid value type"); - data_->insert(data_->end(), buffer.begin(), buffer.end()); + void PushData(const char* ptr, size_t num_bytes) { + data_->insert(data_->end(), ptr, ptr + num_bytes); + } + + void PushData(const Vector* buffer) { + data_->insert(data_->end(), buffer->begin(), buffer->end()); } void UpdateUint32(size_t offset, uint32_t value) { diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index a7461a5525..3d24d19919 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -100,12 +100,6 @@ class ElfBuilder FINAL { header_.sh_entsize = entsize; } - ~Section() OVERRIDE { - if (started_) { - CHECK(finished_); - } - } - // Start writing of this section. void Start() { CHECK(!started_); @@ -171,10 +165,15 @@ class ElfBuilder FINAL { } } - // Set desired allocation size for .bss section. - void SetSize(Elf_Word size) { - CHECK_EQ(header_.sh_type, (Elf_Word)SHT_NOBITS); + // Write this section as "NOBITS" section. (used for the .bss section) + // This means that the ELF file does not contain the initial data for this section + // and it will be zero-initialized when the ELF file is loaded in the running program. + void WriteNoBitsSection(Elf_Word size) { + DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); + Start(); + header_.sh_type = SHT_NOBITS; header_.sh_size = size; + End(); } // This function always succeeds to simplify code. @@ -352,6 +351,12 @@ class ElfBuilder FINAL { other_sections_.push_back(std::move(s)); } + // Set where the next section will be allocated in the virtual address space. + void SetVirtualAddress(Elf_Addr address) { + DCHECK_GE(address, virtual_address_); + virtual_address_ = address; + } + void Start() { // Reserve space for ELF header and program headers. // We do not know the number of headers until later, so diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc index e03614f090..2e98b69c47 100644 --- a/compiler/elf_writer_debug.cc +++ b/compiler/elf_writer_debug.cc @@ -16,15 +16,19 @@ #include "elf_writer_debug.h" +#include <algorithm> #include <unordered_set> #include <vector> +#include <cstdio> #include "base/casts.h" #include "base/stl_util.h" +#include "linear_alloc.h" #include "compiled_method.h" #include "dex_file-inl.h" #include "driver/compiler_driver.h" #include "dwarf/dedup_vector.h" +#include "dwarf/expression.h" #include "dwarf/headers.h" #include "dwarf/method_debug_info.h" #include "dwarf/register.h" @@ -37,6 +41,11 @@ #include "stack_map.h" #include "utils.h" +// liblzma. +#include "XzEnc.h" +#include "7zCrc.h" +#include "XzCrc64.h" + namespace art { namespace dwarf { @@ -219,7 +228,8 @@ static void WriteCIE(InstructionSet isa, template<typename ElfTypes> void WriteCFISection(ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, - CFIFormat format) { + CFIFormat format, + bool write_oat_patches) { CHECK(format == DW_DEBUG_FRAME_FORMAT || format == DW_EH_FRAME_FORMAT); typedef typename ElfTypes::Addr Elf_Addr; @@ -235,6 +245,24 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, patch_locations.reserve(method_infos.size()); } + // The methods can be written 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 + // compressed it reduces the size since similar opcodes sequences are grouped. + std::vector<const MethodDebugInfo*> 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]); + } + std::sort( + sorted_method_infos.begin(), + sorted_method_infos.end(), + [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { + ArrayRef<const uint8_t> l = lhs->compiled_method_->GetCFIInfo(); + ArrayRef<const uint8_t> r = rhs->compiled_method_->GetCFIInfo(); + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + }); + // Write .eh_frame/.debug_frame section. auto* cfi_section = (format == DW_DEBUG_FRAME_FORMAT ? builder->GetDebugFrame() @@ -253,11 +281,11 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, cfi_section->WriteFully(buffer.data(), buffer.size()); buffer_address += buffer.size(); buffer.clear(); - for (const MethodDebugInfo& mi : method_infos) { - if (!mi.deduped_) { // Only one FDE per unique address. - ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo(); + for (const MethodDebugInfo* mi : sorted_method_infos) { + if (!mi->deduped_) { // Only one FDE per unique address. + ArrayRef<const uint8_t> opcodes = mi->compiled_method_->GetCFIInfo(); if (!opcodes.empty()) { - const Elf_Addr code_address = text_address + mi.low_pc_; + const Elf_Addr code_address = text_address + mi->low_pc_; if (format == DW_EH_FRAME_FORMAT) { binary_search_table.push_back( dchecked_integral_cast<uint32_t>(code_address)); @@ -265,7 +293,7 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, dchecked_integral_cast<uint32_t>(buffer_address)); } WriteFDE(is64bit, cfi_address, cie_address, - code_address, mi.high_pc_ - mi.low_pc_, + code_address, mi->high_pc_ - mi->low_pc_, opcodes, format, buffer_address, &buffer, &patch_locations); cfi_section->WriteFully(buffer.data(), buffer.size()); @@ -306,8 +334,10 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, header_section->WriteFully(binary_search_table.data(), binary_search_table.size()); header_section->End(); } else { - builder->WritePatches(".debug_frame.oat_patches", - ArrayRef<const uintptr_t>(patch_locations)); + if (write_oat_patches) { + builder->WritePatches(".debug_frame.oat_patches", + ArrayRef<const uintptr_t>(patch_locations)); + } } } @@ -498,8 +528,10 @@ class DebugInfoWriter { 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<uint32_t>(mi->high_pc_-mi->low_pc_)); - uint8_t frame_base[] = { DW_OP_call_frame_cfa }; - info_.WriteExprLoc(DW_AT_frame_base, &frame_base, sizeof(frame_base)); + std::vector<uint8_t> expr_buffer; + Expression expr(&expr_buffer); + expr.WriteOpCallFrameCfa(); + info_.WriteExprLoc(DW_AT_frame_base, expr); WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto)); // Write parameters. DecodeDebugLocalInfo returns them as well, but it does not @@ -588,6 +620,7 @@ class DebugInfoWriter { info_.WriteStrp(DW_AT_producer, owner_->WriteString("Android dex2oat")); info_.WriteData1(DW_AT_language, DW_LANG_Java); + std::vector<uint8_t> expr_buffer; for (mirror::Class* type : types) { if (type->IsPrimitive()) { // For primitive types the definition and the declaration is the same. @@ -603,20 +636,33 @@ class DebugInfoWriter { info_.StartTag(DW_TAG_array_type); std::string descriptor_string; WriteLazyType(element_type->GetDescriptor(&descriptor_string)); + WriteLinkageName(type); info_.WriteUdata(DW_AT_data_member_location, data_offset); info_.StartTag(DW_TAG_subrange_type); - DCHECK_LT(length_offset, 32u); - uint8_t count[] = { - DW_OP_push_object_address, - static_cast<uint8_t>(DW_OP_lit0 + length_offset), - DW_OP_plus, - DW_OP_deref_size, - 4 // Array length is always 32-bit wide. - }; - info_.WriteExprLoc(DW_AT_count, &count, sizeof(count)); + Expression count_expr(&expr_buffer); + count_expr.WriteOpPushObjectAddress(); + count_expr.WriteOpPlusUconst(length_offset); + count_expr.WriteOpDerefSize(4); // Array length is always 32-bit wide. + info_.WriteExprLoc(DW_AT_count, count_expr); info_.EndTag(); // DW_TAG_subrange_type. info_.EndTag(); // DW_TAG_array_type. + } else if (type->IsInterface()) { + // Skip. Variables cannot have an interface as a dynamic type. + // We do not expose the interface information to the debugger in any way. } else { + // Declare base class. We can not use the standard WriteLazyType + // since we want to avoid the DW_TAG_reference_tag wrapping. + mirror::Class* base_class = type->GetSuperClass(); + size_t base_class_declaration_offset = 0; + if (base_class != nullptr) { + std::string tmp_storage; + const char* base_class_desc = base_class->GetDescriptor(&tmp_storage); + base_class_declaration_offset = StartClassTag(base_class_desc); + info_.WriteFlag(DW_AT_declaration, true); + WriteLinkageName(base_class); + EndClassTag(base_class_desc); + } + std::string descriptor_string; const char* desc = type->GetDescriptor(&descriptor_string); StartClassTag(desc); @@ -625,11 +671,40 @@ class DebugInfoWriter { info_.WriteUdata(DW_AT_byte_size, type->GetObjectSize()); } + WriteLinkageName(type); + + if (type->IsObjectClass()) { + // Generate artificial member which is used to get the dynamic type of variable. + // The run-time value of this field will correspond to linkage name of some type. + // We need to do it only once in j.l.Object since all other types inherit it. + info_.StartTag(DW_TAG_member); + WriteName(".dynamic_type"); + WriteLazyType(sizeof(uintptr_t) == 8 ? "J" : "I"); + info_.WriteFlag(DW_AT_artificial, true); + // Create DWARF expression to get the value of the methods_ field. + Expression expr(&expr_buffer); + // The address of the object has been implicitly pushed on the stack. + // Dereference the klass_ field of Object (32-bit; possibly poisoned). + DCHECK_EQ(type->ClassOffset().Uint32Value(), 0u); + DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Class>), 4u); + expr.WriteOpDerefSize(4); + if (kPoisonHeapReferences) { + expr.WriteOpNeg(); + // DWARF stack is pointer sized. Ensure that the high bits are clear. + expr.WriteOpConstu(0xFFFFFFFF); + expr.WriteOpAnd(); + } + // Add offset to the methods_ field. + expr.WriteOpPlusUconst(mirror::Class::MethodsOffset().Uint32Value()); + // Top of stack holds the location of the field now. + info_.WriteExprLoc(DW_AT_data_member_location, expr); + info_.EndTag(); // DW_TAG_member. + } + // Base class. - mirror::Class* base_class = type->GetSuperClass(); if (base_class != nullptr) { info_.StartTag(DW_TAG_inheritance); - WriteLazyType(base_class->GetDescriptor(&descriptor_string)); + info_.WriteRef4(DW_AT_type, base_class_declaration_offset); info_.WriteUdata(DW_AT_data_member_location, 0); info_.WriteSdata(DW_AT_accessibility, DW_ACCESS_public); info_.EndTag(); // DW_TAG_inheritance. @@ -684,6 +759,24 @@ class DebugInfoWriter { owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size()); } + // Linkage name uniquely identifies type. + // It is used to determine the dynamic type of objects. + // We use the methods_ field of class since it is unique and it is not moved by the GC. + void WriteLinkageName(mirror::Class* type) SHARED_REQUIRES(Locks::mutator_lock_) { + auto* methods_ptr = type->GetMethodsPtr(); + if (methods_ptr == nullptr) { + // Some types might have no methods. Allocate empty array instead. + LinearAlloc* allocator = Runtime::Current()->GetLinearAlloc(); + void* storage = allocator->Alloc(Thread::Current(), sizeof(LengthPrefixedArray<ArtMethod>)); + methods_ptr = new (storage) LengthPrefixedArray<ArtMethod>(0); + type->SetMethodsPtr(methods_ptr, 0, 0); + DCHECK(type->GetMethodsPtr() != nullptr); + } + char name[32]; + snprintf(name, sizeof(name), "0x%" PRIXPTR, reinterpret_cast<uintptr_t>(methods_ptr)); + info_.WriteString(DW_AT_linkage_name, name); + } + // Write table into .debug_loc which describes location of dex register. // The dex register might be valid only at some points and it might // move between machine registers and stack. @@ -713,12 +806,12 @@ class DebugInfoWriter { // Write .debug_loc entries. const InstructionSet isa = owner_->builder_->GetIsa(); const bool is64bit = Is64BitInstructionSet(isa); + std::vector<uint8_t> expr_buffer; for (const VariableLocation& variable_location : variable_locations) { // Translate dex register location to DWARF expression. // Note that 64-bit value might be split to two distinct locations. // (for example, two 32-bit machine registers, or even stack and register) - uint8_t buffer[64]; - uint8_t* pos = buffer; + Expression expr(&expr_buffer); DexRegisterLocation reg_lo = variable_location.reg_lo; DexRegisterLocation reg_hi = variable_location.reg_hi; for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) { @@ -727,15 +820,14 @@ class DebugInfoWriter { const int32_t value = reg_loc.GetValue(); if (kind == Kind::kInStack) { const size_t frame_size = method_info->compiled_method_->GetFrameSizeInBytes(); - *(pos++) = DW_OP_fbreg; // The stack offset is relative to SP. Make it relative to CFA. - pos = EncodeSignedLeb128(pos, value - frame_size); + expr.WriteOpFbreg(value - frame_size); if (piece == 0 && reg_hi.GetKind() == Kind::kInStack && reg_hi.GetValue() == value + 4) { break; // the high word is correctly implied by the low word. } } else if (kind == Kind::kInRegister) { - pos = WriteOpReg(pos, GetDwarfCoreReg(isa, value).num()); + expr.WriteOpReg(GetDwarfCoreReg(isa, value).num()); if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh && reg_hi.GetValue() == value) { break; // the high word is correctly implied by the low word. @@ -745,22 +837,21 @@ class DebugInfoWriter { piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister && reg_hi.GetValue() == value + 1 && value % 2 == 0) { // Translate S register pair to D register (e.g. S4+S5 to D2). - pos = WriteOpReg(pos, Reg::ArmDp(value / 2).num()); + expr.WriteOpReg(Reg::ArmDp(value / 2).num()); break; } if (isa == kMips || isa == kMips64) { // TODO: Find what the DWARF floating point register numbers are on MIPS. break; } - pos = WriteOpReg(pos, GetDwarfFpReg(isa, value).num()); + expr.WriteOpReg(GetDwarfFpReg(isa, value).num()); if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh && reg_hi.GetValue() == reg_lo.GetValue()) { break; // the high word is correctly implied by the low word. } } else if (kind == Kind::kConstant) { - *(pos++) = DW_OP_consts; - pos = EncodeSignedLeb128(pos, value); - *(pos++) = DW_OP_stack_value; + expr.WriteOpConsts(value); + expr.WriteOpStackValue(); } else if (kind == Kind::kNone) { break; } else { @@ -774,14 +865,11 @@ class DebugInfoWriter { if (is64bitValue) { // Write the marker which is needed by split 64-bit values. // This code is skipped by the special cases. - *(pos++) = DW_OP_piece; - pos = EncodeUnsignedLeb128(pos, 4); + expr.WriteOpPiece(4); } } - // Check that the buffer is large enough; keep half of it empty for safety. - DCHECK_LE(static_cast<size_t>(pos - buffer), sizeof(buffer) / 2); - if (pos > buffer) { + 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); @@ -790,8 +878,8 @@ class DebugInfoWriter { debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc); } // Write the expression. - debug_loc.PushUint16(pos - buffer); - debug_loc.PushData(buffer, pos - buffer); + debug_loc.PushUint16(expr.size()); + debug_loc.PushData(expr.data()); } else { // Do not generate .debug_loc if the location is not known. } @@ -856,17 +944,6 @@ class DebugInfoWriter { } } - // Helper which writes DWARF expression referencing a register. - static uint8_t* WriteOpReg(uint8_t* buffer, uint32_t dwarf_reg_num) { - if (dwarf_reg_num < 32) { - *(buffer++) = DW_OP_reg0 + dwarf_reg_num; - } else { - *(buffer++) = DW_OP_regx; - buffer = EncodeUnsignedLeb128(buffer, dwarf_reg_num); - } - return buffer; - } - // Convert dex type descriptor to DWARF. // Returns offset in the compilation unit. size_t WriteTypeDeclaration(const std::string& desc) { @@ -1108,9 +1185,13 @@ class DebugLineWriter { 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); - src_mapping_table_from_stack_maps.push_back({pc, dex}); + // Emit only locations where we have local-variable information. + // In particular, skip mappings inside the prologue. + if (stack_map.HasDexRegisterMap(encoding)) { + const uint32_t pc = stack_map.GetNativePcOffset(encoding); + const int32_t dex = stack_map.GetDexPc(encoding); + src_mapping_table_from_stack_maps.push_back({pc, dex}); + } } std::sort(src_mapping_table_from_stack_maps.begin(), src_mapping_table_from_stack_maps.end()); @@ -1284,8 +1365,9 @@ static void WriteDebugSections(ElfBuilder<ElfTypes>* builder, } template <typename ElfTypes> -void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, - const ArrayRef<const MethodDebugInfo>& method_infos) { +static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos, + bool with_signature) { bool generated_mapping_symbol = false; auto* strtab = builder->GetStrTab(); auto* symtab = builder->GetSymTab(); @@ -1305,22 +1387,31 @@ void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, strtab->Start(); strtab->Write(""); // strtab should start with empty string. + std::string last_name; + size_t last_name_offset = 0; for (const MethodDebugInfo& info : method_infos) { if (info.deduped_) { continue; // Add symbol only for the first instance. } - std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, true); + 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]"; } + // 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_; // Add in code delta, e.g., thumb bit 0 for Thumb2 code. low_pc += info.compiled_method_->CodeDelta(); - symtab->Add(strtab->Write(name), text, low_pc, - is_relative, info.high_pc_ - info.low_pc_, STB_GLOBAL, STT_FUNC); + symtab->Add(name_offset, + text, + low_pc, + is_relative, + info.high_pc_ - info.low_pc_, + 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. @@ -1333,6 +1424,9 @@ void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, generated_mapping_symbol = true; } } + + last_name = std::move(name); + last_name_offset = name_offset; } strtab->End(); @@ -1348,13 +1442,83 @@ void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, CFIFormat cfi_format) { // Add methods to .symtab. - WriteDebugSymbols(builder, method_infos); + WriteDebugSymbols(builder, method_infos, true /* with_signature */); // Generate CFI (stack unwinding information). - WriteCFISection(builder, method_infos, cfi_format); + WriteCFISection(builder, method_infos, cfi_format, true /* write_oat_patches */); // Write DWARF .debug_* sections. WriteDebugSections(builder, method_infos); } +static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) { + // Configure the compression library. + CrcGenerateTable(); + Crc64GenerateTable(); + CLzma2EncProps lzma2Props; + Lzma2EncProps_Init(&lzma2Props); + lzma2Props.lzmaProps.level = 1; // Fast compression. + Lzma2EncProps_Normalize(&lzma2Props); + CXzProps props; + XzProps_Init(&props); + props.lzma2Props = &lzma2Props; + // Implement the required interface for communication (written in C so no virtual methods). + struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress { + static SRes ReadImpl(void* p, void* buf, size_t* size) { + auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p)); + *size = std::min(*size, ctx->src_->size() - ctx->src_pos_); + memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size); + ctx->src_pos_ += *size; + return SZ_OK; + } + static size_t WriteImpl(void* p, const void* buf, size_t size) { + auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p)); + const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf); + ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size); + return size; + } + static SRes ProgressImpl(void* , UInt64, UInt64) { + return SZ_OK; + } + size_t src_pos_; + const std::vector<uint8_t>* src_; + std::vector<uint8_t>* dst_; + }; + XzCallbacks callbacks; + callbacks.Read = XzCallbacks::ReadImpl; + callbacks.Write = XzCallbacks::WriteImpl; + callbacks.Progress = XzCallbacks::ProgressImpl; + callbacks.src_pos_ = 0; + callbacks.src_ = src; + callbacks.dst_ = dst; + // Compress. + SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks); + CHECK_EQ(res, SZ_OK); +} + +template <typename ElfTypes> +void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* parent_builder, + const ArrayRef<const MethodDebugInfo>& method_infos) { + const InstructionSet isa = parent_builder->GetIsa(); + std::vector<uint8_t> buffer; + buffer.reserve(KB); + VectorOutputStream out("Mini-debug-info ELF file", &buffer); + std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); + builder->Start(); + // Write .rodata and .text as NOBITS sections. + // This allows tools to detect virtual address relocation of the parent ELF file. + builder->SetVirtualAddress(parent_builder->GetRoData()->GetAddress()); + builder->GetRoData()->WriteNoBitsSection(parent_builder->GetRoData()->GetSize()); + builder->SetVirtualAddress(parent_builder->GetText()->GetAddress()); + builder->GetText()->WriteNoBitsSection(parent_builder->GetText()->GetSize()); + WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */); + WriteCFISection(builder.get(), method_infos, DW_DEBUG_FRAME_FORMAT, false /* write_oat_paches */); + builder->End(); + CHECK(builder->Good()); + std::vector<uint8_t> compressed_buffer; + compressed_buffer.reserve(buffer.size() / 4); + XzCompress(&buffer, &compressed_buffer); + parent_builder->WriteSection(".gnu_debugdata", &compressed_buffer); +} + template <typename ElfTypes> static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal( const dwarf::MethodDebugInfo& method_info) { @@ -1427,6 +1591,12 @@ template void WriteDebugInfo<ElfTypes64>( ElfBuilder<ElfTypes64>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, CFIFormat cfi_format); +template void WriteMiniDebugInfo<ElfTypes32>( + ElfBuilder<ElfTypes32>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos); +template void WriteMiniDebugInfo<ElfTypes64>( + ElfBuilder<ElfTypes64>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos); } // namespace dwarf } // namespace art diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h index e4bc856c5e..e19da088da 100644 --- a/compiler/elf_writer_debug.h +++ b/compiler/elf_writer_debug.h @@ -35,6 +35,10 @@ void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, CFIFormat cfi_format); +template <typename ElfTypes> +void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos); + ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info); ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa, diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 7b1bdd72e5..6bf080a083 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -137,9 +137,7 @@ template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::SetBssSize(size_t bss_size) { auto* bss = builder_->GetBss(); if (bss_size != 0u) { - bss->Start(); - bss->SetSize(bss_size); - bss->End(); + bss->WriteNoBitsSection(bss_size); } } @@ -152,8 +150,13 @@ template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::WriteDebugInfo( const ArrayRef<const dwarf::MethodDebugInfo>& method_infos) { if (compiler_options_->GetGenerateDebugInfo()) { + // Generate all the debug information we can. dwarf::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat); } + if (compiler_options_->GetGenerateMiniDebugInfo()) { + // Generate only some information and compress it. + dwarf::WriteMiniDebugInfo(builder_.get(), method_infos); + } } template <typename ElfTypes> diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 6859605095..b65fb36167 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -95,25 +95,37 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { t.NewTiming("WriteElf"); SafeMap<std::string, std::string> key_value_store; - OatWriter oat_writer(class_linker->GetBootClassPath(), - 0, - 0, - 0, - compiler_driver_.get(), - writer.get(), - /*compiling_boot_image*/true, - &timings, - &key_value_store); + const std::vector<const DexFile*>& dex_files = class_linker->GetBootClassPath(); std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), &compiler_driver_->GetCompilerOptions(), oat_file.GetFile()); - bool success = writer->PrepareImageAddressSpace(); - ASSERT_TRUE(success); - elf_writer->Start(); - + OatWriter oat_writer(/*compiling_boot_image*/true, &timings); OutputStream* rodata = elf_writer->StartRoData(); + for (const DexFile* dex_file : dex_files) { + ArrayRef<const uint8_t> raw_dex_file( + reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), + dex_file->GetHeader().file_size_); + oat_writer.AddRawDexFileSource(raw_dex_file, + dex_file->GetLocation().c_str(), + dex_file->GetLocationChecksum()); + } + std::unique_ptr<MemMap> opened_dex_files_map; + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + bool dex_files_ok = oat_writer.WriteAndOpenDexFiles( + rodata, + oat_file.GetFile(), + compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), + &key_value_store, + &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); + bool rodata_ok = oat_writer.WriteRodata(rodata); ASSERT_TRUE(rodata_ok); elf_writer->EndRoData(rodata); @@ -123,12 +135,15 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { ASSERT_TRUE(text_ok); elf_writer->EndText(text); + bool header_ok = oat_writer.WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); + ASSERT_TRUE(header_ok); + elf_writer->SetBssSize(oat_writer.GetBssSize()); elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations()); - success = elf_writer->End(); + bool success = elf_writer->End(); ASSERT_TRUE(success); } @@ -140,7 +155,11 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { { std::vector<const char*> dup_oat_filename(1, dup_oat->GetPath().c_str()); std::vector<const char*> dup_image_filename(1, image_file.GetFilename().c_str()); - bool success_image = writer->Write(kInvalidImageFd, dup_image_filename, dup_oat_filename); + bool success_image = writer->Write(kInvalidFd, + dup_image_filename, + kInvalidFd, + dup_oat_filename, + dup_oat_filename[0]); ASSERT_TRUE(success_image); bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin(dup_oat_filename[0])); @@ -162,7 +181,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { ASSERT_NE(0U, bitmap_section.Size()); gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_TRUE(!heap->GetContinuousSpaces().empty()); + ASSERT_TRUE(heap->HaveContinuousSpaces()); gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); ASSERT_FALSE(space->IsImageSpace()); ASSERT_TRUE(space != nullptr); @@ -277,11 +296,17 @@ TEST_F(ImageTest, ImageHeaderIsValid) { oat_data_begin, oat_data_end, oat_file_end, + /*boot_image_begin*/0U, + /*boot_image_size*/0U, + /*boot_oat_begin*/0U, + /*boot_oat_size_*/0U, sizeof(void*), /*compile_pic*/false, + /*is_pic*/false, ImageHeader::kDefaultStorageMode, /*data_size*/0u); ASSERT_TRUE(image_header.IsValid()); + ASSERT_TRUE(!image_header.IsAppImage()); char* magic = const_cast<char*>(image_header.GetMagic()); strcpy(magic, ""); // bad magic diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index d0bb201d69..72c615e4bc 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -159,17 +159,45 @@ bool ImageWriter::PrepareImageAddressSpace() { bool ImageWriter::Write(int image_fd, const std::vector<const char*>& image_filenames, - const std::vector<const char*>& oat_filenames) { + int oat_fd, + const std::vector<const char*>& oat_filenames, + const std::string& oat_location) { + // 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()); + if (image_fd != kInvalidFd) { + CHECK_EQ(image_filenames.size(), 1u); + } CHECK(!oat_filenames.empty()); + if (oat_fd != kInvalidFd) { + CHECK_EQ(oat_filenames.size(), 1u); + } CHECK_EQ(image_filenames.size(), oat_filenames.size()); size_t oat_file_offset = 0; for (size_t i = 0; i < oat_filenames.size(); ++i) { const char* oat_filename = oat_filenames[i]; - std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename)); - if (oat_file.get() == nullptr) { + std::unique_ptr<File> oat_file; + + if (oat_fd != -1) { + if (strlen(oat_filename) == 0u) { + oat_file.reset(new File(oat_fd, false)); + } else { + oat_file.reset(new File(oat_fd, oat_filename, false)); + } + int length = oat_file->GetLength(); + if (length < 0) { + PLOG(ERROR) << "Oat file has negative length " << length; + return false; + } else { + // Leave the fd open since dex2oat still needs to write out the oat file with the fd. + oat_file->DisableAutoClose(); + } + } else { + oat_file.reset(OS::OpenFileReadWrite(oat_filename)); + } + if (oat_file == nullptr) { PLOG(ERROR) << "Failed to open oat file " << oat_filename; return false; } @@ -181,7 +209,7 @@ bool ImageWriter::Write(int image_fd, return false; } Runtime::Current()->GetOatFileManager().RegisterOatFile( - std::unique_ptr<const OatFile>(oat_file_)); + std::unique_ptr<const OatFile>(oat_file_)); const OatHeader& oat_header = oat_file_->GetOatHeader(); ImageInfo& image_info = GetImageInfo(oat_filename); @@ -220,8 +248,15 @@ bool ImageWriter::Write(int image_fd, SetOatChecksumFromElfFile(oat_file.get()); - if (oat_file->FlushCloseOrErase() != 0) { - LOG(ERROR) << "Failed to flush and close oat file " << oat_filename; + 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; } } @@ -238,16 +273,22 @@ bool ImageWriter::Write(int image_fd, const char* oat_filename = oat_filenames[i]; ImageInfo& image_info = GetImageInfo(oat_filename); std::unique_ptr<File> image_file; - if (image_fd != kInvalidImageFd) { - image_file.reset(new File(image_fd, image_filename, unix_file::kCheckSafeUsage)); + if (image_fd != kInvalidFd) { + if (strlen(image_filename) == 0u) { + image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage)); + } else { + LOG(ERROR) << "image fd " << image_fd << " name " << image_filename; + } } else { image_file.reset(OS::CreateEmptyFile(image_filename)); } + if (image_file == nullptr) { LOG(ERROR) << "Failed to open image file " << image_filename; return false; } - if (fchmod(image_file->Fd(), 0644) != 0) { + + if (!compile_app_image_ && fchmod(image_file->Fd(), 0644) != 0) { PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; image_file->Erase(); return EXIT_FAILURE; @@ -701,6 +742,7 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal( std::unordered_set<mirror::Class*>* visited) { DCHECK(early_exit != nullptr); DCHECK(visited != nullptr); + DCHECK(compile_app_image_); if (klass == nullptr) { return false; } @@ -717,6 +759,13 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal( visited->emplace(klass); bool result = IsBootClassLoaderNonImageClass(klass); bool my_early_exit = false; // Only for ourselves, ignore caller. + // Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the + // app image. + if (klass->GetStatus() == mirror::Class::kStatusError) { + result = true; + } else { + CHECK(klass->GetVerifyError() == nullptr) << PrettyClass(klass); + } if (!result) { // Check interfaces since these wont be visited through VisitReferences.) mirror::IfTable* if_table = klass->GetIfTable(); @@ -727,6 +776,12 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal( visited); } } + if (klass->IsObjectArrayClass()) { + result = result || ContainsBootClassLoaderNonImageClassInternal( + klass->GetComponentType(), + &my_early_exit, + visited); + } // Check static fields and their classes. size_t num_static_fields = klass->NumReferenceStaticFields(); if (num_static_fields != 0 && klass->IsResolved()) { @@ -780,7 +835,9 @@ bool ImageWriter::KeepClass(Class* klass) { if (compile_app_image_) { // For app images, we need to prune boot loader classes that are not in the boot image since // these may have already been loaded when the app image is loaded. - return !ContainsBootClassLoaderNonImageClass(klass); + // Keep classes in the boot image space since we don't want to re-resolve these. + return Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass) || + !ContainsBootClassLoaderNonImageClass(klass); } std::string temp; return compiler_driver_.IsImageClass(klass->GetDescriptor(&temp)); @@ -843,25 +900,25 @@ void ImageWriter::PruneNonImageClasses() { for (size_t i = 0, num = dex_cache->NumResolvedMethods(); i != num; ++i) { ArtMethod* method = mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_); - if (method != nullptr) { - auto* declaring_class = method->GetDeclaringClass(); - // Miranda methods may be held live by a class which was not an image class but have a - // declaring class which is an image class. Set it to the resolution method to be safe and - // prevent dangling pointers. - if (method->IsMiranda() || !KeepClass(declaring_class)) { - mirror::DexCache::SetElementPtrSize(resolved_methods, - i, - resolution_method, - target_ptr_size_); - } else { - // Check that the class is still in the classes table. - DCHECK(class_linker->ClassInClassTable(declaring_class)) << "Class " - << PrettyClass(declaring_class) << " not in class linker table"; - } + DCHECK(method != nullptr) << "Expected resolution method instead of null method"; + mirror::Class* declaring_class = method->GetDeclaringClass(); + // Miranda methods may be held live by a class which was not an image class but have a + // declaring class which is an image class. Set it to the resolution method to be safe and + // prevent dangling pointers. + if (method->IsMiranda() || !KeepClass(declaring_class)) { + mirror::DexCache::SetElementPtrSize(resolved_methods, + i, + resolution_method, + target_ptr_size_); + } else { + // Check that the class is still in the classes table. + DCHECK(class_linker->ClassInClassTable(declaring_class)) << "Class " + << PrettyClass(declaring_class) << " not in class linker table"; } } + ArtField** resolved_fields = dex_cache->GetResolvedFields(); for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) { - ArtField* field = dex_cache->GetResolvedField(i, target_ptr_size_); + ArtField* field = mirror::DexCache::GetElementPtrSize(resolved_fields, i, target_ptr_size_); if (field != nullptr && !KeepClass(field->GetDeclaringClass())) { dex_cache->SetResolvedField(i, nullptr, target_ptr_size_); } @@ -906,6 +963,32 @@ 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; + mirror::String* const found = image_info.intern_table_->LookupStrong(self, string); + DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr) + << string->ToModifiedUtf8(); + if (found != nullptr) { + return found; + } + } + if (compile_app_image_) { + Runtime* const runtime = Runtime::Current(); + mirror::String* found = runtime->GetInternTable()->LookupStrong(self, string); + // If we found it in the runtime intern table it could either be in the boot image or interned + // during app image compilation. If it was in the boot image return that, otherwise return null + // since it belongs to another image space. + if (found != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(found)) { + return found; + } + DCHECK(runtime->GetInternTable()->LookupWeak(self, string) == nullptr) + << string->ToModifiedUtf8(); + } + return nullptr; +} + void ImageWriter::CalculateObjectBinSlots(Object* obj) { DCHECK(obj != nullptr); // if it is a string, we want to intern it if its not interned. @@ -915,13 +998,16 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) { // we must be an interned string that was forward referenced and already assigned if (IsImageBinSlotAssigned(obj)) { - DCHECK_EQ(obj, image_info.intern_table_->InternStrongImageString(obj->AsString())); + DCHECK_EQ(obj, FindInternedString(obj->AsString())); return; } - // InternImageString allows us to intern while holding the heap bitmap lock. This is safe since - // we are guaranteed to not have GC during image writing. - mirror::String* const interned = image_info.intern_table_->InternStrongImageString( - obj->AsString()); + // Need to check if the string is already interned in another image info so that we don't have + // the intern tables of two different images contain the same string. + mirror::String* interned = FindInternedString(obj->AsString()); + if (interned == nullptr) { + // Not in another image space, insert to our table. + interned = image_info.intern_table_->InternStrongImageString(obj->AsString()); + } if (obj != interned) { if (!IsImageBinSlotAssigned(interned)) { // interned obj is after us, allocate its location early @@ -1066,6 +1152,11 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { // Visit and assign offsets for fields and field arrays. auto* as_klass = h_obj->AsClass(); mirror::DexCache* dex_cache = as_klass->GetDexCache(); + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + if (compile_app_image_) { + // Extra sanity, no boot loader classes should be left! + CHECK(!IsBootClassLoaderClass(as_klass)) << PrettyClass(as_klass); + } LengthPrefixedArray<ArtField>* fields[] = { as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(), }; @@ -1405,6 +1496,13 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { << " Oat data end=" << reinterpret_cast<uintptr_t>(oat_data_end) << " Oat file end=" << reinterpret_cast<uintptr_t>(oat_file_end); } + // Store boot image info for app image so that we can relocate. + uint32_t boot_image_begin = 0; + uint32_t boot_image_end = 0; + uint32_t boot_oat_begin = 0; + uint32_t boot_oat_end = 0; + gc::Heap* const heap = Runtime::Current()->GetHeap(); + heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end); // Create the header, leave 0 for data size since we will fill this in as we are writing the // image. @@ -1417,8 +1515,13 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { PointerToLowMemUInt32(image_info.oat_data_begin_), PointerToLowMemUInt32(oat_data_end), PointerToLowMemUInt32(oat_file_end), + boot_image_begin, + boot_image_end - boot_image_begin, + boot_oat_begin, + boot_oat_end - boot_oat_begin, target_ptr_size_, compile_pic_, + /*is_pic*/compile_app_image_, image_storage_mode_, /*data_size*/0u); } @@ -1805,13 +1908,14 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) { FixupDexCache(down_cast<mirror::DexCache*>(orig), down_cast<mirror::DexCache*>(copy)); } else if (klass->IsClassLoaderClass()) { + mirror::ClassLoader* copy_loader = down_cast<mirror::ClassLoader*>(copy); // If src is a ClassLoader, set the class table to null so that it gets recreated by the // ClassLoader. - down_cast<mirror::ClassLoader*>(copy)->SetClassTable(nullptr); + copy_loader->SetClassTable(nullptr); // Also set allocator to null to be safe. The allocator is created when we create the class // table. We also never expect to unload things in the image since they are held live as // roots. - down_cast<mirror::ClassLoader*>(copy)->SetAllocator(nullptr); + copy_loader->SetAllocator(nullptr); } } FixupVisitor visitor(this, copy); @@ -1896,7 +2000,7 @@ const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { // If we are compiling an app image, we need to use the stubs of the boot image. if (compile_app_image_) { // Use the current image pointers. - std::vector<gc::space::ImageSpace*> image_spaces = + const std::vector<gc::space::ImageSpace*>& image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); DCHECK(!image_spaces.empty()); const OatFile* oat_file = image_spaces[0]->GetOatFile(); diff --git a/compiler/image_writer.h b/compiler/image_writer.h index ad690389e9..622eb1985b 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -49,7 +49,7 @@ class ImageSpace; class ClassTable; -static constexpr int kInvalidImageFd = -1; +static constexpr int kInvalidFd = -1; // Write a Space built during compilation for use during execution. class ImageWriter FINAL { @@ -103,11 +103,15 @@ class ImageWriter FINAL { uint8_t* GetOatFileBegin(const char* oat_filename) const; - // If image_fd is not kInvalidImageFd, then we use that for the file. Otherwise we open + // If image_fd is not kInvalidFd, then we use that for the image file. Otherwise we open // the names in image_filenames. + // If oat_fd is not kInvalidFd, then we use that for the oat file. Otherwise we open + // the names in oat_filenames. bool Write(int image_fd, const std::vector<const char*>& image_filenames, - const std::vector<const char*>& oat_filenames) + int oat_fd, + const std::vector<const char*>& oat_filenames, + const std::string& oat_location) REQUIRES(!Locks::mutator_lock_); uintptr_t GetOatDataBegin(const char* oat_filename) { @@ -447,6 +451,10 @@ class ImageWriter FINAL { const ImageInfo& GetConstImageInfo(const char* oat_filename) const; const ImageInfo& GetImageInfo(size_t index) const; + // 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. + mirror::String* FindInternedString(mirror::String* string) SHARED_REQUIRES(Locks::mutator_lock_); + const CompilerDriver& compiler_driver_; // Beginning target image address for the first image. diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 52a238233b..e92046057c 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -95,7 +95,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, // Assembler that holds generated instructions std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set, instruction_set_features)); - jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo()); + jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GenerateAnyDebugInfo()); // Offsets into data structures // TODO: if cross compiling these offsets are for the host not the target diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 7a2b74ed88..c0d15f3439 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -38,6 +38,7 @@ #include "oat_file-inl.h" #include "oat_writer.h" #include "scoped_thread_state_change.h" +#include "utils/test_dex_file_builder.h" namespace art { @@ -127,23 +128,74 @@ class OatTest : public CommonCompilerTest { const std::vector<const DexFile*>& dex_files, SafeMap<std::string, std::string>& key_value_store) { TimingLogger timings("WriteElf", false, false); - OatWriter oat_writer(dex_files, - 42U, - 4096U, - 0, - compiler_driver_.get(), - nullptr, - /*compiling_boot_image*/false, - &timings, - &key_value_store); + OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + for (const DexFile* dex_file : dex_files) { + ArrayRef<const uint8_t> raw_dex_file( + reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), + dex_file->GetHeader().file_size_); + if (!oat_writer.AddRawDexFileSource(raw_dex_file, + dex_file->GetLocation().c_str(), + dex_file->GetLocationChecksum())) { + return false; + } + } + return DoWriteElf(file, oat_writer, key_value_store); + } + + bool WriteElf(File* file, + const std::vector<const char*>& dex_filenames, + SafeMap<std::string, std::string>& key_value_store) { + TimingLogger timings("WriteElf", false, false); + OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + for (const char* dex_filename : dex_filenames) { + if (!oat_writer.AddDexFileSource(dex_filename, dex_filename)) { + return false; + } + } + return DoWriteElf(file, oat_writer, key_value_store); + } + + bool WriteElf(File* file, + ScopedFd&& zip_fd, + const char* location, + SafeMap<std::string, std::string>& key_value_store) { + TimingLogger timings("WriteElf", false, false); + OatWriter oat_writer(/*compiling_boot_image*/false, &timings); + if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) { + return false; + } + return DoWriteElf(file, oat_writer, key_value_store); + } + + bool DoWriteElf(File* file, + OatWriter& oat_writer, + SafeMap<std::string, std::string>& key_value_store) { std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick( compiler_driver_->GetInstructionSet(), &compiler_driver_->GetCompilerOptions(), file); - elf_writer->Start(); - OutputStream* rodata = elf_writer->StartRoData(); + std::unique_ptr<MemMap> opened_dex_files_map; + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + if (!oat_writer.WriteAndOpenDexFiles(rodata, + file, + compiler_driver_->GetInstructionSet(), + compiler_driver_->GetInstructionSetFeatures(), + &key_value_store, + &opened_dex_files_map, + &opened_dex_files)) { + return false; + } + Runtime* runtime = Runtime::Current(); + ClassLinker* const class_linker = runtime->GetClassLinker(); + std::vector<const DexFile*> dex_files; + for (const std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { + dex_files.push_back(dex_file.get()); + ScopedObjectAccess soa(Thread::Current()); + class_linker->RegisterDexFile(*dex_file, runtime->GetLinearAlloc()); + } + oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files); if (!oat_writer.WriteRodata(rodata)) { return false; } @@ -155,6 +207,10 @@ class OatTest : public CommonCompilerTest { } elf_writer->EndText(text); + if (!oat_writer.WriteHeader(elf_writer->GetStream(), 42U, 4096U, 0)) { + return false; + } + elf_writer->SetBssSize(oat_writer.GetBssSize()); elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo()); @@ -167,6 +223,117 @@ class OatTest : public CommonCompilerTest { std::unique_ptr<QuickCompilerCallbacks> callbacks_; }; +class ZipBuilder { + public: + explicit ZipBuilder(File* zip_file) : zip_file_(zip_file) { } + + bool AddFile(const char* location, const void* data, size_t size) { + off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR); + if (offset == static_cast<off_t>(-1)) { + return false; + } + + ZipFileHeader file_header; + file_header.crc32 = crc32(0u, reinterpret_cast<const Bytef*>(data), size); + file_header.compressed_size = size; + file_header.uncompressed_size = size; + file_header.filename_length = strlen(location); + + if (!zip_file_->WriteFully(&file_header, sizeof(file_header)) || + !zip_file_->WriteFully(location, file_header.filename_length) || + !zip_file_->WriteFully(data, size)) { + return false; + } + + CentralDirectoryFileHeader cdfh; + cdfh.crc32 = file_header.crc32; + cdfh.compressed_size = size; + cdfh.uncompressed_size = size; + cdfh.filename_length = file_header.filename_length; + cdfh.relative_offset_of_local_file_header = offset; + file_data_.push_back(FileData { cdfh, location }); + return true; + } + + bool Finish() { + off_t offset = lseek(zip_file_->Fd(), 0, SEEK_CUR); + if (offset == static_cast<off_t>(-1)) { + return false; + } + + size_t central_directory_size = 0u; + for (const FileData& file_data : file_data_) { + if (!zip_file_->WriteFully(&file_data.cdfh, sizeof(file_data.cdfh)) || + !zip_file_->WriteFully(file_data.location, file_data.cdfh.filename_length)) { + return false; + } + central_directory_size += sizeof(file_data.cdfh) + file_data.cdfh.filename_length; + } + EndOfCentralDirectoryRecord eocd_record; + eocd_record.number_of_central_directory_records_on_this_disk = file_data_.size(); + eocd_record.total_number_of_central_directory_records = file_data_.size(); + eocd_record.size_of_central_directory = central_directory_size; + eocd_record.offset_of_start_of_central_directory = offset; + return + zip_file_->WriteFully(&eocd_record, sizeof(eocd_record)) && + zip_file_->Flush() == 0; + } + + private: + struct PACKED(1) ZipFileHeader { + uint32_t signature = 0x04034b50; + uint16_t version_needed_to_extract = 10; + uint16_t general_purpose_bit_flag = 0; + uint16_t compression_method = 0; // 0 = store only. + uint16_t file_last_modification_time = 0u; + uint16_t file_last_modification_date = 0u; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_length; + uint16_t extra_field_length = 0u; // No extra fields. + }; + + struct PACKED(1) CentralDirectoryFileHeader { + uint32_t signature = 0x02014b50; + uint16_t version_made_by = 10; + uint16_t version_needed_to_extract = 10; + uint16_t general_purpose_bit_flag = 0; + uint16_t compression_method = 0; // 0 = store only. + uint16_t file_last_modification_time = 0u; + uint16_t file_last_modification_date = 0u; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_length; + uint16_t extra_field_length = 0u; // No extra fields. + uint16_t file_comment_length = 0u; // No file comment. + uint16_t disk_number_where_file_starts = 0u; + uint16_t internal_file_attributes = 0u; + uint32_t external_file_attributes = 0u; + uint32_t relative_offset_of_local_file_header; + }; + + struct PACKED(1) EndOfCentralDirectoryRecord { + uint32_t signature = 0x06054b50; + uint16_t number_of_this_disk = 0u; + uint16_t disk_where_central_directory_starts = 0u; + uint16_t number_of_central_directory_records_on_this_disk; + uint16_t total_number_of_central_directory_records; + uint32_t size_of_central_directory; + uint32_t offset_of_start_of_central_directory; + uint16_t comment_length = 0u; // No file comment. + }; + + struct FileData { + CentralDirectoryFileHeader cdfh; + const char* location; + }; + + File* zip_file_; + std::vector<FileData> file_data_; +}; + TEST_F(OatTest, WriteRead) { TimingLogger timings("OatTest::WriteRead", false, false); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -327,4 +494,189 @@ TEST_F(OatTest, EmptyTextSection) { EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength())); } +TEST_F(OatTest, DexFileInput) { + TimingLogger timings("OatTest::DexFileInput", false, false); + + std::vector<const char*> input_filenames; + + ScratchFile dex_file1; + TestDexFileBuilder builder1; + builder1.AddField("Lsome.TestClass;", "int", "someField"); + builder1.AddMethod("Lsome.TestClass;", "()I", "foo"); + std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename()); + bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(), + dex_file1_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file1.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + input_filenames.push_back(dex_file1.GetFilename().c_str()); + + ScratchFile dex_file2; + TestDexFileBuilder builder2; + builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField"); + builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar"); + std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename()); + success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(), + dex_file2_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file2.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + input_filenames.push_back(dex_file2.GetFilename().c_str()); + + ScratchFile oat_file; + SafeMap<std::string, std::string> key_value_store; + key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); + success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store); + ASSERT_TRUE(success); + + std::string error_msg; + std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), + oat_file.GetFilename(), + nullptr, + nullptr, + false, + nullptr, + &error_msg)); + ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); + std::unique_ptr<const DexFile> opened_dex_file1 = + opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); + std::unique_ptr<const DexFile> opened_dex_file2 = + opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), + &opened_dex_file1->GetHeader(), + dex_file1_data->GetHeader().file_size_)); + ASSERT_EQ(dex_file1_data->GetLocation(), opened_dex_file1->GetLocation()); + + ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), + &opened_dex_file2->GetHeader(), + dex_file2_data->GetHeader().file_size_)); + ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); +} + +TEST_F(OatTest, ZipFileInput) { + TimingLogger timings("OatTest::DexFileInput", false, false); + + ScratchFile zip_file; + ZipBuilder zip_builder(zip_file.GetFile()); + + ScratchFile dex_file1; + TestDexFileBuilder builder1; + builder1.AddField("Lsome.TestClass;", "long", "someField"); + builder1.AddMethod("Lsome.TestClass;", "()D", "foo"); + std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename()); + bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(), + dex_file1_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file1.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + success = zip_builder.AddFile("classes.dex", + &dex_file1_data->GetHeader(), + dex_file1_data->GetHeader().file_size_); + ASSERT_TRUE(success); + + ScratchFile dex_file2; + TestDexFileBuilder builder2; + builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField"); + builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar"); + std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename()); + success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(), + dex_file2_data->GetHeader().file_size_); + ASSERT_TRUE(success); + success = dex_file2.GetFile()->Flush() == 0; + ASSERT_TRUE(success); + success = zip_builder.AddFile("classes2.dex", + &dex_file2_data->GetHeader(), + dex_file2_data->GetHeader().file_size_); + ASSERT_TRUE(success); + + success = zip_builder.Finish(); + ASSERT_TRUE(success) << strerror(errno); + + SafeMap<std::string, std::string> key_value_store; + key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); + { + // Test using the AddDexFileSource() interface with the zip file. + std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + + ScratchFile oat_file; + success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store); + ASSERT_TRUE(success); + + std::string error_msg; + std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), + oat_file.GetFilename(), + nullptr, + nullptr, + false, + nullptr, + &error_msg)); + ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); + std::unique_ptr<const DexFile> opened_dex_file1 = + opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); + std::unique_ptr<const DexFile> opened_dex_file2 = + opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), + &opened_dex_file1->GetHeader(), + dex_file1_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()), + opened_dex_file1->GetLocation()); + + ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), + &opened_dex_file2->GetHeader(), + dex_file2_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()), + opened_dex_file2->GetLocation()); + } + + { + // Test using the AddZipDexFileSource() interface with the zip file handle. + ScopedFd zip_fd(dup(zip_file.GetFd())); + ASSERT_NE(-1, zip_fd.get()); + + ScratchFile oat_file; + success = WriteElf(oat_file.GetFile(), + std::move(zip_fd), + zip_file.GetFilename().c_str(), + key_value_store); + ASSERT_TRUE(success); + + std::string error_msg; + std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(), + oat_file.GetFilename(), + nullptr, + nullptr, + false, + nullptr, + &error_msg)); + ASSERT_TRUE(opened_oat_file != nullptr); + ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size()); + std::unique_ptr<const DexFile> opened_dex_file1 = + opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg); + std::unique_ptr<const DexFile> opened_dex_file2 = + opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), + &opened_dex_file1->GetHeader(), + dex_file1_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()), + opened_dex_file1->GetLocation()); + + ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_); + ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(), + &opened_dex_file2->GetHeader(), + dex_file2_data->GetHeader().file_size_)); + ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()), + opened_dex_file2->GetLocation()); + } +} + } // namespace art diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 025e35e178..569e0f4e19 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -16,12 +16,14 @@ #include "oat_writer.h" +#include <unistd.h> #include <zlib.h> #include "arch/arm64/instruction_set_features_arm64.h" #include "art_method-inl.h" #include "base/allocator.h" #include "base/bit_vector.h" +#include "base/file_magic.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -49,9 +51,77 @@ #include "type_lookup_table.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "verifier/method_verifier.h" +#include "zip_archive.h" namespace art { +namespace { // anonymous namespace + +typedef DexFile::Header __attribute__((aligned(1))) UnalignedDexFileHeader; + +const UnalignedDexFileHeader* AsUnalignedDexFileHeader(const uint8_t* raw_data) { + return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data); +} + +} // anonymous namespace + +// Defines the location of the raw dex file to write. +class OatWriter::DexFileSource { + public: + explicit DexFileSource(ZipEntry* zip_entry) + : type_(kZipEntry), source_(zip_entry) { + DCHECK(source_ != nullptr); + } + + explicit DexFileSource(File* raw_file) + : type_(kRawFile), source_(raw_file) { + DCHECK(source_ != nullptr); + } + + explicit DexFileSource(const uint8_t* dex_file) + : type_(kRawData), source_(dex_file) { + DCHECK(source_ != nullptr); + } + + bool IsZipEntry() const { return type_ == kZipEntry; } + bool IsRawFile() const { return type_ == kRawFile; } + bool IsRawData() const { return type_ == kRawData; } + + ZipEntry* GetZipEntry() const { + DCHECK(IsZipEntry()); + DCHECK(source_ != nullptr); + return static_cast<ZipEntry*>(const_cast<void*>(source_)); + } + + File* GetRawFile() const { + DCHECK(IsRawFile()); + DCHECK(source_ != nullptr); + return static_cast<File*>(const_cast<void*>(source_)); + } + + const uint8_t* GetRawData() const { + DCHECK(IsRawData()); + DCHECK(source_ != nullptr); + return static_cast<const uint8_t*>(source_); + } + + void Clear() { + type_ = kNone; + source_ = nullptr; + } + + private: + enum Type { + kNone, + kZipEntry, + kRawFile, + kRawData, + }; + + Type type_; + const void* source_; +}; + class OatWriter::OatClass { public: OatClass(size_t offset, @@ -116,11 +186,30 @@ class OatWriter::OatClass { class OatWriter::OatDexFile { public: - OatDexFile(size_t offset, const DexFile& dex_file); + OatDexFile(const char* dex_file_location, + DexFileSource source, + CreateTypeLookupTable create_type_lookup_table); OatDexFile(OatDexFile&& src) = default; + const char* GetLocation() const { + return dex_file_location_data_; + } + + void ReserveTypeLookupTable(OatWriter* oat_writer); + void ReserveClassOffsets(OatWriter* oat_writer); + size_t SizeOf() const; - bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const; + bool Write(OatWriter* oat_writer, OutputStream* out) const; + bool WriteClassOffsets(OatWriter* oat_writer, OutputStream* out); + + // The source of the dex file. + DexFileSource source_; + + // Whether to create the type lookup table. + CreateTypeLookupTable create_type_lookup_table_; + + // Dex file size. Initialized when writing the dex file. + size_t dex_file_size_; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. @@ -128,11 +217,13 @@ class OatWriter::OatDexFile { // Data to write. uint32_t dex_file_location_size_; - const uint8_t* dex_file_location_data_; + const char* dex_file_location_data_; uint32_t dex_file_location_checksum_; uint32_t dex_file_offset_; + uint32_t class_offsets_offset_; uint32_t lookup_table_offset_; - TypeLookupTable* lookup_table_; // Owned by the dex file. + + // Data to write to a separate section. dchecked_vector<uint32_t> class_offsets_; private: @@ -151,26 +242,20 @@ class OatWriter::OatDexFile { DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \ << "file_offset=" << file_offset << " offset_=" << offset_ -OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, - uint32_t image_file_location_oat_checksum, - uintptr_t image_file_location_oat_begin, - int32_t image_patch_delta, - const CompilerDriver* compiler, - ImageWriter* image_writer, - bool compiling_boot_image, - TimingLogger* timings, - SafeMap<std::string, std::string>* key_value_store) - : compiler_driver_(compiler), - image_writer_(image_writer), +OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings) + : write_state_(WriteState::kAddingDexFileSources), + timings_(timings), + raw_dex_files_(), + zip_archives_(), + zipped_dex_files_(), + zipped_dex_file_locations_(), + compiler_driver_(nullptr), + image_writer_(nullptr), compiling_boot_image_(compiling_boot_image), - dex_files_(&dex_files), + dex_files_(nullptr), size_(0u), bss_size_(0u), oat_data_offset_(0u), - image_file_location_oat_checksum_(image_file_location_oat_checksum), - image_file_location_oat_begin_(image_file_location_oat_begin), - image_patch_delta_(image_patch_delta), - key_value_store_(key_value_store), oat_header_(nullptr), size_dex_file_alignment_(0), size_executable_offset_alignment_(0), @@ -197,55 +282,192 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_oat_dex_file_location_data_(0), size_oat_dex_file_location_checksum_(0), size_oat_dex_file_offset_(0), + size_oat_dex_file_class_offsets_offset_(0), size_oat_dex_file_lookup_table_offset_(0), - size_oat_dex_file_class_offsets_(0), size_oat_lookup_table_alignment_(0), size_oat_lookup_table_(0), + size_oat_class_offsets_alignment_(0), + size_oat_class_offsets_(0), size_oat_class_type_(0), size_oat_class_status_(0), size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), method_offset_map_() { - CHECK(key_value_store != nullptr); - if (compiling_boot_image) { - CHECK(image_writer != nullptr); +} + +bool OatWriter::AddDexFileSource(const char* filename, + const char* location, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + uint32_t magic; + std::string error_msg; + ScopedFd fd(OpenAndReadMagic(filename, &magic, &error_msg)); + if (fd.get() == -1) { + PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'"; + return false; + } else if (IsDexMagic(magic)) { + // The file is open for reading, not writing, so it's OK to let the File destructor + // close it without checking for explicit Close(), so pass checkUsage = false. + raw_dex_files_.emplace_back(new File(fd.release(), location, /* checkUsage */ false)); + oat_dex_files_.emplace_back(location, + DexFileSource(raw_dex_files_.back().get()), + create_type_lookup_table); + } else if (IsZipMagic(magic)) { + if (!AddZippedDexFilesSource(std::move(fd), location, create_type_lookup_table)) { + return false; + } + } else { + LOG(ERROR) << "Expected valid zip or dex file: '" << filename << "'"; + return false; } - InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); - const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures(); - relative_patcher_ = linker::RelativePatcher::Create(instruction_set, features, - &method_offset_map_); + return true; +} - size_t offset; - { - TimingLogger::ScopedTiming split("InitOatHeader", timings); - offset = InitOatHeader(); +// Add dex file source(s) from a zip file specified by a file handle. +bool OatWriter::AddZippedDexFilesSource(ScopedFd&& zip_fd, + const char* location, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + std::string error_msg; + zip_archives_.emplace_back(ZipArchive::OpenFromFd(zip_fd.release(), location, &error_msg)); + ZipArchive* zip_archive = zip_archives_.back().get(); + if (zip_archive == nullptr) { + LOG(ERROR) << "Failed to open zip from file descriptor for '" << location << "': " + << error_msg; + return false; } - { - TimingLogger::ScopedTiming split("InitOatDexFiles", timings); - offset = InitOatDexFiles(offset); + for (size_t i = 0; ; ++i) { + std::string entry_name = DexFile::GetMultiDexClassesDexName(i); + std::unique_ptr<ZipEntry> entry(zip_archive->Find(entry_name.c_str(), &error_msg)); + if (entry == nullptr) { + break; + } + zipped_dex_files_.push_back(std::move(entry)); + zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location)); + const char* full_location = zipped_dex_file_locations_.back().c_str(); + oat_dex_files_.emplace_back(full_location, + DexFileSource(zipped_dex_files_.back().get()), + create_type_lookup_table); + } + if (zipped_dex_file_locations_.empty()) { + LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; + return false; } - { - TimingLogger::ScopedTiming split("InitDexFiles", timings); - offset = InitDexFiles(offset); + return true; +} + +// Add dex file source from raw memory. +bool OatWriter::AddRawDexFileSource(const ArrayRef<const uint8_t>& data, + const char* location, + uint32_t location_checksum, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + if (data.size() < sizeof(DexFile::Header)) { + LOG(ERROR) << "Provided data is shorter than dex file header. size: " + << data.size() << " File: " << location; + return false; } - { - TimingLogger::ScopedTiming split("InitLookupTables", timings); - offset = InitLookupTables(offset); + if (!ValidateDexFileHeader(data.data(), location)) { + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(data.data()); + if (data.size() < header->file_size_) { + LOG(ERROR) << "Truncated dex file data. Data size: " << data.size() + << " file size from header: " << header->file_size_ << " File: " << location; + return false; } + + oat_dex_files_.emplace_back(location, DexFileSource(data.data()), create_type_lookup_table); + oat_dex_files_.back().dex_file_location_checksum_ = location_checksum; + return true; +} + +dchecked_vector<const char*> OatWriter::GetSourceLocations() const { + dchecked_vector<const char*> locations; + locations.reserve(oat_dex_files_.size()); + for (const OatDexFile& oat_dex_file : oat_dex_files_) { + locations.push_back(oat_dex_file.GetLocation()); + } + return locations; +} + +bool OatWriter::WriteAndOpenDexFiles( + OutputStream* rodata, + File* file, + InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + SafeMap<std::string, std::string>* key_value_store, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { + CHECK(write_state_ == WriteState::kAddingDexFileSources); + + size_t offset = InitOatHeader(instruction_set, + instruction_set_features, + dchecked_integral_cast<uint32_t>(oat_dex_files_.size()), + key_value_store); + offset = InitOatDexFiles(offset); + size_ = offset; + + std::unique_ptr<MemMap> dex_files_map; + std::vector<std::unique_ptr<const DexFile>> dex_files; + if (!WriteDexFiles(rodata, file)) { + return false; + } + // Reserve space for type lookup tables and update type_lookup_table_offset_. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + oat_dex_file.ReserveTypeLookupTable(this); + } + size_t size_after_type_lookup_tables = size_; + // Reserve space for class offsets and update class_offsets_offset_. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + oat_dex_file.ReserveClassOffsets(this); + } + if (!WriteOatDexFiles(rodata) || + !ExtendForTypeLookupTables(rodata, file, size_after_type_lookup_tables) || + !OpenDexFiles(file, &dex_files_map, &dex_files) || + !WriteTypeLookupTables(dex_files_map.get(), dex_files)) { + return false; + } + + *opened_dex_files_map = std::move(dex_files_map); + *opened_dex_files = std::move(dex_files); + write_state_ = WriteState::kPrepareLayout; + return true; +} + +void OatWriter::PrepareLayout(const CompilerDriver* compiler, + ImageWriter* image_writer, + const std::vector<const DexFile*>& dex_files) { + CHECK(write_state_ == WriteState::kPrepareLayout); + + dex_files_ = &dex_files; + + compiler_driver_ = compiler; + image_writer_ = image_writer; + 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_; { - TimingLogger::ScopedTiming split("InitOatClasses", timings); + TimingLogger::ScopedTiming split("InitOatClasses", timings_); offset = InitOatClasses(offset); } { - TimingLogger::ScopedTiming split("InitOatMaps", timings); + TimingLogger::ScopedTiming split("InitOatMaps", timings_); offset = InitOatMaps(offset); } { - TimingLogger::ScopedTiming split("InitOatCode", timings); + TimingLogger::ScopedTiming split("InitOatCode", timings_); offset = InitOatCode(offset); } { - TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings); + TimingLogger::ScopedTiming split("InitOatCodeDexFiles", timings_); offset = InitOatCodeDexFiles(offset); } size_ = offset; @@ -255,7 +477,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_t bss_start = RoundUp(size_, kPageSize); size_t pointer_size = GetInstructionSetPointerSize(instruction_set); bss_size_ = 0u; - for (const DexFile* dex_file : dex_files) { + for (const DexFile* dex_file : *dex_files_) { dex_cache_arrays_offsets_.Put(dex_file, bss_start + bss_size_); DexCacheArraysLayout layout(pointer_size, dex_file); bss_size_ += layout.Size(); @@ -265,9 +487,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); if (compiling_boot_image_) { CHECK_EQ(image_writer_ != nullptr, - key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end()); + oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr); } - CHECK_ALIGNED(image_patch_delta_, kPageSize); + + write_state_ = WriteState::kWriteRoData; } OatWriter::~OatWriter() { @@ -502,8 +725,18 @@ 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_) { - quick_code_offset = NewQuickCodeOffset(compiled_method, it, thumb_offset); + if (method_lb != writer_->method_offset_map_.map.end() && + !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) { + // 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); + } } else { auto lb = dedupe_map_.lower_bound(compiled_method); if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(compiled_method, lb->first)) { @@ -516,14 +749,12 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } if (code_size != 0) { - MethodReference method_ref(dex_file_, it.GetMemberIndex()); - auto method_lb = writer_->method_offset_map_.map.lower_bound(method_ref); if (method_lb != writer_->method_offset_map_.map.end() && !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) { // TODO: Should this be a hard failure? LOG(WARNING) << "Multiple definitions of " << PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file) - << ((method_lb->second != quick_code_offset) ? "; OFFSET MISMATCH" : ""); + << " offsets " << method_lb->second << " " << quick_code_offset; } else { writer_->method_offset_map_.map.PutBefore(method_lb, method_ref, quick_code_offset); } @@ -575,7 +806,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } } - if (writer_->compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) { + if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) { // 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; @@ -735,30 +966,40 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { } ClassLinker* linker = Runtime::Current()->GetClassLinker(); - InvokeType invoke_type = it.GetMethodInvokeType(dex_file_->GetClassDef(class_def_index_)); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache( Thread::Current(), *dex_file_))); - ArtMethod* method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( - *dex_file_, - it.GetMemberIndex(), - dex_cache, - ScopedNullHandle<mirror::ClassLoader>(), - nullptr, - invoke_type); - if (method == nullptr) { - LOG(INTERNAL_FATAL) << "Unexpected failure to resolve a method: " - << PrettyMethod(it.GetMemberIndex(), *dex_file_, true); - soa.Self()->AssertPendingException(); - mirror::Throwable* exc = soa.Self()->GetException(); - std::string dump = exc->Dump(); - LOG(FATAL) << dump; - UNREACHABLE(); + ArtMethod* method; + if (writer_->HasBootImage()) { + const InvokeType invoke_type = it.GetMethodInvokeType( + dex_file_->GetClassDef(class_def_index_)); + method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + *dex_file_, + it.GetMemberIndex(), + dex_cache, + ScopedNullHandle<mirror::ClassLoader>(), + nullptr, + invoke_type); + if (method == nullptr) { + LOG(INTERNAL_FATAL) << "Unexpected failure to resolve a method: " + << PrettyMethod(it.GetMemberIndex(), *dex_file_, true); + soa.Self()->AssertPendingException(); + mirror::Throwable* exc = soa.Self()->GetException(); + std::string dump = exc->Dump(); + LOG(FATAL) << dump; + UNREACHABLE(); + } + } else { + // Should already have been resolved by the compiler, just peek into the dex cache. + // It may not be resolved if the class failed to verify, in this case, don't set the + // entrypoint. This is not fatal since the dex cache will contain a resolution method. + method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), linker->GetImagePointerSize()); } - - if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0) { + if (method != nullptr && + compiled_method != nullptr && + compiled_method->GetQuickCode().size() != 0) { method->SetEntryPointFromQuickCompiledCodePtrSize( reinterpret_cast<void*>(offsets.code_offset_), pointer_size_); } @@ -1134,59 +1375,26 @@ bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { return true; } -size_t OatWriter::InitOatHeader() { - oat_header_.reset(OatHeader::Create(compiler_driver_->GetInstructionSet(), - compiler_driver_->GetInstructionSetFeatures(), - dchecked_integral_cast<uint32_t>(dex_files_->size()), - key_value_store_)); - oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum_); - oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin_); - +size_t OatWriter::InitOatHeader(InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + uint32_t num_dex_files, + SafeMap<std::string, std::string>* key_value_store) { + TimingLogger::ScopedTiming split("InitOatHeader", timings_); + oat_header_.reset(OatHeader::Create(instruction_set, + instruction_set_features, + num_dex_files, + key_value_store)); + size_oat_header_ += sizeof(OatHeader); + size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader); return oat_header_->GetHeaderSize(); } size_t OatWriter::InitOatDexFiles(size_t offset) { - // create the OatDexFiles - for (size_t i = 0; i != dex_files_->size(); ++i) { - const DexFile* dex_file = (*dex_files_)[i]; - CHECK(dex_file != nullptr); - oat_dex_files_.emplace_back(offset, *dex_file); - offset += oat_dex_files_.back().SizeOf(); - } - return offset; -} - -size_t OatWriter::InitDexFiles(size_t offset) { - // calculate the offsets within OatDexFiles to the DexFiles - for (size_t i = 0; i != dex_files_->size(); ++i) { - // dex files are required to be 4 byte aligned - size_t original_offset = offset; - offset = RoundUp(offset, 4); - size_dex_file_alignment_ += offset - original_offset; - - // set offset in OatDexFile to DexFile - oat_dex_files_[i].dex_file_offset_ = offset; - - const DexFile* dex_file = (*dex_files_)[i]; - - // Initialize type lookup table - oat_dex_files_[i].lookup_table_ = dex_file->GetTypeLookupTable(); - - offset += dex_file->GetHeader().file_size_; - } - return offset; -} - -size_t OatWriter::InitLookupTables(size_t offset) { + TimingLogger::ScopedTiming split("InitOatDexFiles", timings_); + // Initialize offsets of dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { - if (oat_dex_file.lookup_table_ != nullptr) { - uint32_t aligned_offset = RoundUp(offset, 4); - oat_dex_file.lookup_table_offset_ = aligned_offset; - size_oat_lookup_table_alignment_ += aligned_offset - offset; - offset = aligned_offset + oat_dex_file.lookup_table_->RawDataLength(); - } else { - oat_dex_file.lookup_table_offset_ = 0; - } + oat_dex_file.offset_ = offset; + offset += oat_dex_file.SizeOf(); } return offset; } @@ -1239,7 +1447,6 @@ size_t OatWriter::InitOatCode(size_t offset) { oat_header_->SetExecutableOffset(offset); size_executable_offset_alignment_ = offset - old_offset; if (compiler_driver_->IsBootImage()) { - CHECK_EQ(image_patch_delta_, 0); InstructionSet instruction_set = compiler_driver_->GetInstructionSet(); #define DO_TRAMPOLINE(field, fn_name) \ @@ -1264,7 +1471,6 @@ size_t OatWriter::InitOatCode(size_t offset) { oat_header_->SetQuickImtConflictTrampolineOffset(0); oat_header_->SetQuickResolutionTrampolineOffset(0); oat_header_->SetQuickToInterpreterBridgeOffset(0); - oat_header_->SetImagePatchDelta(image_patch_delta_); } return offset; } @@ -1279,7 +1485,7 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { } while (false) VISIT(InitCodeMethodVisitor); - if (compiler_driver_->IsBootImage()) { + if (HasImage()) { VISIT(InitImageMethodVisitor); } @@ -1289,22 +1495,15 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { } bool OatWriter::WriteRodata(OutputStream* out) { - if (!GetOatDataOffset(out)) { - return false; - } - const size_t file_offset = oat_data_offset_; + CHECK(write_state_ == WriteState::kWriteRoData); - // Reserve space for header. It will be written last - after updating the checksum. - size_t header_size = oat_header_->GetHeaderSize(); - if (out->Seek(header_size, kSeekCurrent) == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to reserve space for oat header in " << out->GetLocation(); + if (!WriteClassOffsets(out)) { + LOG(ERROR) << "Failed to write class offsets to " << out->GetLocation(); return false; } - size_oat_header_ += sizeof(OatHeader); - size_oat_header_key_value_store_ += oat_header_->GetHeaderSize() - sizeof(OatHeader); - if (!WriteTables(out, file_offset)) { - LOG(ERROR) << "Failed to write oat tables to " << out->GetLocation(); + if (!WriteClasses(out)) { + LOG(ERROR) << "Failed to write classes to " << out->GetLocation(); return false; } @@ -1313,6 +1512,7 @@ bool OatWriter::WriteRodata(OutputStream* out) { LOG(ERROR) << "Failed to seek to oat code position in " << out->GetLocation(); return false; } + size_t file_offset = oat_data_offset_; size_t relative_offset = static_cast<size_t>(tables_end_offset) - file_offset; relative_offset = WriteMaps(out, file_offset, relative_offset); if (relative_offset == 0) { @@ -1332,11 +1532,13 @@ bool OatWriter::WriteRodata(OutputStream* out) { } DCHECK_OFFSET(); + write_state_ = WriteState::kWriteText; return true; } bool OatWriter::WriteCode(OutputStream* out) { - size_t header_size = oat_header_->GetHeaderSize(); + CHECK(write_state_ == WriteState::kWriteText); + const size_t file_offset = oat_data_offset_; size_t relative_offset = oat_header_->GetExecutableOffset(); DCHECK_OFFSET(); @@ -1390,10 +1592,12 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_dex_file_location_data_); DO_STAT(size_oat_dex_file_location_checksum_); DO_STAT(size_oat_dex_file_offset_); + DO_STAT(size_oat_dex_file_class_offsets_offset_); DO_STAT(size_oat_dex_file_lookup_table_offset_); - DO_STAT(size_oat_dex_file_class_offsets_); DO_STAT(size_oat_lookup_table_alignment_); DO_STAT(size_oat_lookup_table_); + DO_STAT(size_oat_class_offsets_alignment_); + DO_STAT(size_oat_class_offsets_); DO_STAT(size_oat_class_type_); DO_STAT(size_oat_class_status_); DO_STAT(size_oat_class_method_bitmaps_); @@ -1408,89 +1612,91 @@ bool OatWriter::WriteCode(OutputStream* out) { CHECK_EQ(file_offset + size_, static_cast<size_t>(oat_end_file_offset)); CHECK_EQ(size_, relative_offset); - // Finalize the header checksum. + write_state_ = WriteState::kWriteHeader; + return true; +} + +bool OatWriter::WriteHeader(OutputStream* out, + uint32_t image_file_location_oat_checksum, + uintptr_t image_file_location_oat_begin, + int32_t image_patch_delta) { + CHECK(write_state_ == WriteState::kWriteHeader); + + oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum); + oat_header_->SetImageFileLocationOatDataBegin(image_file_location_oat_begin); + if (compiler_driver_->IsBootImage()) { + CHECK_EQ(image_patch_delta, 0); + CHECK_EQ(oat_header_->GetImagePatchDelta(), 0); + } else { + CHECK_ALIGNED(image_patch_delta, kPageSize); + oat_header_->SetImagePatchDelta(image_patch_delta); + } oat_header_->UpdateChecksumWithHeaderData(); - // Write the header now that the checksum is final. + const size_t file_offset = oat_data_offset_; + + off_t current_offset = out->Seek(0, kSeekCurrent); + if (current_offset == static_cast<off_t>(-1)) { + PLOG(ERROR) << "Failed to get current offset from " << out->GetLocation(); + return false; + } if (out->Seek(file_offset, kSeekSet) == static_cast<off_t>(-1)) { PLOG(ERROR) << "Failed to seek to oat header position in " << out->GetLocation(); return false; } DCHECK_EQ(file_offset, static_cast<size_t>(out->Seek(0, kSeekCurrent))); + + // Flush all other data before writing the header. + if (!out->Flush()) { + PLOG(ERROR) << "Failed to flush before writing oat header to " << out->GetLocation(); + return false; + } + // Write the header. + size_t header_size = oat_header_->GetHeaderSize(); if (!out->WriteFully(oat_header_.get(), header_size)) { PLOG(ERROR) << "Failed to write oat header to " << out->GetLocation(); return false; } - if (out->Seek(oat_end_file_offset, kSeekSet) == static_cast<off_t>(-1)) { - PLOG(ERROR) << "Failed to seek to end after writing oat header to " << out->GetLocation(); + // Flush the header data. + if (!out->Flush()) { + PLOG(ERROR) << "Failed to flush after writing oat header to " << out->GetLocation(); + return false; + } + + if (out->Seek(current_offset, kSeekSet) == static_cast<off_t>(-1)) { + PLOG(ERROR) << "Failed to seek back after writing oat header to " << out->GetLocation(); return false; } - DCHECK_EQ(oat_end_file_offset, out->Seek(0, kSeekCurrent)); + DCHECK_EQ(current_offset, out->Seek(0, kSeekCurrent)); + write_state_ = WriteState::kDone; return true; } -bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) { - for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - if (!oat_dex_files_[i].Write(this, out, file_offset)) { - PLOG(ERROR) << "Failed to write oat dex information to " << out->GetLocation(); - return false; - } - } - for (size_t i = 0; i != oat_dex_files_.size(); ++i) { - uint32_t expected_offset = file_offset + oat_dex_files_[i].dex_file_offset_; - off_t actual_offset = out->Seek(expected_offset, kSeekSet); - if (static_cast<uint32_t>(actual_offset) != expected_offset) { - const DexFile* dex_file = (*dex_files_)[i]; - PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset - << " Expected: " << expected_offset << " File: " << dex_file->GetLocation(); - return false; - } - const DexFile* dex_file = (*dex_files_)[i]; - if (!out->WriteFully(&dex_file->GetHeader(), dex_file->GetHeader().file_size_)) { - PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() - << " to " << out->GetLocation(); - return false; - } - size_dex_file_ += dex_file->GetHeader().file_size_; - } - if (!WriteLookupTables(out, file_offset)) { - return false; - } - for (size_t i = 0; i != oat_classes_.size(); ++i) { - if (!oat_classes_[i].Write(this, out, file_offset)) { - PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation(); - return false; +bool OatWriter::WriteClassOffsets(OutputStream* out) { + for (OatDexFile& oat_dex_file : oat_dex_files_) { + if (oat_dex_file.class_offsets_offset_ != 0u) { + uint32_t expected_offset = oat_data_offset_ + oat_dex_file.class_offsets_offset_; + off_t actual_offset = out->Seek(expected_offset, kSeekSet); + if (static_cast<uint32_t>(actual_offset) != expected_offset) { + PLOG(ERROR) << "Failed to seek to oat class offsets section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << oat_dex_file.GetLocation(); + return false; + } + if (!oat_dex_file.WriteClassOffsets(this, out)) { + return false; + } } } return true; } -bool OatWriter::WriteLookupTables(OutputStream* out, const size_t file_offset) { - for (size_t i = 0; i < oat_dex_files_.size(); ++i) { - const uint32_t lookup_table_offset = oat_dex_files_[i].lookup_table_offset_; - const TypeLookupTable* table = oat_dex_files_[i].lookup_table_; - DCHECK_EQ(lookup_table_offset == 0, table == nullptr); - if (lookup_table_offset == 0) { - continue; - } - const uint32_t expected_offset = file_offset + lookup_table_offset; - off_t actual_offset = out->Seek(expected_offset, kSeekSet); - if (static_cast<uint32_t>(actual_offset) != expected_offset) { - const DexFile* dex_file = (*dex_files_)[i]; - PLOG(ERROR) << "Failed to seek to lookup table section. Actual: " << actual_offset - << " Expected: " << expected_offset << " File: " << dex_file->GetLocation(); +bool OatWriter::WriteClasses(OutputStream* out) { + for (OatClass& oat_class : oat_classes_) { + if (!oat_class.Write(this, out, oat_data_offset_)) { + PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation(); return false; } - if (table != nullptr) { - if (!WriteData(out, table->RawData(), table->RawDataLength())) { - const DexFile* dex_file = (*dex_files_)[i]; - PLOG(ERROR) << "Failed to write lookup table for " << dex_file->GetLocation() - << " to " << out->GetLocation(); - return false; - } - size_oat_lookup_table_ += table->RawDataLength(); - } } return true; } @@ -1585,6 +1791,455 @@ bool OatWriter::GetOatDataOffset(OutputStream* out) { return true; } +bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) { + // Read the dex file header and perform minimal verification. + uint8_t raw_header[sizeof(DexFile::Header)]; + if (!file->ReadFully(&raw_header, sizeof(DexFile::Header))) { + PLOG(ERROR) << "Failed to read dex file header. Actual: " + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!ValidateDexFileHeader(raw_header, oat_dex_file->GetLocation())) { + return false; + } + + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); + oat_dex_file->dex_file_size_ = header->file_size_; + oat_dex_file->dex_file_location_checksum_ = header->checksum_; + oat_dex_file->class_offsets_.resize(header->class_defs_size_); + return true; +} + +bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { + if (!DexFile::IsMagicValid(raw_header)) { + LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location; + return false; + } + if (!DexFile::IsVersionValid(raw_header)) { + LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location; + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); + if (header->file_size_ < sizeof(DexFile::Header)) { + LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header." + << " File: " << location; + return false; + } + return true; +} + +bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) { + TimingLogger::ScopedTiming split("WriteDexFiles", timings_); + + // Get the elf file offset of the oat file. + if (!GetOatDataOffset(rodata)) { + return false; + } + + // Write dex files. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + if (!WriteDexFile(rodata, file, &oat_dex_file)) { + return false; + } + } + + // Close sources. + for (OatDexFile& oat_dex_file : oat_dex_files_) { + oat_dex_file.source_.Clear(); // Get rid of the reference, it's about to be invalidated. + } + zipped_dex_files_.clear(); + zip_archives_.clear(); + raw_dex_files_.clear(); + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file) { + if (!SeekToDexFile(rodata, file, oat_dex_file)) { + return false; + } + if (oat_dex_file->source_.IsZipEntry()) { + if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetZipEntry())) { + return false; + } + } else if (oat_dex_file->source_.IsRawFile()) { + if (!WriteDexFile(rodata, file, oat_dex_file, oat_dex_file->source_.GetRawFile())) { + return false; + } + } else { + DCHECK(oat_dex_file->source_.IsRawData()); + if (!WriteDexFile(rodata, oat_dex_file, oat_dex_file->source_.GetRawData())) { + return false; + } + } + + // Update current size and account for the written data. + DCHECK_EQ(size_, oat_dex_file->dex_file_offset_); + size_ += oat_dex_file->dex_file_size_; + size_dex_file_ += oat_dex_file->dex_file_size_; + return true; +} + +bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { + // Dex files are required to be 4 byte aligned. + size_t original_offset = size_; + size_t offset = RoundUp(original_offset, 4); + size_dex_file_alignment_ += offset - original_offset; + + // Seek to the start of the dex file and flush any pending operations in the stream. + // Verify that, after flushing the stream, the file is at the same offset as the stream. + uint32_t start_offset = oat_data_offset_ + offset; + off_t actual_offset = out->Seek(start_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(start_offset)) { + PLOG(ERROR) << "Failed to seek to dex file section. Actual: " << actual_offset + << " Expected: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!out->Flush()) { + PLOG(ERROR) << "Failed to flush before writing dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + actual_offset = lseek(file->Fd(), 0, SEEK_CUR); + if (actual_offset != static_cast<off_t>(start_offset)) { + PLOG(ERROR) << "Stream/file position mismatch! Actual: " << actual_offset + << " Expected: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + size_ = offset; + oat_dex_file->dex_file_offset_ = offset; + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, + File* file, + OatDexFile* oat_dex_file, + ZipEntry* dex_file) { + size_t start_offset = oat_data_offset_ + size_; + DCHECK_EQ(static_cast<off_t>(start_offset), rodata->Seek(0, kSeekCurrent)); + + // Extract the dex file and get the extracted size. + std::string error_msg; + if (!dex_file->ExtractToFile(*file, &error_msg)) { + LOG(ERROR) << "Failed to extract dex file from ZIP entry: " << error_msg + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (file->Flush() != 0) { + PLOG(ERROR) << "Failed to flush dex file from ZIP entry." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + off_t extracted_end = lseek(file->Fd(), 0, SEEK_CUR); + if (extracted_end == static_cast<off_t>(-1)) { + PLOG(ERROR) << "Failed get end offset after writing dex file from ZIP entry." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (extracted_end < static_cast<off_t>(start_offset)) { + LOG(ERROR) << "Dex file end position is before start position! End: " << extracted_end + << " Start: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + uint64_t extracted_size = static_cast<uint64_t>(extracted_end - start_offset); + if (extracted_size < sizeof(DexFile::Header)) { + LOG(ERROR) << "Extracted dex file is shorter than dex file header. size: " + << extracted_size << " File: " << oat_dex_file->GetLocation(); + return false; + } + + // Read the dex file header and extract required data to OatDexFile. + off_t actual_offset = lseek(file->Fd(), start_offset, SEEK_SET); + if (actual_offset != static_cast<off_t>(start_offset)) { + PLOG(ERROR) << "Failed to seek back to dex file header. Actual: " << actual_offset + << " Expected: " << start_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!ReadDexFileHeader(file, oat_dex_file)) { + return false; + } + if (extracted_size < oat_dex_file->dex_file_size_) { + LOG(ERROR) << "Extracted truncated dex file. Extracted size: " << extracted_size + << " file size from header: " << oat_dex_file->dex_file_size_ + << " File: " << oat_dex_file->GetLocation(); + return false; + } + + // Override the checksum from header with the CRC from ZIP entry. + oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32(); + + // Seek both file and stream to the end offset. + size_t end_offset = start_offset + oat_dex_file->dex_file_size_; + actual_offset = lseek(file->Fd(), end_offset, SEEK_SET); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Failed to seek to end of dex file. Actual: " << actual_offset + << " Expected: " << end_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + actual_offset = rodata->Seek(end_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset + << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after seeking over dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + // If we extracted more than the size specified in the header, truncate the file. + if (extracted_size > oat_dex_file->dex_file_size_) { + if (file->SetLength(end_offset) != 0) { + PLOG(ERROR) << "Failed to truncate excessive dex file length." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + } + + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, + File* file, + OatDexFile* oat_dex_file, + File* dex_file) { + size_t start_offset = oat_data_offset_ + size_; + DCHECK_EQ(static_cast<off_t>(start_offset), rodata->Seek(0, kSeekCurrent)); + + off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET); + if (input_offset != static_cast<off_t>(0)) { + PLOG(ERROR) << "Failed to seek to dex file header. Actual: " << input_offset + << " Expected: 0" + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (!ReadDexFileHeader(dex_file, oat_dex_file)) { + return false; + } + + // Copy the input dex file using sendfile(). + if (!file->Copy(dex_file, 0, oat_dex_file->dex_file_size_)) { + PLOG(ERROR) << "Failed to copy dex file to oat file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + if (file->Flush() != 0) { + PLOG(ERROR) << "Failed to flush dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + // Check file position and seek the stream to the end offset. + size_t end_offset = start_offset + oat_dex_file->dex_file_size_; + off_t actual_offset = lseek(file->Fd(), 0, SEEK_CUR); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Unexpected file position after copying dex file. Actual: " << actual_offset + << " Expected: " << end_offset + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + actual_offset = rodata->Seek(end_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(end_offset)) { + PLOG(ERROR) << "Failed to seek stream to end of dex file. Actual: " << actual_offset + << " Expected: " << end_offset << " File: " << oat_dex_file->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after seeking over dex file." + << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); + return false; + } + + return true; +} + +bool OatWriter::WriteDexFile(OutputStream* rodata, + OatDexFile* oat_dex_file, + const uint8_t* dex_file) { + // Note: The raw data has already been checked to contain the header + // and all the data that the header specifies as the file size. + DCHECK(dex_file != nullptr); + DCHECK(ValidateDexFileHeader(dex_file, oat_dex_file->GetLocation())); + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(dex_file); + + if (!rodata->WriteFully(dex_file, header->file_size_)) { + PLOG(ERROR) << "Failed to write dex file " << oat_dex_file->GetLocation() + << " to " << rodata->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing dex file." + << " File: " << oat_dex_file->GetLocation(); + return false; + } + + // Update dex file size and resize class offsets in the OatDexFile. + // Note: For raw data, the checksum is passed directly to AddRawDexFileSource(). + oat_dex_file->dex_file_size_ = header->file_size_; + oat_dex_file->class_offsets_.resize(header->class_defs_size_); + return true; +} + +bool OatWriter::WriteOatDexFiles(OutputStream* rodata) { + TimingLogger::ScopedTiming split("WriteOatDexFiles", timings_); + + // Seek to the start of OatDexFiles, i.e. to the end of the OatHeader. If there are + // no OatDexFiles, no data is actually written to .rodata before WriteHeader() and + // this Seek() ensures that we reserve the space for OatHeader in .rodata. + DCHECK(oat_dex_files_.empty() || oat_dex_files_[0u].offset_ == oat_header_->GetHeaderSize()); + uint32_t expected_offset = oat_data_offset_ + oat_header_->GetHeaderSize(); + off_t actual_offset = rodata->Seek(expected_offset, kSeekSet); + if (static_cast<uint32_t>(actual_offset) != expected_offset) { + PLOG(ERROR) << "Failed to seek to OatDexFile table section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << rodata->GetLocation(); + return false; + } + + for (size_t i = 0, size = oat_dex_files_.size(); i != size; ++i) { + OatDexFile* oat_dex_file = &oat_dex_files_[i]; + + DCHECK_EQ(oat_data_offset_ + oat_dex_file->offset_, + static_cast<size_t>(rodata->Seek(0, kSeekCurrent))); + + // Write OatDexFile. + if (!oat_dex_file->Write(this, rodata)) { + PLOG(ERROR) << "Failed to write oat dex information to " << rodata->GetLocation(); + return false; + } + } + + return true; +} + +bool OatWriter::ExtendForTypeLookupTables(OutputStream* rodata, File* file, size_t offset) { + TimingLogger::ScopedTiming split("ExtendForTypeLookupTables", timings_); + + int64_t new_length = oat_data_offset_ + dchecked_integral_cast<int64_t>(offset); + if (file->SetLength(new_length) != 0) { + PLOG(ERROR) << "Failed to extend file for type lookup tables. new_length: " << new_length + << "File: " << file->GetPath(); + return false; + } + off_t actual_offset = rodata->Seek(new_length, kSeekSet); + if (actual_offset != static_cast<off_t>(new_length)) { + PLOG(ERROR) << "Failed to seek stream after extending file for type lookup tables." + << " Actual: " << actual_offset << " Expected: " << new_length + << " File: " << rodata->GetLocation(); + return false; + } + if (!rodata->Flush()) { + PLOG(ERROR) << "Failed to flush stream after extending for type lookup tables." + << " File: " << rodata->GetLocation(); + return false; + } + return true; +} + +bool OatWriter::OpenDexFiles( + File* file, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { + TimingLogger::ScopedTiming split("OpenDexFiles", timings_); + + if (oat_dex_files_.empty()) { + // Nothing to do. + return true; + } + + size_t map_offset = oat_dex_files_[0].dex_file_offset_; + size_t length = size_ - map_offset; + std::string error_msg; + std::unique_ptr<MemMap> dex_files_map(MemMap::MapFile(length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + file->Fd(), + oat_data_offset_ + map_offset, + /* low_4gb */ false, + file->GetPath().c_str(), + &error_msg)); + if (dex_files_map == nullptr) { + LOG(ERROR) << "Failed to mmap() dex files from oat file. File: " << file->GetPath() + << " error: " << error_msg; + return false; + } + std::vector<std::unique_ptr<const DexFile>> dex_files; + for (OatDexFile& oat_dex_file : oat_dex_files_) { + // Make sure no one messed with input files while we were copying data. + // At the very least we need consistent file size and number of class definitions. + const uint8_t* raw_dex_file = + dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset; + if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) { + // Note: ValidateDexFileHeader() already logged an error message. + LOG(ERROR) << "Failed to verify written dex file header!" + << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset + << " ~ " << static_cast<const void*>(raw_dex_file); + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); + if (header->file_size_ != oat_dex_file.dex_file_size_) { + LOG(ERROR) << "File size mismatch in written dex file header! Expected: " + << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_ + << " Output: " << file->GetPath(); + return false; + } + if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) { + LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: " + << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_ + << " Output: " << file->GetPath(); + return false; + } + + // Now, open the dex file. + dex_files.emplace_back(DexFile::Open(raw_dex_file, + oat_dex_file.dex_file_size_, + oat_dex_file.GetLocation(), + oat_dex_file.dex_file_location_checksum_, + /* oat_dex_file */ nullptr, + &error_msg)); + if (dex_files.back() == nullptr) { + LOG(ERROR) << "Failed to open dex file from oat file. File:" << oat_dex_file.GetLocation(); + return false; + } + } + + *opened_dex_files_map = std::move(dex_files_map); + *opened_dex_files = std::move(dex_files); + return true; +} + +bool OatWriter::WriteTypeLookupTables( + MemMap* opened_dex_files_map, + const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files) { + TimingLogger::ScopedTiming split("WriteTypeLookupTables", timings_); + + DCHECK_EQ(opened_dex_files.size(), oat_dex_files_.size()); + for (size_t i = 0, size = opened_dex_files.size(); i != size; ++i) { + OatDexFile* oat_dex_file = &oat_dex_files_[i]; + if (oat_dex_file->lookup_table_offset_ != 0u) { + DCHECK(oat_dex_file->create_type_lookup_table_ == CreateTypeLookupTable::kCreate); + DCHECK_NE(oat_dex_file->class_offsets_.size(), 0u); + size_t map_offset = oat_dex_files_[0].dex_file_offset_; + size_t lookup_table_offset = oat_dex_file->lookup_table_offset_; + uint8_t* lookup_table = opened_dex_files_map->Begin() + (lookup_table_offset - map_offset); + opened_dex_files[i]->CreateTypeLookupTable(lookup_table); + } + } + + DCHECK_EQ(opened_dex_files_map == nullptr, opened_dex_files.empty()); + if (opened_dex_files_map != nullptr && !opened_dex_files_map->Sync()) { + PLOG(ERROR) << "Failed to Sync() type lookup tables. Map: " << opened_dex_files_map->GetName(); + return false; + } + + return true; +} + bool OatWriter::WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta) { static const uint8_t kPadding[] = { 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u @@ -1611,15 +2266,20 @@ std::pair<bool, uint32_t> OatWriter::MethodOffsetMap::FindMethodOffset(MethodRef } } -OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) { - offset_ = offset; - const std::string& location(dex_file.GetLocation()); - dex_file_location_size_ = location.size(); - dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data()); - dex_file_location_checksum_ = dex_file.GetLocationChecksum(); - dex_file_offset_ = 0; - lookup_table_offset_ = 0; - class_offsets_.resize(dex_file.NumClassDefs()); +OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, + DexFileSource source, + CreateTypeLookupTable create_type_lookup_table) + : source_(source), + create_type_lookup_table_(create_type_lookup_table), + dex_file_size_(0), + offset_(0), + dex_file_location_size_(strlen(dex_file_location)), + dex_file_location_data_(dex_file_location), + dex_file_location_checksum_(0u), + dex_file_offset_(0u), + class_offsets_offset_(0u), + lookup_table_offset_(0u), + class_offsets_() { } size_t OatWriter::OatDexFile::SizeOf() const { @@ -1627,24 +2287,54 @@ size_t OatWriter::OatDexFile::SizeOf() const { + dex_file_location_size_ + sizeof(dex_file_location_checksum_) + sizeof(dex_file_offset_) - + sizeof(lookup_table_offset_) - + (sizeof(class_offsets_[0]) * class_offsets_.size()); + + sizeof(class_offsets_offset_) + + sizeof(lookup_table_offset_); } -bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, - OutputStream* out, - const size_t file_offset) const { +void OatWriter::OatDexFile::ReserveTypeLookupTable(OatWriter* oat_writer) { + DCHECK_EQ(lookup_table_offset_, 0u); + if (create_type_lookup_table_ == CreateTypeLookupTable::kCreate && !class_offsets_.empty()) { + size_t table_size = TypeLookupTable::RawDataLength(class_offsets_.size()); + if (table_size != 0u) { + // Type tables are required to be 4 byte aligned. + size_t original_offset = oat_writer->size_; + size_t offset = RoundUp(original_offset, 4); + oat_writer->size_oat_lookup_table_alignment_ += offset - original_offset; + lookup_table_offset_ = offset; + oat_writer->size_ = offset + table_size; + oat_writer->size_oat_lookup_table_ += table_size; + } + } +} + +void OatWriter::OatDexFile::ReserveClassOffsets(OatWriter* oat_writer) { + DCHECK_EQ(class_offsets_offset_, 0u); + if (!class_offsets_.empty()) { + // Class offsets are required to be 4 byte aligned. + size_t original_offset = oat_writer->size_; + size_t offset = RoundUp(original_offset, 4); + oat_writer->size_oat_class_offsets_alignment_ += offset - original_offset; + class_offsets_offset_ = offset; + oat_writer->size_ = offset + GetClassOffsetsRawSize(); + } +} + +bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) const { + const size_t file_offset = oat_writer->oat_data_offset_; DCHECK_OFFSET_(); + if (!oat_writer->WriteData(out, &dex_file_location_size_, sizeof(dex_file_location_size_))) { PLOG(ERROR) << "Failed to write dex file location length to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_location_size_ += sizeof(dex_file_location_size_); + if (!oat_writer->WriteData(out, dex_file_location_data_, dex_file_location_size_)) { PLOG(ERROR) << "Failed to write dex file location data to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_location_data_ += dex_file_location_size_; + if (!oat_writer->WriteData(out, &dex_file_location_checksum_, sizeof(dex_file_location_checksum_))) { @@ -1652,21 +2342,35 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, return false; } oat_writer->size_oat_dex_file_location_checksum_ += sizeof(dex_file_location_checksum_); + if (!oat_writer->WriteData(out, &dex_file_offset_, sizeof(dex_file_offset_))) { PLOG(ERROR) << "Failed to write dex file offset to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); + + if (!oat_writer->WriteData(out, &class_offsets_offset_, sizeof(class_offsets_offset_))) { + PLOG(ERROR) << "Failed to write class offsets offset to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_class_offsets_offset_ += sizeof(class_offsets_offset_); + if (!oat_writer->WriteData(out, &lookup_table_offset_, sizeof(lookup_table_offset_))) { PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation(); return false; } oat_writer->size_oat_dex_file_lookup_table_offset_ += sizeof(lookup_table_offset_); + + return true; +} + +bool OatWriter::OatDexFile::WriteClassOffsets(OatWriter* oat_writer, OutputStream* out) { if (!oat_writer->WriteData(out, class_offsets_.data(), GetClassOffsetsRawSize())) { - PLOG(ERROR) << "Failed to write methods offsets to " << out->GetLocation(); + PLOG(ERROR) << "Failed to write oat class offsets for " << GetLocation() + << " to " << out->GetLocation(); return false; } - oat_writer->size_oat_dex_file_class_offsets_ += GetClassOffsetsRawSize(); + oat_writer->size_oat_class_offsets_ += GetClassOffsetsRawSize(); return true; } diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 5feb5fc516..d681998774 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -27,7 +27,9 @@ #include "method_reference.h" #include "mirror/class.h" #include "oat.h" +#include "os.h" #include "safe_map.h" +#include "ScopedFd.h" #include "utils/array_ref.h" namespace art { @@ -39,6 +41,7 @@ class ImageWriter; class OutputStream; class TimingLogger; class TypeLookupTable; +class ZipEntry; namespace dwarf { struct MethodDebugInfo; @@ -61,6 +64,11 @@ struct MethodDebugInfo; // ... // TypeLookupTable[D] // +// ClassOffsets[0] one table of OatClass offsets for each class def for each OatDexFile. +// ClassOffsets[1] +// ... +// ClassOffsets[D] +// // OatClass[0] one variable sized OatClass for each of C DexFile::ClassDefs // OatClass[1] contains OatClass entries with class status, offsets to code, etc. // ... @@ -93,15 +101,65 @@ struct MethodDebugInfo; // class OatWriter { public: - OatWriter(const std::vector<const DexFile*>& dex_files, - uint32_t image_file_location_oat_checksum, - uintptr_t image_file_location_oat_begin, - int32_t image_patch_delta, - const CompilerDriver* compiler, - ImageWriter* image_writer, - bool compiling_boot_image, - TimingLogger* timings, - SafeMap<std::string, std::string>* key_value_store); + enum class CreateTypeLookupTable { + kCreate, + kDontCreate, + kDefault = kCreate + }; + + OatWriter(bool compiling_boot_image, TimingLogger* timings); + + // To produce a valid oat file, the user must first add sources with any combination of + // - AddDexFileSource(), + // - AddZippedDexFilesSource(), + // - AddRawDexFileSource(). + // Then the user must call in order + // - WriteAndOpenDexFiles() + // - PrepareLayout(), + // - WriteRodata(), + // - WriteCode(), + // - WriteHeader(). + + // Add dex file source(s) from a file, either a plain dex file or + // a zip file with one or more dex files. + bool AddDexFileSource( + const char* filename, + const char* location, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + // Add dex file source(s) from a zip file specified by a file handle. + bool AddZippedDexFilesSource( + ScopedFd&& zip_fd, + const char* location, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + // Add dex file source from raw memory. + bool AddRawDexFileSource( + const ArrayRef<const uint8_t>& data, + const char* location, + uint32_t location_checksum, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + dchecked_vector<const char*> GetSourceLocations() const; + + // Write raw dex files to the .rodata section and open them from the oat file. + bool WriteAndOpenDexFiles(OutputStream* rodata, + File* file, + InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + SafeMap<std::string, std::string>* key_value_store, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); + // Prepare layout of remaining data. + void PrepareLayout(const CompilerDriver* compiler, + ImageWriter* image_writer, + const std::vector<const DexFile*>& dex_files); + // Write the rest of .rodata section (ClassOffsets[], OatClass[], maps). + bool WriteRodata(OutputStream* out); + // Write the code to the .text section. + bool WriteCode(OutputStream* out); + // Write the oat header. This finalizes the oat file. + bool WriteHeader(OutputStream* out, + uint32_t image_file_location_oat_checksum, + uintptr_t image_file_location_oat_begin, + int32_t image_patch_delta); // Returns whether the oat file has an associated image. bool HasImage() const { @@ -130,9 +188,6 @@ class OatWriter { return ArrayRef<const uintptr_t>(absolute_patch_locations_); } - bool WriteRodata(OutputStream* out); - bool WriteCode(OutputStream* out); - ~OatWriter(); ArrayRef<const dwarf::MethodDebugInfo> GetMethodDebugInfo() const { @@ -144,6 +199,7 @@ class OatWriter { } private: + class DexFileSource; class OatClass; class OatDexFile; @@ -174,29 +230,65 @@ class OatWriter { // with a given DexMethodVisitor. bool VisitDexMethods(DexMethodVisitor* visitor); - size_t InitOatHeader(); + size_t InitOatHeader(InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + uint32_t num_dex_files, + SafeMap<std::string, std::string>* key_value_store); size_t InitOatDexFiles(size_t offset); - size_t InitLookupTables(size_t offset); - size_t InitDexFiles(size_t offset); size_t InitOatClasses(size_t offset); size_t InitOatMaps(size_t offset); size_t InitOatCode(size_t offset); size_t InitOatCodeDexFiles(size_t offset); - bool WriteTables(OutputStream* out, const size_t file_offset); - bool WriteLookupTables(OutputStream* out, const size_t file_offset); + bool WriteClassOffsets(OutputStream* out); + bool WriteClasses(OutputStream* out); size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset); 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 ReadDexFileHeader(File* file, OatDexFile* oat_dex_file); + bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); + bool WriteDexFiles(OutputStream* rodata, File* file); + bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file); + bool SeekToDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file); + bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, ZipEntry* dex_file); + bool WriteDexFile(OutputStream* rodata, File* file, OatDexFile* oat_dex_file, File* dex_file); + bool WriteDexFile(OutputStream* rodata, OatDexFile* oat_dex_file, const uint8_t* dex_file); + bool WriteOatDexFiles(OutputStream* rodata); + bool ExtendForTypeLookupTables(OutputStream* rodata, File* file, size_t offset); + bool OpenDexFiles(File* file, + /*out*/ std::unique_ptr<MemMap>* opened_dex_files_map, + /*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files); + bool WriteTypeLookupTables(MemMap* opened_dex_files_map, + const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files); bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta); bool WriteData(OutputStream* out, const void* data, size_t size); + enum class WriteState { + kAddingDexFileSources, + kPrepareLayout, + kWriteRoData, + kWriteText, + kWriteHeader, + kDone + }; + + WriteState write_state_; + TimingLogger* timings_; + + std::vector<std::unique_ptr<File>> raw_dex_files_; + std::vector<std::unique_ptr<ZipArchive>> zip_archives_; + std::vector<std::unique_ptr<ZipEntry>> zipped_dex_files_; + + // Using std::list<> which doesn't move elements around on push/emplace_back(). + // We need this because we keep plain pointers to the strings' c_str(). + std::list<std::string> zipped_dex_file_locations_; + dchecked_vector<dwarf::MethodDebugInfo> method_info_; - const CompilerDriver* const compiler_driver_; - ImageWriter* const image_writer_; + const CompilerDriver* compiler_driver_; + ImageWriter* image_writer_; const bool compiling_boot_image_; // note OatFile does not take ownership of the DexFiles @@ -215,13 +307,7 @@ class OatWriter { // Offset of the oat data from the start of the mmapped region of the elf file. size_t oat_data_offset_; - // dependencies on the image. - uint32_t image_file_location_oat_checksum_; - uintptr_t image_file_location_oat_begin_; - int32_t image_patch_delta_; - // data to write - SafeMap<std::string, std::string>* key_value_store_; std::unique_ptr<OatHeader> oat_header_; dchecked_vector<OatDexFile> oat_dex_files_; dchecked_vector<OatClass> oat_classes_; @@ -257,10 +343,12 @@ class OatWriter { uint32_t size_oat_dex_file_location_data_; uint32_t size_oat_dex_file_location_checksum_; uint32_t size_oat_dex_file_offset_; + uint32_t size_oat_dex_file_class_offsets_offset_; uint32_t size_oat_dex_file_lookup_table_offset_; - uint32_t size_oat_dex_file_class_offsets_; uint32_t size_oat_lookup_table_alignment_; uint32_t size_oat_lookup_table_; + uint32_t size_oat_class_offsets_alignment_; + uint32_t size_oat_class_offsets_; uint32_t size_oat_class_type_; uint32_t size_oat_class_status_; uint32_t size_oat_class_method_bitmaps_; @@ -269,7 +357,7 @@ class OatWriter { std::unique_ptr<linker::RelativePatcher> relative_patcher_; // The locations of absolute patches relative to the start of the executable section. - std::vector<uintptr_t> absolute_patch_locations_; + dchecked_vector<uintptr_t> absolute_patch_locations_; // Map method reference to assigned offset. // Wrap the map in a class implementing linker::RelativePatcherTargetProvider. diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h index b9df686ffd..6dc53207ea 100644 --- a/compiler/optimizing/bounds_check_elimination.h +++ b/compiler/optimizing/bounds_check_elimination.h @@ -29,13 +29,13 @@ class BoundsCheckElimination : public HOptimization { BoundsCheckElimination(HGraph* graph, const SideEffectsAnalysis& side_effects, HInductionVarAnalysis* induction_analysis) - : HOptimization(graph, kBoundsCheckEliminiationPassName), + : HOptimization(graph, kBoundsCheckEliminationPassName), side_effects_(side_effects), induction_analysis_(induction_analysis) {} void Run() OVERRIDE; - static constexpr const char* kBoundsCheckEliminiationPassName = "BCE"; + static constexpr const char* kBoundsCheckEliminationPassName = "BCE"; private: const SideEffectsAnalysis& side_effects_; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 272579219f..5f00f0a536 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -5381,7 +5381,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { Register cls = locations->InAt(1).AsRegister<Register>(); Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); - Location temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(0) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); @@ -5398,7 +5398,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { } // /* HeapReference<Class> */ out = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, temp_loc); + GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); switch (type_check_kind) { case TypeCheckKind::kExactCheck: { @@ -5416,7 +5416,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { Label loop; __ Bind(&loop); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. __ CompareAndBranchIfZero(out, &done); __ cmp(out, ShifterOperand(cls)); @@ -5435,7 +5435,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { __ cmp(out, ShifterOperand(cls)); __ b(&success, EQ); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); __ CompareAndBranchIfNonZero(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. __ b(&done); @@ -5454,7 +5454,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { __ b(&exact_check, EQ); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ out = out->component_type_ - GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. __ CompareAndBranchIfZero(out, &done); __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); @@ -5567,7 +5567,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { Register cls = locations->InAt(1).AsRegister<Register>(); Location temp_loc = locations->GetTemp(0); Register temp = temp_loc.AsRegister<Register>(); - Location temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(1) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); @@ -5593,7 +5593,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); switch (type_check_kind) { case TypeCheckKind::kExactCheck: @@ -5611,7 +5611,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { Label loop, compare_classes; __ Bind(&loop); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // to the `compare_classes` label to compare it with the checked @@ -5623,7 +5623,8 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ b(type_check_slow_path->GetEntryLabel()); __ Bind(&compare_classes); @@ -5640,7 +5641,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { __ b(&done, EQ); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // back at the beginning of the loop. @@ -5651,7 +5652,8 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ b(type_check_slow_path->GetEntryLabel()); break; } @@ -5664,7 +5666,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); // If the component type is not null (i.e. the object is indeed // an array), jump to label `check_non_primitive_component_type` @@ -5677,7 +5679,8 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ b(type_check_slow_path->GetEntryLabel()); __ Bind(&check_non_primitive_component_type); @@ -5686,7 +5689,8 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { __ CompareAndBranchIfZero(temp, &done); // Same comment as above regarding `temp` and the slow path. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ b(type_check_slow_path->GetEntryLabel()); break; } @@ -5897,23 +5901,24 @@ void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instr void InstructionCodeGeneratorARM::GenerateReferenceLoadOneRegister(HInstruction* instruction, Location out, uint32_t offset, - Location temp) { + Location maybe_temp) { Register out_reg = out.AsRegister<Register>(); if (kEmitCompilerReadBarrier) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; if (kUseBakerReadBarrier) { // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(out + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, out_reg, offset, temp, /* needs_null_check */ false); + instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); } else { // Load with slow path based read barrier. - // Save the value of `out` into `temp` before overwriting it + // Save the value of `out` into `maybe_temp` before overwriting it // in the following move operation, as we will need it for the // read barrier below. - __ Mov(temp.AsRegister<Register>(), out_reg); + __ Mov(maybe_temp.AsRegister<Register>(), out_reg); // /* HeapReference<Object> */ out = *(out + offset) __ LoadFromOffset(kLoadWord, out_reg, out_reg, offset); - codegen_->GenerateReadBarrierSlow(instruction, out, out, temp, offset); + codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); } } else { // Plain load with no read barrier. @@ -5927,15 +5932,16 @@ void InstructionCodeGeneratorARM::GenerateReferenceLoadTwoRegisters(HInstruction Location out, Location obj, uint32_t offset, - Location temp) { + Location maybe_temp) { Register out_reg = out.AsRegister<Register>(); Register obj_reg = obj.AsRegister<Register>(); if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(obj + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, obj_reg, offset, temp, /* needs_null_check */ false); + instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); } else { // Load with slow path based read barrier. // /* HeapReference<Object> */ out = *(obj + offset) diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index d45ea973f9..558c9cf0e4 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -240,23 +240,29 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { // out <- *(out + offset) // // while honoring heap poisoning and/or read barriers (if any). - // Register `temp` is used when generating a read barrier. + // + // Location `maybe_temp` is used when generating a read barrier and + // shall be a register in that case; it may be an invalid location + // otherwise. void GenerateReferenceLoadOneRegister(HInstruction* instruction, Location out, uint32_t offset, - Location temp); + Location maybe_temp); // Generate a heap reference load using two different registers // `out` and `obj`: // // out <- *(obj + offset) // // while honoring heap poisoning and/or read barriers (if any). - // Register `temp` is used when generating a Baker's read barrier. + // + // Location `maybe_temp` is used when generating a Baker's (fast + // path) read barrier and shall be a register in that case; it may + // be an invalid location otherwise. void GenerateReferenceLoadTwoRegisters(HInstruction* instruction, Location out, Location obj, uint32_t offset, - Location temp); + Location maybe_temp); // Generate a GC root reference load: // // root <- *(obj + offset) diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 2cb2741b17..c0e3959933 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -584,6 +584,56 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { } } +// Slow path marking an object during a read barrier. +class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { + public: + ReadBarrierMarkSlowPathARM64(HInstruction* instruction, Location out, Location obj) + : instruction_(instruction), out_(out), obj_(obj) { + DCHECK(kEmitCompilerReadBarrier); + } + + const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARM64"; } + + void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); + Primitive::Type type = Primitive::kPrimNot; + DCHECK(locations->CanCall()); + DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg())); + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsLoadClass() || + instruction_->IsLoadString() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast()) + << "Unexpected instruction in read barrier marking slow path: " + << instruction_->DebugName(); + + __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); + + InvokeRuntimeCallingConvention calling_convention; + CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); + arm64_codegen->MoveLocation(LocationFrom(calling_convention.GetRegisterAt(0)), obj_, type); + arm64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pReadBarrierMark), + instruction_, + instruction_->GetDexPc(), + this); + CheckEntrypointTypes<kQuickReadBarrierMark, mirror::Object*, mirror::Object*>(); + arm64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type); + + RestoreLiveRegisters(codegen, locations); + __ B(GetExitLabel()); + } + + private: + HInstruction* const instruction_; + const Location out_; + const Location obj_; + + DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM64); +}; + // Slow path generating a read barrier for a heap reference. class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { public: @@ -605,7 +655,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { // to be instrumented, e.g.: // // __ Ldr(out, HeapOperand(out, class_offset); - // codegen_->GenerateReadBarrier(instruction, out_loc, out_loc, out_loc, offset); + // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset); // // In that case, we have lost the information about the original // object, and the emitted read barrier cannot work properly. @@ -621,7 +671,9 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg())); DCHECK(!instruction_->IsInvoke() || (instruction_->IsInvokeStaticOrDirect() && - instruction_->GetLocations()->Intrinsified())); + instruction_->GetLocations()->Intrinsified())) + << "Unexpected instruction in read barrier for heap reference slow path: " + << instruction_->DebugName(); // The read barrier instrumentation does not support the // HArm64IntermediateAddress instruction yet. DCHECK(!(instruction_->IsArrayGet() && @@ -769,14 +821,18 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 { public: ReadBarrierForRootSlowPathARM64(HInstruction* instruction, Location out, Location root) - : instruction_(instruction), out_(out), root_(root) {} + : instruction_(instruction), out_(out), root_(root) { + DCHECK(kEmitCompilerReadBarrier); + } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); Primitive::Type type = Primitive::kPrimNot; DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg())); - DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString()); + DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString()) + << "Unexpected instruction in read barrier for GC root slow path: " + << instruction_->DebugName(); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); @@ -1338,7 +1394,8 @@ void CodeGeneratorARM64::Load(Primitive::Type type, void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, CPURegister dst, - const MemOperand& src) { + const MemOperand& src, + bool needs_null_check) { MacroAssembler* masm = GetVIXLAssembler(); BlockPoolsScope block_pools(masm); UseScratchRegisterScope temps(masm); @@ -1354,20 +1411,28 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, switch (type) { case Primitive::kPrimBoolean: __ Ldarb(Register(dst), base); - MaybeRecordImplicitNullCheck(instruction); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } break; case Primitive::kPrimByte: __ Ldarb(Register(dst), base); - MaybeRecordImplicitNullCheck(instruction); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); break; case Primitive::kPrimChar: __ Ldarh(Register(dst), base); - MaybeRecordImplicitNullCheck(instruction); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } break; case Primitive::kPrimShort: __ Ldarh(Register(dst), base); - MaybeRecordImplicitNullCheck(instruction); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); break; case Primitive::kPrimInt: @@ -1375,7 +1440,9 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, case Primitive::kPrimLong: DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); __ Ldar(Register(dst), base); - MaybeRecordImplicitNullCheck(instruction); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } break; case Primitive::kPrimFloat: case Primitive::kPrimDouble: { @@ -1384,7 +1451,9 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW(); __ Ldar(temp, base); - MaybeRecordImplicitNullCheck(instruction); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } __ Fmov(FPRegister(dst), temp); break; } @@ -1505,7 +1574,7 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod __ Bind(slow_path->GetExitLabel()); } -void InstructionCodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { +void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) { BarrierType type = BarrierAll; switch (kind) { @@ -1641,33 +1710,62 @@ void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction) { void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); + LocationSummary* locations = instruction->GetLocations(); + Location base_loc = locations->InAt(0); + Location out = locations->Out(); + uint32_t offset = field_info.GetFieldOffset().Uint32Value(); Primitive::Type field_type = field_info.GetFieldType(); BlockPoolsScope block_pools(GetVIXLAssembler()); MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), field_info.GetFieldOffset()); bool use_acquire_release = codegen_->GetInstructionSetFeatures().PreferAcquireRelease(); - if (field_info.IsVolatile()) { - if (use_acquire_release) { - // NB: LoadAcquire will record the pc info if needed. - codegen_->LoadAcquire(instruction, OutputCPURegister(instruction), field); + if (field_type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { + // Object FieldGet with Baker's read barrier case. + MacroAssembler* masm = GetVIXLAssembler(); + UseScratchRegisterScope temps(masm); + // /* HeapReference<Object> */ out = *(base + offset) + Register base = RegisterFrom(base_loc, Primitive::kPrimNot); + Register temp = temps.AcquireW(); + // Note that potential implicit null checks are handled in this + // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier call. + codegen_->GenerateFieldLoadWithBakerReadBarrier( + instruction, + out, + base, + offset, + temp, + /* needs_null_check */ true, + field_info.IsVolatile() && use_acquire_release); + if (field_info.IsVolatile() && !use_acquire_release) { + // For IRIW sequential consistency kLoadAny is not sufficient. + codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); + } + } else { + // General case. + if (field_info.IsVolatile()) { + if (use_acquire_release) { + // Note that a potential implicit null check is handled in this + // CodeGeneratorARM64::LoadAcquire call. + // NB: LoadAcquire will record the pc info if needed. + codegen_->LoadAcquire( + instruction, OutputCPURegister(instruction), field, /* needs_null_check */ true); + } else { + codegen_->Load(field_type, OutputCPURegister(instruction), field); + codegen_->MaybeRecordImplicitNullCheck(instruction); + // For IRIW sequential consistency kLoadAny is not sufficient. + codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); + } } else { codegen_->Load(field_type, OutputCPURegister(instruction), field); codegen_->MaybeRecordImplicitNullCheck(instruction); - // For IRIW sequential consistency kLoadAny is not sufficient. - GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } - } else { - codegen_->Load(field_type, OutputCPURegister(instruction), field); - codegen_->MaybeRecordImplicitNullCheck(instruction); - } - - if (field_type == Primitive::kPrimNot) { - LocationSummary* locations = instruction->GetLocations(); - Location base = locations->InAt(0); - Location out = locations->Out(); - uint32_t offset = field_info.GetFieldOffset().Uint32Value(); - codegen_->MaybeGenerateReadBarrier(instruction, out, out, base, offset); + if (field_type == Primitive::kPrimNot) { + // If read barriers are enabled, emit read barriers other than + // Baker's using a slow path (and also unpoison the loaded + // reference, if heap poisoning is enabled). + codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset); + } } } @@ -1713,10 +1811,10 @@ void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction, codegen_->StoreRelease(field_type, source, HeapOperand(obj, offset)); codegen_->MaybeRecordImplicitNullCheck(instruction); } else { - GenerateMemoryBarrier(MemBarrierKind::kAnyStore); + codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore); codegen_->Store(field_type, source, HeapOperand(obj, offset)); codegen_->MaybeRecordImplicitNullCheck(instruction); - GenerateMemoryBarrier(MemBarrierKind::kAnyAny); + codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } } else { codegen_->Store(field_type, source, HeapOperand(obj, offset)); @@ -2021,50 +2119,62 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); Location index = locations->InAt(1); uint32_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value(); - MemOperand source = HeapOperand(obj); - CPURegister dest = OutputCPURegister(instruction); + Location out = locations->Out(); MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(masm); // Block pools between `Load` and `MaybeRecordImplicitNullCheck`. BlockPoolsScope block_pools(masm); - if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type); - source = HeapOperand(obj, offset); + if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { + // Object ArrayGet with Baker's read barrier case. + Register temp = temps.AcquireW(); + // The read barrier instrumentation does not support the + // HArm64IntermediateAddress instruction yet. + DCHECK(!instruction->GetArray()->IsArm64IntermediateAddress()); + // Note that a potential implicit null check is handled in the + // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call. + codegen_->GenerateArrayLoadWithBakerReadBarrier( + instruction, out, obj.W(), offset, index, temp, /* needs_null_check */ true); } else { - Register temp = temps.AcquireSameSizeAs(obj); - if (instruction->GetArray()->IsArm64IntermediateAddress()) { - // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. - DCHECK(!kEmitCompilerReadBarrier); - // We do not need to compute the intermediate address from the array: the - // input instruction has done it already. See the comment in - // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`. - if (kIsDebugBuild) { - HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress(); - DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset); - } - temp = obj; + // General case. + MemOperand source = HeapOperand(obj); + if (index.IsConstant()) { + offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type); + source = HeapOperand(obj, offset); } else { - __ Add(temp, obj, offset); + Register temp = temps.AcquireSameSizeAs(obj); + if (instruction->GetArray()->IsArm64IntermediateAddress()) { + // The read barrier instrumentation does not support the + // HArm64IntermediateAddress instruction yet. + DCHECK(!kEmitCompilerReadBarrier); + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress(); + DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset); + } + temp = obj; + } else { + __ Add(temp, obj, offset); + } + source = HeapOperand(temp, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type)); } - source = HeapOperand(temp, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type)); - } - codegen_->Load(type, dest, source); - codegen_->MaybeRecordImplicitNullCheck(instruction); + codegen_->Load(type, OutputCPURegister(instruction), source); + codegen_->MaybeRecordImplicitNullCheck(instruction); - if (type == Primitive::kPrimNot) { - static_assert( - sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), - "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - Location obj_loc = locations->InAt(0); - Location out = locations->Out(); - if (index.IsConstant()) { - codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset); - } else { - codegen_->MaybeGenerateReadBarrier(instruction, out, out, obj_loc, offset, index); + if (type == Primitive::kPrimNot) { + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); + Location obj_loc = locations->InAt(0); + if (index.IsConstant()) { + codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, obj_loc, offset); + } else { + codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, obj_loc, offset, index); + } } } } @@ -2194,12 +2304,12 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { // __ Mov(temp2, temp); // // /* HeapReference<Class> */ temp = temp->component_type_ // __ Ldr(temp, HeapOperand(temp, component_offset)); - // codegen_->GenerateReadBarrier( + // codegen_->GenerateReadBarrierSlow( // instruction, temp_loc, temp_loc, temp2_loc, component_offset); // // // /* HeapReference<Class> */ temp2 = value->klass_ // __ Ldr(temp2, HeapOperand(Register(value), class_offset)); - // codegen_->GenerateReadBarrier( + // codegen_->GenerateReadBarrierSlow( // instruction, temp2_loc, temp2_loc, value_loc, class_offset, temp_loc); // // __ Cmp(temp, temp2); @@ -2820,7 +2930,8 @@ void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruct non_fallthrough_target = true_target; } - if ((arm64_cond != gt && arm64_cond != le) && rhs.IsImmediate() && (rhs.immediate() == 0)) { + if ((arm64_cond == eq || arm64_cond == ne || arm64_cond == lt || arm64_cond == ge) && + rhs.IsImmediate() && (rhs.immediate() == 0)) { switch (arm64_cond) { case eq: __ Cbz(lhs, non_fallthrough_target); @@ -2921,6 +3032,14 @@ void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* ins HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } +static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { + return kEmitCompilerReadBarrier && + (kUseBakerReadBarrier || + type_check_kind == TypeCheckKind::kAbstractClassCheck || + type_check_kind == TypeCheckKind::kClassHierarchyCheck || + type_check_kind == TypeCheckKind::kArrayObjectCheck); +} + void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { LocationSummary::CallKind call_kind = LocationSummary::kNoCall; TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); @@ -2947,21 +3066,22 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); // When read barriers are enabled, we need a temporary register for // some cases. - if (kEmitCompilerReadBarrier && - (type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck)) { + if (TypeCheckNeedsATemporary(type_check_kind)) { locations->AddTemp(Location::RequiresRegister()); } } void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { + TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); Register cls = InputRegisterAt(instruction, 1); Location out_loc = locations->Out(); Register out = OutputRegister(instruction); + Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? + locations->GetTemp(0) : + Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); @@ -2977,10 +3097,9 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } // /* HeapReference<Class> */ out = obj->klass_ - __ Ldr(out, HeapOperand(obj.W(), class_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); - switch (instruction->GetTypeCheckKind()) { + switch (type_check_kind) { case TypeCheckKind::kExactCheck: { __ Cmp(out, cls); __ Cset(out, eq); @@ -2995,17 +3114,8 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { // object to avoid doing a comparison we know will fail. vixl::Label loop, success; __ Bind(&loop); - Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); - if (kEmitCompilerReadBarrier) { - // Save the value of `out` into `temp` before overwriting it - // in the following move operation, as we will need it for the - // read barrier below. - Register temp = WRegisterFrom(temp_loc); - __ Mov(temp, out); - } // /* HeapReference<Class> */ out = out->super_class_ - __ Ldr(out, HeapOperand(out, super_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. __ Cbz(out, &done); __ Cmp(out, cls); @@ -3023,17 +3133,8 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { __ Bind(&loop); __ Cmp(out, cls); __ B(eq, &success); - Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); - if (kEmitCompilerReadBarrier) { - // Save the value of `out` into `temp` before overwriting it - // in the following move operation, as we will need it for the - // read barrier below. - Register temp = WRegisterFrom(temp_loc); - __ Mov(temp, out); - } // /* HeapReference<Class> */ out = out->super_class_ - __ Ldr(out, HeapOperand(out, super_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, super_offset); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); __ Cbnz(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. __ B(&done); @@ -3051,17 +3152,8 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { __ Cmp(out, cls); __ B(eq, &exact_check); // Otherwise, we need to check that the object's class is a non-primitive array. - Location temp_loc = kEmitCompilerReadBarrier ? locations->GetTemp(0) : Location::NoLocation(); - if (kEmitCompilerReadBarrier) { - // Save the value of `out` into `temp` before overwriting it - // in the following move operation, as we will need it for the - // read barrier below. - Register temp = WRegisterFrom(temp_loc); - __ Mov(temp, out); - } // /* HeapReference<Class> */ out = out->component_type_ - __ Ldr(out, HeapOperand(out, component_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, out_loc, out_loc, temp_loc, component_offset); + GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); // If `out` is null, we use it for the result, and jump to `done`. __ Cbz(out, &done); __ Ldrh(out, HeapOperand(out, primitive_offset)); @@ -3100,6 +3192,13 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { // HInstanceOf instruction (following the runtime calling // convention), which might be cluttered by the potential first // read barrier emission at the beginning of this method. + // + // TODO: Introduce a new runtime entry point taking the object + // to test (instead of its class) as argument, and let it deal + // with the read barrier issues. This will let us refactor this + // case of the `switch` code as it was previously (with a direct + // call to the runtime not using a type checking slow path). + // This should also be beneficial for the other cases above. DCHECK(locations->OnlyCallsOnSlowPath()); slow_path = new (GetGraph()->GetArena()) TypeCheckSlowPathARM64(instruction, /* is_fatal */ false); @@ -3152,30 +3251,29 @@ void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { locations->SetInAt(1, Location::RequiresRegister()); // Note that TypeCheckSlowPathARM64 uses this "temp" register too. locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); // When read barriers are enabled, we need an additional temporary // register for some cases. - if (kEmitCompilerReadBarrier && - (type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck)) { - locations->AddTemp(Location::RequiresRegister()); + if (TypeCheckNeedsATemporary(type_check_kind)) { + locations->AddTemp(Location::RequiresRegister()); } } void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { + TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); LocationSummary* locations = instruction->GetLocations(); Location obj_loc = locations->InAt(0); Register obj = InputRegisterAt(instruction, 0); Register cls = InputRegisterAt(instruction, 1); Location temp_loc = locations->GetTemp(0); + Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? + locations->GetTemp(1) : + Location::NoLocation(); Register temp = WRegisterFrom(temp_loc); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); - TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); bool is_type_check_slow_path_fatal = (type_check_kind == TypeCheckKind::kExactCheck || type_check_kind == TypeCheckKind::kAbstractClassCheck || @@ -3194,8 +3292,7 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - __ Ldr(temp, HeapOperand(obj, class_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); switch (type_check_kind) { case TypeCheckKind::kExactCheck: @@ -3212,18 +3309,8 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { // object to avoid doing a comparison we know will fail. vixl::Label loop, compare_classes; __ Bind(&loop); - Location temp2_loc = - kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); - if (kEmitCompilerReadBarrier) { - // Save the value of `temp` into `temp2` before overwriting it - // in the following move operation, as we will need it for the - // read barrier below. - Register temp2 = WRegisterFrom(temp2_loc); - __ Mov(temp2, temp); - } // /* HeapReference<Class> */ temp = temp->super_class_ - __ Ldr(temp, HeapOperand(temp, super_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // to the `compare_classes` label to compare it with the checked @@ -3235,8 +3322,8 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - __ Ldr(temp, HeapOperand(obj, class_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ B(type_check_slow_path->GetEntryLabel()); __ Bind(&compare_classes); @@ -3252,18 +3339,8 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ Cmp(temp, cls); __ B(eq, &done); - Location temp2_loc = - kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); - if (kEmitCompilerReadBarrier) { - // Save the value of `temp` into `temp2` before overwriting it - // in the following move operation, as we will need it for the - // read barrier below. - Register temp2 = WRegisterFrom(temp2_loc); - __ Mov(temp2, temp); - } // /* HeapReference<Class> */ temp = temp->super_class_ - __ Ldr(temp, HeapOperand(temp, super_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, temp2_loc, super_offset); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // back at the beginning of the loop. @@ -3274,8 +3351,8 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - __ Ldr(temp, HeapOperand(obj, class_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ B(type_check_slow_path->GetEntryLabel()); break; } @@ -3287,19 +3364,8 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ B(eq, &done); // Otherwise, we need to check that the object's class is a non-primitive array. - Location temp2_loc = - kEmitCompilerReadBarrier ? locations->GetTemp(1) : Location::NoLocation(); - if (kEmitCompilerReadBarrier) { - // Save the value of `temp` into `temp2` before overwriting it - // in the following move operation, as we will need it for the - // read barrier below. - Register temp2 = WRegisterFrom(temp2_loc); - __ Mov(temp2, temp); - } // /* HeapReference<Class> */ temp = temp->component_type_ - __ Ldr(temp, HeapOperand(temp, component_offset)); - codegen_->MaybeGenerateReadBarrier( - instruction, temp_loc, temp_loc, temp2_loc, component_offset); + GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); // If the component type is not null (i.e. the object is indeed // an array), jump to label `check_non_primitive_component_type` @@ -3312,8 +3378,8 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - __ Ldr(temp, HeapOperand(obj, class_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ B(type_check_slow_path->GetEntryLabel()); __ Bind(&check_non_primitive_component_type); @@ -3322,8 +3388,8 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { __ Cbz(temp, &done); // Same comment as above regarding `temp` and the slow path. // /* HeapReference<Class> */ temp = obj->klass_ - __ Ldr(temp, HeapOperand(obj, class_offset)); - codegen_->MaybeGenerateReadBarrier(instruction, temp_loc, temp_loc, obj_loc, class_offset); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ B(type_check_slow_path->GetEntryLabel()); break; } @@ -3340,6 +3406,13 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { // instruction (following the runtime calling convention), which // might be cluttered by the potential first read barrier // emission at the beginning of this method. + // + // TODO: Introduce a new runtime entry point taking the object + // to test (instead of its class) as argument, and let it deal + // with the read barrier issues. This will let us refactor this + // case of the `switch` code as it was previously (with a direct + // call to the runtime not using a type checking slow path). + // This should also be beneficial for the other cases above. __ B(type_check_slow_path->GetEntryLabel()); break; } @@ -3465,7 +3538,7 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM64* codege HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, MethodReference target_method ATTRIBUTE_UNUSED) { - // On arm64 we support all dispatch types. + // On ARM64 we support all dispatch types. return desired_dispatch_info; } @@ -3742,32 +3815,17 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { if (cls->IsReferrersClass()) { DCHECK(!cls->CanCallRuntime()); DCHECK(!cls->MustGenerateClinitCheck()); - uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value(); - if (kEmitCompilerReadBarrier) { - // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_) - __ Add(out.X(), current_method.X(), declaring_class_offset); - // /* mirror::Class* */ out = out->Read() - codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc); - } else { - // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ - __ Ldr(out, MemOperand(current_method, declaring_class_offset)); - } + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + GenerateGcRootFieldLoad( + cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); } else { MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize); // /* GcRoot<mirror::Class>[] */ out = // current_method.ptr_sized_fields_->dex_cache_resolved_types_ __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value())); - - size_t cache_offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); - if (kEmitCompilerReadBarrier) { - // /* GcRoot<mirror::Class>* */ out = &out[type_index] - __ Add(out.X(), out.X(), cache_offset); - // /* mirror::Class* */ out = out->Read() - codegen_->GenerateReadBarrierForRoot(cls, out_loc, out_loc); - } else { - // /* GcRoot<mirror::Class> */ out = out[type_index] - __ Ldr(out, MemOperand(out.X(), cache_offset)); - } + // /* GcRoot<mirror::Class> */ out = out[type_index] + GenerateGcRootFieldLoad( + cls, out_loc, out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { DCHECK(cls->CanCallRuntime()); @@ -3830,30 +3888,14 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { Register out = OutputRegister(load); Register current_method = InputRegisterAt(load, 0); - uint32_t declaring_class_offset = ArtMethod::DeclaringClassOffset().Int32Value(); - if (kEmitCompilerReadBarrier) { - // /* GcRoot<mirror::Class>* */ out = &(current_method->declaring_class_) - __ Add(out.X(), current_method.X(), declaring_class_offset); - // /* mirror::Class* */ out = out->Read() - codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc); - } else { - // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ - __ Ldr(out, MemOperand(current_method, declaring_class_offset)); - } - + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + GenerateGcRootFieldLoad( + load, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); // /* GcRoot<mirror::String>[] */ out = out->dex_cache_strings_ __ Ldr(out.X(), HeapOperand(out, mirror::Class::DexCacheStringsOffset().Uint32Value())); - - size_t cache_offset = CodeGenerator::GetCacheOffset(load->GetStringIndex()); - if (kEmitCompilerReadBarrier) { - // /* GcRoot<mirror::String>* */ out = &out[string_index] - __ Add(out.X(), out.X(), cache_offset); - // /* mirror::String* */ out = out->Read() - codegen_->GenerateReadBarrierForRoot(load, out_loc, out_loc); - } else { - // /* GcRoot<mirror::String> */ out = out[string_index] - __ Ldr(out, MemOperand(out.X(), cache_offset)); - } + // /* GcRoot<mirror::String> */ out = out[string_index] + GenerateGcRootFieldLoad( + load, out_loc, out.X(), CodeGenerator::GetCacheOffset(load->GetStringIndex())); if (!load->IsInDexCache()) { SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathARM64(load); @@ -4222,7 +4264,7 @@ void LocationsBuilderARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { } void InstructionCodeGeneratorARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) { - GenerateMemoryBarrier(memory_barrier->GetBarrierKind()); + codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind()); } void LocationsBuilderARM64::VisitReturn(HReturn* instruction) { @@ -4607,14 +4649,288 @@ void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_inst } } -void CodeGeneratorARM64::GenerateReadBarrier(HInstruction* instruction, - Location out, - Location ref, - Location obj, - uint32_t offset, - Location index) { +void InstructionCodeGeneratorARM64::GenerateReferenceLoadOneRegister(HInstruction* instruction, + Location out, + uint32_t offset, + Location maybe_temp) { + Primitive::Type type = Primitive::kPrimNot; + Register out_reg = RegisterFrom(out, type); + if (kEmitCompilerReadBarrier) { + Register temp_reg = RegisterFrom(maybe_temp, type); + if (kUseBakerReadBarrier) { + // Load with fast path based Baker's read barrier. + // /* HeapReference<Object> */ out = *(out + offset) + codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, + out, + out_reg, + offset, + temp_reg, + /* needs_null_check */ false, + /* use_load_acquire */ false); + } else { + // Load with slow path based read barrier. + // Save the value of `out` into `maybe_temp` before overwriting it + // in the following move operation, as we will need it for the + // read barrier below. + __ Mov(temp_reg, out_reg); + // /* HeapReference<Object> */ out = *(out + offset) + __ Ldr(out_reg, HeapOperand(out_reg, offset)); + codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); + } + } else { + // Plain load with no read barrier. + // /* HeapReference<Object> */ out = *(out + offset) + __ Ldr(out_reg, HeapOperand(out_reg, offset)); + GetAssembler()->MaybeUnpoisonHeapReference(out_reg); + } +} + +void InstructionCodeGeneratorARM64::GenerateReferenceLoadTwoRegisters(HInstruction* instruction, + Location out, + Location obj, + uint32_t offset, + Location maybe_temp) { + Primitive::Type type = Primitive::kPrimNot; + Register out_reg = RegisterFrom(out, type); + Register obj_reg = RegisterFrom(obj, type); + if (kEmitCompilerReadBarrier) { + if (kUseBakerReadBarrier) { + // Load with fast path based Baker's read barrier. + Register temp_reg = RegisterFrom(maybe_temp, type); + // /* HeapReference<Object> */ out = *(obj + offset) + codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction, + out, + obj_reg, + offset, + temp_reg, + /* needs_null_check */ false, + /* use_load_acquire */ false); + } else { + // Load with slow path based read barrier. + // /* HeapReference<Object> */ out = *(obj + offset) + __ Ldr(out_reg, HeapOperand(obj_reg, offset)); + codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset); + } + } else { + // Plain load with no read barrier. + // /* HeapReference<Object> */ out = *(obj + offset) + __ Ldr(out_reg, HeapOperand(obj_reg, offset)); + GetAssembler()->MaybeUnpoisonHeapReference(out_reg); + } +} + +void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::Register obj, + uint32_t offset) { + Register root_reg = RegisterFrom(root, Primitive::kPrimNot); + if (kEmitCompilerReadBarrier) { + if (kUseBakerReadBarrier) { + // Fast path implementation of art::ReadBarrier::BarrierForRoot when + // Baker's read barrier are used: + // + // root = obj.field; + // if (Thread::Current()->GetIsGcMarking()) { + // root = ReadBarrier::Mark(root) + // } + + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ Ldr(root_reg, MemOperand(obj, offset)); + static_assert( + sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>), + "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> " + "have different sizes."); + static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::CompressedReference<mirror::Object> and int32_t " + "have different sizes."); + + // Slow path used to mark the GC root `root`. + SlowPathCodeARM64* slow_path = + new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(instruction, root, root); + codegen_->AddSlowPath(slow_path); + + MacroAssembler* masm = GetVIXLAssembler(); + UseScratchRegisterScope temps(masm); + Register temp = temps.AcquireW(); + // temp = Thread::Current()->GetIsGcMarking() + __ Ldr(temp, MemOperand(tr, Thread::IsGcMarkingOffset<kArm64WordSize>().Int32Value())); + __ Cbnz(temp, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); + } else { + // GC root loaded through a slow path for read barriers other + // than Baker's. + // /* GcRoot<mirror::Object>* */ root = obj + offset + __ Add(root_reg.X(), obj.X(), offset); + // /* mirror::Object* */ root = root->Read() + codegen_->GenerateReadBarrierForRootSlow(instruction, root, root); + } + } else { + // Plain GC root load with no read barrier. + // /* GcRoot<mirror::Object> */ root = *(obj + offset) + __ Ldr(root_reg, MemOperand(obj, offset)); + // Note that GC roots are not affected by heap poisoning, thus we + // do not have to unpoison `root_reg` here. + } +} + +void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t offset, + Register temp, + bool needs_null_check, + bool use_load_acquire) { + DCHECK(kEmitCompilerReadBarrier); + DCHECK(kUseBakerReadBarrier); + + // /* HeapReference<Object> */ ref = *(obj + offset) + Location no_index = Location::NoLocation(); + GenerateReferenceLoadWithBakerReadBarrier( + instruction, ref, obj, offset, no_index, temp, needs_null_check, use_load_acquire); +} + +void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t data_offset, + Location index, + Register temp, + bool needs_null_check) { + DCHECK(kEmitCompilerReadBarrier); + DCHECK(kUseBakerReadBarrier); + + // Array cells are never volatile variables, therefore array loads + // never use Load-Acquire instructions on ARM64. + const bool use_load_acquire = false; + + // /* HeapReference<Object> */ ref = + // *(obj + data_offset + index * sizeof(HeapReference<Object>)) + GenerateReferenceLoadWithBakerReadBarrier( + instruction, ref, obj, data_offset, index, temp, needs_null_check, use_load_acquire); +} + +void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t offset, + Location index, + Register temp, + bool needs_null_check, + bool use_load_acquire) { + DCHECK(kEmitCompilerReadBarrier); + DCHECK(kUseBakerReadBarrier); + // If `index` is a valid location, then we are emitting an array + // load, so we shouldn't be using a Load Acquire instruction. + // In other words: `index.IsValid()` => `!use_load_acquire`. + DCHECK(!index.IsValid() || !use_load_acquire); + + MacroAssembler* masm = GetVIXLAssembler(); + UseScratchRegisterScope temps(masm); + + // In slow path based read barriers, the read barrier call is + // inserted after the original load. However, in fast path based + // Baker's read barriers, we need to perform the load of + // mirror::Object::monitor_ *before* the original reference load. + // This load-load ordering is required by the read barrier. + // The fast path/slow path (for Baker's algorithm) should look like: + // + // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState(); + // lfence; // Load fence or artificial data dependency to prevent load-load reordering + // HeapReference<Object> ref = *src; // Original reference load. + // bool is_gray = (rb_state == ReadBarrier::gray_ptr_); + // if (is_gray) { + // ref = ReadBarrier::Mark(ref); // Performed by runtime entrypoint slow path. + // } + // + // Note: the original implementation in ReadBarrier::Barrier is + // slightly more complex as it performs additional checks that we do + // not do here for performance reasons. + + Primitive::Type type = Primitive::kPrimNot; + Register ref_reg = RegisterFrom(ref, type); + DCHECK(obj.IsW()); + uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); + + // /* int32_t */ monitor = obj->monitor_ + __ Ldr(temp, HeapOperand(obj, monitor_offset)); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + // /* LockWord */ lock_word = LockWord(monitor) + static_assert(sizeof(LockWord) == sizeof(int32_t), + "art::LockWord and int32_t have different sizes."); + // /* uint32_t */ rb_state = lock_word.ReadBarrierState() + __ Lsr(temp, temp, LockWord::kReadBarrierStateShift); + __ And(temp, temp, Operand(LockWord::kReadBarrierStateMask)); + static_assert( + LockWord::kReadBarrierStateMask == ReadBarrier::rb_ptr_mask_, + "art::LockWord::kReadBarrierStateMask is not equal to art::ReadBarrier::rb_ptr_mask_."); + + // Introduce a dependency on the high bits of rb_state, which shall + // be all zeroes, to prevent load-load reordering, and without using + // a memory barrier (which would be more expensive). + // temp2 = rb_state & ~LockWord::kReadBarrierStateMask = 0 + Register temp2 = temps.AcquireW(); + __ Bic(temp2, temp, Operand(LockWord::kReadBarrierStateMask)); + // obj is unchanged by this operation, but its value now depends on + // temp2, which depends on temp. + __ Add(obj, obj, Operand(temp2)); + temps.Release(temp2); + + // The actual reference load. + if (index.IsValid()) { + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); + temp2 = temps.AcquireW(); + // /* HeapReference<Object> */ ref = + // *(obj + offset + index * sizeof(HeapReference<Object>)) + MemOperand source = HeapOperand(obj); + if (index.IsConstant()) { + uint32_t computed_offset = + offset + (Int64ConstantFrom(index) << Primitive::ComponentSizeShift(type)); + source = HeapOperand(obj, computed_offset); + } else { + __ Add(temp2, obj, offset); + source = HeapOperand(temp2, XRegisterFrom(index), LSL, Primitive::ComponentSizeShift(type)); + } + Load(type, ref_reg, source); + temps.Release(temp2); + } else { + // /* HeapReference<Object> */ ref = *(obj + offset) + MemOperand field = HeapOperand(obj, offset); + if (use_load_acquire) { + LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false); + } else { + Load(type, ref_reg, field); + } + } + + // Object* ref = ref_addr->AsMirrorPtr() + GetAssembler()->MaybeUnpoisonHeapReference(ref_reg); + + // Slow path used to mark the object `ref` when it is gray. + SlowPathCodeARM64* slow_path = + new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(instruction, ref, ref); + AddSlowPath(slow_path); + + // if (rb_state == ReadBarrier::gray_ptr_) + // ref = ReadBarrier::Mark(ref); + __ Cmp(temp, ReadBarrier::gray_ptr_); + __ B(eq, slow_path->GetEntryLabel()); + __ Bind(slow_path->GetExitLabel()); +} + +void CodeGeneratorARM64::GenerateReadBarrierSlow(HInstruction* instruction, + Location out, + Location ref, + Location obj, + uint32_t offset, + Location index) { DCHECK(kEmitCompilerReadBarrier); + // Insert a slow path based read barrier *after* the reference load. + // // If heap poisoning is enabled, the unpoisoning of the loaded // reference will be carried out by the runtime within the slow // path. @@ -4628,57 +4944,41 @@ void CodeGeneratorARM64::GenerateReadBarrier(HInstruction* instruction, ReadBarrierForHeapReferenceSlowPathARM64(instruction, out, ref, obj, offset, index); AddSlowPath(slow_path); - // TODO: When read barrier has a fast path, add it here. - /* Currently the read barrier call is inserted after the original load. - * However, if we have a fast path, we need to perform the load of obj.LockWord *before* the - * original load. This load-load ordering is required by the read barrier. - * The fast path/slow path (for Baker's algorithm) should look like: - * - * bool isGray = obj.LockWord & kReadBarrierMask; - * lfence; // load fence or artificial data dependence to prevent load-load reordering - * ref = obj.field; // this is the original load - * if (isGray) { - * ref = Mark(ref); // ideally the slow path just does Mark(ref) - * } - */ - __ B(slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } -void CodeGeneratorARM64::MaybeGenerateReadBarrier(HInstruction* instruction, - Location out, - Location ref, - Location obj, - uint32_t offset, - Location index) { +void CodeGeneratorARM64::MaybeGenerateReadBarrierSlow(HInstruction* instruction, + Location out, + Location ref, + Location obj, + uint32_t offset, + Location index) { if (kEmitCompilerReadBarrier) { + // Baker's read barriers shall be handled by the fast path + // (CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier). + DCHECK(!kUseBakerReadBarrier); // If heap poisoning is enabled, unpoisoning will be taken care of // by the runtime within the slow path. - GenerateReadBarrier(instruction, out, ref, obj, offset, index); + GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index); } else if (kPoisonHeapReferences) { GetAssembler()->UnpoisonHeapReference(WRegisterFrom(out)); } } -void CodeGeneratorARM64::GenerateReadBarrierForRoot(HInstruction* instruction, - Location out, - Location root) { +void CodeGeneratorARM64::GenerateReadBarrierForRootSlow(HInstruction* instruction, + Location out, + Location root) { DCHECK(kEmitCompilerReadBarrier); + // Insert a slow path based read barrier *after* the GC root load. + // // Note that GC roots are not affected by heap poisoning, so we do // not need to do anything special for this here. SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) ReadBarrierForRootSlowPathARM64(instruction, out, root); AddSlowPath(slow_path); - // TODO: Implement a fast path for ReadBarrierForRoot, performing - // the following operation (for Baker's algorithm): - // - // if (thread.tls32_.is_gc_marking) { - // root = Mark(root); - // } - __ B(slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 8eb9fcc558..a9d1bbde98 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -208,14 +208,53 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { private: void GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path, vixl::Register class_reg); - void GenerateMemoryBarrier(MemBarrierKind kind); void GenerateSuspendCheck(HSuspendCheck* instruction, HBasicBlock* successor); void HandleBinaryOp(HBinaryOperation* instr); + void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); void HandleCondition(HCondition* instruction); + + // Generate a heap reference load using one register `out`: + // + // out <- *(out + offset) + // + // while honoring heap poisoning and/or read barriers (if any). + // + // Location `maybe_temp` is used when generating a read barrier and + // shall be a register in that case; it may be an invalid location + // otherwise. + void GenerateReferenceLoadOneRegister(HInstruction* instruction, + Location out, + uint32_t offset, + Location maybe_temp); + // Generate a heap reference load using two different registers + // `out` and `obj`: + // + // out <- *(obj + offset) + // + // while honoring heap poisoning and/or read barriers (if any). + // + // Location `maybe_temp` is used when generating a Baker's (fast + // path) read barrier and shall be a register in that case; it may + // be an invalid location otherwise. + void GenerateReferenceLoadTwoRegisters(HInstruction* instruction, + Location out, + Location obj, + uint32_t offset, + Location maybe_temp); + // Generate a GC root reference load: + // + // root <- *(obj + offset) + // + // while honoring read barriers (if any). + void GenerateGcRootFieldLoad(HInstruction* instruction, + Location root, + vixl::Register obj, + uint32_t offset); + void HandleShift(HBinaryOperation* instr); void GenerateImplicitNullCheck(HNullCheck* instruction); void GenerateExplicitNullCheck(HNullCheck* instruction); @@ -337,6 +376,8 @@ class CodeGeneratorARM64 : public CodeGenerator { // Emit a write barrier. void MarkGCCard(vixl::Register object, vixl::Register value, bool value_can_be_null); + void GenerateMemoryBarrier(MemBarrierKind kind); + // Register allocation. void SetupBlockedRegisters() const OVERRIDE; @@ -386,9 +427,12 @@ class CodeGeneratorARM64 : public CodeGenerator { void AddLocationAsTemp(Location location, LocationSummary* locations) OVERRIDE; void Load(Primitive::Type type, vixl::CPURegister dst, const vixl::MemOperand& src); - void Store(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst); - void LoadAcquire(HInstruction* instruction, vixl::CPURegister dst, const vixl::MemOperand& src); - void StoreRelease(Primitive::Type type, vixl::CPURegister rt, const vixl::MemOperand& dst); + void Store(Primitive::Type type, vixl::CPURegister src, const vixl::MemOperand& dst); + void LoadAcquire(HInstruction* instruction, + vixl::CPURegister dst, + const vixl::MemOperand& src, + bool needs_null_check); + void StoreRelease(Primitive::Type type, vixl::CPURegister src, const vixl::MemOperand& dst); // Generate code to invoke a runtime entry point. void InvokeRuntime(QuickEntrypointEnum entrypoint, @@ -423,7 +467,27 @@ class CodeGeneratorARM64 : public CodeGenerator { void EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patches) OVERRIDE; - // Generate a read barrier for a heap reference within `instruction`. + // Fast path implementation of ReadBarrier::Barrier for a heap + // reference field load when Baker's read barriers are used. + void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t offset, + vixl::Register temp, + bool needs_null_check, + bool use_load_acquire); + // Fast path implementation of ReadBarrier::Barrier for a heap + // reference array load when Baker's read barriers are used. + void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t data_offset, + Location index, + vixl::Register temp, + bool needs_null_check); + + // Generate a read barrier for a heap reference within `instruction` + // using a slow path. // // A read barrier for an object reference read from the heap is // implemented as a call to the artReadBarrierSlow runtime entry @@ -440,23 +504,25 @@ class CodeGeneratorARM64 : public CodeGenerator { // When `index` is provided (i.e. for array accesses), the offset // value passed to artReadBarrierSlow is adjusted to take `index` // into account. - void GenerateReadBarrier(HInstruction* instruction, - Location out, - Location ref, - Location obj, - uint32_t offset, - Location index = Location::NoLocation()); - - // If read barriers are enabled, generate a read barrier for a heap reference. - // If heap poisoning is enabled, also unpoison the reference in `out`. - void MaybeGenerateReadBarrier(HInstruction* instruction, - Location out, - Location ref, - Location obj, - uint32_t offset, - Location index = Location::NoLocation()); - - // Generate a read barrier for a GC root within `instruction`. + void GenerateReadBarrierSlow(HInstruction* instruction, + Location out, + Location ref, + Location obj, + uint32_t offset, + Location index = Location::NoLocation()); + + // If read barriers are enabled, generate a read barrier for a heap + // reference using a slow path. If heap poisoning is enabled, also + // unpoison the reference in `out`. + void MaybeGenerateReadBarrierSlow(HInstruction* instruction, + Location out, + Location ref, + Location obj, + uint32_t offset, + Location index = Location::NoLocation()); + + // Generate a read barrier for a GC root within `instruction` using + // a slow path. // // A read barrier for an object reference GC root is implemented as // a call to the artReadBarrierForRootSlow runtime entry point, @@ -466,9 +532,20 @@ class CodeGeneratorARM64 : public CodeGenerator { // // The `out` location contains the value returned by // artReadBarrierForRootSlow. - void GenerateReadBarrierForRoot(HInstruction* instruction, Location out, Location root); + void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root); private: + // Factored implementation of GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t offset, + Location index, + vixl::Register temp, + bool needs_null_check, + bool use_load_acquire); + using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, vixl::Literal<uint64_t>*>; using MethodToLiteralMap = ArenaSafeMap<MethodReference, vixl::Literal<uint64_t>*, diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 05054867fe..e3115f416a 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -964,11 +964,15 @@ Location CodeGeneratorMIPS64::GetStackLocation(HLoadLocal* load) const { return Location::NoLocation(); } -void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object, GpuRegister value) { +void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object, + GpuRegister value, + bool value_can_be_null) { Mips64Label done; GpuRegister card = AT; GpuRegister temp = TMP; - __ Beqzc(value, &done); + if (value_can_be_null) { + __ Beqzc(value, &done); + } __ LoadFromOffset(kLoadDoubleword, card, TR, @@ -976,7 +980,9 @@ void CodeGeneratorMIPS64::MarkGCCard(GpuRegister object, GpuRegister value) { __ Dsrl(temp, object, gc::accounting::CardTable::kCardShift); __ Daddu(temp, card, temp); __ Sb(card, temp, 0); - __ Bind(&done); + if (value_can_be_null) { + __ Bind(&done); + } } void CodeGeneratorMIPS64::SetupBlockedRegisters() const { @@ -1601,7 +1607,7 @@ void InstructionCodeGeneratorMIPS64::VisitArraySet(HArraySet* instruction) { codegen_->MaybeRecordImplicitNullCheck(instruction); if (needs_write_barrier) { DCHECK_EQ(value_type, Primitive::kPrimNot); - codegen_->MarkGCCard(obj, value); + codegen_->MarkGCCard(obj, value, instruction->GetValueCanBeNull()); } } else { DCHECK_EQ(value_type, Primitive::kPrimNot); @@ -2827,7 +2833,8 @@ void LocationsBuilderMIPS64::HandleFieldSet(HInstruction* instruction, } void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction, - const FieldInfo& field_info) { + const FieldInfo& field_info, + bool value_can_be_null) { Primitive::Type type = field_info.GetFieldType(); LocationSummary* locations = instruction->GetLocations(); GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); @@ -2869,7 +2876,7 @@ void InstructionCodeGeneratorMIPS64::HandleFieldSet(HInstruction* instruction, if (CodeGenerator::StoreNeedsWriteBarrier(type, instruction->InputAt(1))) { DCHECK(locations->InAt(1).IsRegister()); GpuRegister src = locations->InAt(1).AsRegister<GpuRegister>(); - codegen_->MarkGCCard(obj, src); + codegen_->MarkGCCard(obj, src, value_can_be_null); } } @@ -2886,7 +2893,7 @@ void LocationsBuilderMIPS64::VisitInstanceFieldSet(HInstanceFieldSet* instructio } void InstructionCodeGeneratorMIPS64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { - HandleFieldSet(instruction, instruction->GetFieldInfo()); + HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) { @@ -3810,7 +3817,7 @@ void LocationsBuilderMIPS64::VisitStaticFieldSet(HStaticFieldSet* instruction) { } void InstructionCodeGeneratorMIPS64::VisitStaticFieldSet(HStaticFieldSet* instruction) { - HandleFieldSet(instruction, instruction->GetFieldInfo()); + HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } void LocationsBuilderMIPS64::VisitUnresolvedInstanceFieldGet( diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 140ff95f14..08e56158b8 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -227,7 +227,9 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { void HandleBinaryOp(HBinaryOperation* operation); void HandleCondition(HCondition* instruction); void HandleShift(HBinaryOperation* operation); - void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); + void HandleFieldSet(HInstruction* instruction, + const FieldInfo& field_info, + bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); void GenerateImplicitNullCheck(HNullCheck* instruction); void GenerateExplicitNullCheck(HNullCheck* instruction); @@ -285,7 +287,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator { Mips64Assembler* GetAssembler() OVERRIDE { return &assembler_; } const Mips64Assembler& GetAssembler() const OVERRIDE { return assembler_; } - void MarkGCCard(GpuRegister object, GpuRegister value); + void MarkGCCard(GpuRegister object, GpuRegister value, bool value_can_be_null); // Register allocation. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index f7ccdd8b8f..50c4ba23c5 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -5878,7 +5878,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { Location cls = locations->InAt(1); Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); - Location temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(0) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); @@ -5896,7 +5896,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } // /* HeapReference<Class> */ out = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, temp_loc); + GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); switch (type_check_kind) { case TypeCheckKind::kExactCheck: { @@ -5920,7 +5920,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { NearLabel loop; __ Bind(&loop); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -5950,7 +5950,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } __ j(kEqual, &success); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); __ testl(out, out); __ j(kNotEqual, &loop); // If `out` is null, we use it for the result, and jump to `done`. @@ -5975,7 +5975,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { __ j(kEqual, &exact_check); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ out = out->component_type_ - GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -6091,7 +6091,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); Register temp = temp_loc.AsRegister<Register>(); - Location temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(1) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); @@ -6118,7 +6118,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); switch (type_check_kind) { case TypeCheckKind::kExactCheck: @@ -6141,7 +6141,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { NearLabel loop, compare_classes; __ Bind(&loop); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // to the `compare_classes` label to compare it with the checked @@ -6154,7 +6154,8 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&compare_classes); @@ -6181,7 +6182,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ j(kEqual, &done); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // back at the beginning of the loop. @@ -6193,7 +6194,8 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); break; } @@ -6211,7 +6213,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); // If the component type is not null (i.e. the object is indeed // an array), jump to label `check_non_primitive_component_type` @@ -6225,7 +6227,8 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&check_non_primitive_component_type); @@ -6233,7 +6236,8 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { __ j(kEqual, &done); // Same comment as above regarding `temp` and the slow path. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); break; } @@ -6421,23 +6425,24 @@ void InstructionCodeGeneratorX86::HandleBitwiseOperation(HBinaryOperation* instr void InstructionCodeGeneratorX86::GenerateReferenceLoadOneRegister(HInstruction* instruction, Location out, uint32_t offset, - Location temp) { + Location maybe_temp) { Register out_reg = out.AsRegister<Register>(); if (kEmitCompilerReadBarrier) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; if (kUseBakerReadBarrier) { // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(out + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, out_reg, offset, temp, /* needs_null_check */ false); + instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); } else { // Load with slow path based read barrier. - // Save the value of `out` into `temp` before overwriting it + // Save the value of `out` into `maybe_temp` before overwriting it // in the following move operation, as we will need it for the // read barrier below. - __ movl(temp.AsRegister<Register>(), out_reg); + __ movl(maybe_temp.AsRegister<Register>(), out_reg); // /* HeapReference<Object> */ out = *(out + offset) __ movl(out_reg, Address(out_reg, offset)); - codegen_->GenerateReadBarrierSlow(instruction, out, out, temp, offset); + codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); } } else { // Plain load with no read barrier. @@ -6451,15 +6456,16 @@ void InstructionCodeGeneratorX86::GenerateReferenceLoadTwoRegisters(HInstruction Location out, Location obj, uint32_t offset, - Location temp) { + Location maybe_temp) { Register out_reg = out.AsRegister<Register>(); Register obj_reg = obj.AsRegister<Register>(); if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(obj + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, obj_reg, offset, temp, /* needs_null_check */ false); + instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); } else { // Load with slow path based read barrier. // /* HeapReference<Object> */ out = *(obj + offset) diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 43e9543e41..0aef478569 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -233,23 +233,29 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { // out <- *(out + offset) // // while honoring heap poisoning and/or read barriers (if any). - // Register `temp` is used when generating a read barrier. + // + // Location `maybe_temp` is used when generating a read barrier and + // shall be a register in that case; it may be an invalid location + // otherwise. void GenerateReferenceLoadOneRegister(HInstruction* instruction, Location out, uint32_t offset, - Location temp); + Location maybe_temp); // Generate a heap reference load using two different registers // `out` and `obj`: // // out <- *(obj + offset) // // while honoring heap poisoning and/or read barriers (if any). - // Register `temp` is used when generating a Baker's read barrier. + // + // Location `maybe_temp` is used when generating a Baker's (fast + // path) read barrier and shall be a register in that case; it may + // be an invalid location otherwise. void GenerateReferenceLoadTwoRegisters(HInstruction* instruction, Location out, Location obj, uint32_t offset, - Location temp); + Location maybe_temp); // Generate a GC root reference load: // // root <- *(obj + offset) diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 2ce2d91502..fd188346e4 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5490,7 +5490,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { Location cls = locations->InAt(1); Location out_loc = locations->Out(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); - Location temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp_loc = TypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(0) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); @@ -5508,7 +5508,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } // /* HeapReference<Class> */ out = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, temp_loc); + GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc); switch (type_check_kind) { case TypeCheckKind::kExactCheck: { @@ -5537,7 +5537,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { NearLabel loop, success; __ Bind(&loop); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -5567,7 +5567,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } __ j(kEqual, &success); // /* HeapReference<Class> */ out = out->super_class_ - GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, super_offset, maybe_temp_loc); __ testl(out, out); __ j(kNotEqual, &loop); // If `out` is null, we use it for the result, and jump to `done`. @@ -5592,7 +5592,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { __ j(kEqual, &exact_check); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ out = out->component_type_ - GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, temp_loc); + GenerateReferenceLoadOneRegister(instruction, out_loc, component_offset, maybe_temp_loc); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -5708,7 +5708,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { Location cls = locations->InAt(1); Location temp_loc = locations->GetTemp(0); CpuRegister temp = temp_loc.AsRegister<CpuRegister>(); - Location temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? + Location maybe_temp2_loc = TypeCheckNeedsATemporary(type_check_kind) ? locations->GetTemp(1) : Location::NoLocation(); uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); @@ -5735,7 +5735,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { } // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); switch (type_check_kind) { case TypeCheckKind::kExactCheck: @@ -5758,7 +5758,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { NearLabel loop, compare_classes; __ Bind(&loop); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // to the `compare_classes` label to compare it with the checked @@ -5771,7 +5771,8 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&compare_classes); @@ -5798,7 +5799,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { __ j(kEqual, &done); // /* HeapReference<Class> */ temp = temp->super_class_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, super_offset, maybe_temp2_loc); // If the class reference currently in `temp` is not null, jump // back at the beginning of the loop. @@ -5810,7 +5811,8 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); break; } @@ -5828,7 +5830,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ - GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, temp2_loc); + GenerateReferenceLoadOneRegister(instruction, temp_loc, component_offset, maybe_temp2_loc); // If the component type is not null (i.e. the object is indeed // an array), jump to label `check_non_primitive_component_type` @@ -5842,7 +5844,8 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { // going into the slow path, as it has been overwritten in the // meantime. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); __ Bind(&check_non_primitive_component_type); @@ -5850,7 +5853,8 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { __ j(kEqual, &done); // Same comment as above regarding `temp` and the slow path. // /* HeapReference<Class> */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, temp2_loc); + GenerateReferenceLoadTwoRegisters( + instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc); __ jmp(type_check_slow_path->GetEntryLabel()); break; } @@ -6020,23 +6024,24 @@ void InstructionCodeGeneratorX86_64::HandleBitwiseOperation(HBinaryOperation* in void InstructionCodeGeneratorX86_64::GenerateReferenceLoadOneRegister(HInstruction* instruction, Location out, uint32_t offset, - Location temp) { + Location maybe_temp) { CpuRegister out_reg = out.AsRegister<CpuRegister>(); if (kEmitCompilerReadBarrier) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; if (kUseBakerReadBarrier) { // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(out + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, out_reg, offset, temp, /* needs_null_check */ false); + instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false); } else { // Load with slow path based read barrier. - // Save the value of `out` into `temp` before overwriting it + // Save the value of `out` into `maybe_temp` before overwriting it // in the following move operation, as we will need it for the // read barrier below. - __ movl(temp.AsRegister<CpuRegister>(), out_reg); + __ movl(maybe_temp.AsRegister<CpuRegister>(), out_reg); // /* HeapReference<Object> */ out = *(out + offset) __ movl(out_reg, Address(out_reg, offset)); - codegen_->GenerateReadBarrierSlow(instruction, out, out, temp, offset); + codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset); } } else { // Plain load with no read barrier. @@ -6050,15 +6055,16 @@ void InstructionCodeGeneratorX86_64::GenerateReferenceLoadTwoRegisters(HInstruct Location out, Location obj, uint32_t offset, - Location temp) { + Location maybe_temp) { CpuRegister out_reg = out.AsRegister<CpuRegister>(); CpuRegister obj_reg = obj.AsRegister<CpuRegister>(); if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { + DCHECK(maybe_temp.IsRegister()) << maybe_temp; // Load with fast path based Baker's read barrier. // /* HeapReference<Object> */ out = *(obj + offset) codegen_->GenerateFieldLoadWithBakerReadBarrier( - instruction, out, obj_reg, offset, temp, /* needs_null_check */ false); + instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false); } else { // Load with slow path based read barrier. // /* HeapReference<Object> */ out = *(obj + offset) diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 82aabb04d3..96265902ba 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -227,23 +227,29 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { // out <- *(out + offset) // // while honoring heap poisoning and/or read barriers (if any). - // Register `temp` is used when generating a read barrier. + // + // Location `maybe_temp` is used when generating a read barrier and + // shall be a register in that case; it may be an invalid location + // otherwise. void GenerateReferenceLoadOneRegister(HInstruction* instruction, Location out, uint32_t offset, - Location temp); + Location maybe_temp); // Generate a heap reference load using two different registers // `out` and `obj`: // // out <- *(obj + offset) // // while honoring heap poisoning and/or read barriers (if any). - // Register `temp` is used when generating a Baker's read barrier. + // + // Location `maybe_temp` is used when generating a Baker's (fast + // path) read barrier and shall be a register in that case; it may + // be an invalid location otherwise. void GenerateReferenceLoadTwoRegisters(HInstruction* instruction, Location out, Location obj, uint32_t offset, - Location temp); + Location maybe_temp); // Generate a GC root reference load: // // root <- *(obj + offset) diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc index 91e4a997fd..feb8b2092a 100644 --- a/compiler/optimizing/dominator_test.cc +++ b/compiler/optimizing/dominator_test.cc @@ -133,8 +133,9 @@ TEST(OptimizerTest, CFG4) { const uint32_t dominators[] = { kInvalidBlockId, - 0, - kInvalidBlockId + 3, + kInvalidBlockId, + 0 }; TestCode(data1, dominators, sizeof(dominators) / sizeof(int)); diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc index d4b9b71952..d5305646a8 100644 --- a/compiler/optimizing/graph_test.cc +++ b/compiler/optimizing/graph_test.cc @@ -164,7 +164,7 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges1) { // Ensure there is only one back edge. ASSERT_EQ(if_block->GetPredecessors().size(), 2u); - ASSERT_EQ(if_block->GetPredecessors()[0], entry_block); + ASSERT_EQ(if_block->GetPredecessors()[0], entry_block->GetSingleSuccessor()); ASSERT_NE(if_block->GetPredecessors()[1], if_block); // Ensure the new block is the back edge. @@ -199,7 +199,7 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges2) { // Ensure there is only one back edge. ASSERT_EQ(if_block->GetPredecessors().size(), 2u); - ASSERT_EQ(if_block->GetPredecessors()[0], entry_block); + ASSERT_EQ(if_block->GetPredecessors()[0], entry_block->GetSingleSuccessor()); ASSERT_NE(if_block->GetPredecessors()[1], if_block); // Ensure the new block is the back edge. diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 32c3a925e0..280516252b 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -21,6 +21,7 @@ #include <cctype> #include <sstream> +#include "bounds_check_elimination.h" #include "code_generator.h" #include "dead_code_elimination.h" #include "disassembler.h" @@ -505,6 +506,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { if (IsPass(LICM::kLoopInvariantCodeMotionPassName) || IsPass(HDeadCodeElimination::kFinalDeadCodeEliminationPassName) || IsPass(HDeadCodeElimination::kInitialDeadCodeEliminationPassName) + || IsPass(BoundsCheckElimination::kBoundsCheckEliminationPassName) || IsPass(SsaBuilder::kSsaBuilderPassName)) { HLoopInformation* info = instruction->GetBlock()->GetLoopInformation(); if (info == nullptr) { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 20c4f1f698..2e79df1b84 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -419,7 +419,10 @@ bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits(); if (code_item->insns_size_in_code_units_ > inline_max_code_units) { VLOG(compiler) << "Method " << PrettyMethod(method) - << " is too big to inline"; + << " is too big to inline: " + << code_item->insns_size_in_code_units_ + << " > " + << inline_max_code_units; return false; } @@ -639,9 +642,12 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, for (; !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); - if (block->IsLoopHeader()) { + + if (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible()) { + // Don't inline methods with irreducible loops, they could prevent some + // optimizations to run. VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file) - << " could not be inlined because it contains a loop"; + << " could not be inlined because it contains an irreducible loop"; return false; } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index c6da9a3f5e..a6be324730 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -131,6 +131,16 @@ static Intrinsics GetIntrinsic(InlineMethod method) { return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ? Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat; + // Floating-point tests. + case kIntrinsicFloatIsInfinite: + return Intrinsics::kFloatIsInfinite; + case kIntrinsicDoubleIsInfinite: + return Intrinsics::kDoubleIsInfinite; + case kIntrinsicFloatIsNaN: + return Intrinsics::kFloatIsNaN; + case kIntrinsicDoubleIsNaN: + return Intrinsics::kDoubleIsNaN; + // Bit manipulations. case kIntrinsicReverseBits: switch (GetType(method.d.data, true)) { @@ -176,6 +186,46 @@ static Intrinsics GetIntrinsic(InlineMethod method) { } // Misc data processing. + case kIntrinsicBitCount: + switch (GetType(method.d.data, true)) { + case Primitive::kPrimInt: + return Intrinsics::kIntegerBitCount; + case Primitive::kPrimLong: + return Intrinsics::kLongBitCount; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + case kIntrinsicCompare: + switch (GetType(method.d.data, true)) { + case Primitive::kPrimInt: + return Intrinsics::kIntegerCompare; + case Primitive::kPrimLong: + return Intrinsics::kLongCompare; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + case kIntrinsicHighestOneBit: + switch (GetType(method.d.data, true)) { + case Primitive::kPrimInt: + return Intrinsics::kIntegerHighestOneBit; + case Primitive::kPrimLong: + return Intrinsics::kLongHighestOneBit; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } + case kIntrinsicLowestOneBit: + switch (GetType(method.d.data, true)) { + case Primitive::kPrimInt: + return Intrinsics::kIntegerLowestOneBit; + case Primitive::kPrimLong: + return Intrinsics::kLongLowestOneBit; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } case kIntrinsicNumberOfLeadingZeros: switch (GetType(method.d.data, true)) { case Primitive::kPrimInt: @@ -196,6 +246,16 @@ static Intrinsics GetIntrinsic(InlineMethod method) { LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; UNREACHABLE(); } + case kIntrinsicSignum: + switch (GetType(method.d.data, true)) { + case Primitive::kPrimInt: + return Intrinsics::kIntegerSignum; + case Primitive::kPrimLong: + return Intrinsics::kLongSignum; + default: + LOG(FATAL) << "Unknown/unsupported op size " << method.d.data; + UNREACHABLE(); + } // Abs. case kIntrinsicAbsDouble: diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index b1fbf28204..97fe5872bf 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -1577,14 +1577,12 @@ void IntrinsicLocationsBuilderARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ } +UNIMPLEMENTED_INTRINSIC(IntegerBitCount) UNIMPLEMENTED_INTRINSIC(IntegerReverse) UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes) -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) +UNIMPLEMENTED_INTRINSIC(LongBitCount) UNIMPLEMENTED_INTRINSIC(LongReverse) UNIMPLEMENTED_INTRINSIC(LongReverseBytes) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) UNIMPLEMENTED_INTRINSIC(ShortReverseBytes) UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) @@ -1619,6 +1617,26 @@ UNIMPLEMENTED_INTRINSIC(MathSinh) UNIMPLEMENTED_INTRINSIC(MathTan) UNIMPLEMENTED_INTRINSIC(MathTanh) +UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) + +UNIMPLEMENTED_INTRINSIC(IntegerCompare) +UNIMPLEMENTED_INTRINSIC(LongCompare) +UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerSignum) +UNIMPLEMENTED_INTRINSIC(LongSignum) + +// Rotate operations are handled as HRor instructions. +UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) +UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) +UNIMPLEMENTED_INTRINSIC(LongRotateLeft) +UNIMPLEMENTED_INTRINSIC(LongRotateRight) + #undef UNIMPLEMENTED_INTRINSIC #undef __ diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 81cab86c83..c888f01841 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -752,21 +752,33 @@ static void GenUnsafeGet(HInvoke* invoke, Register trg = RegisterFrom(trg_loc, type); bool use_acquire_release = codegen->GetInstructionSetFeatures().PreferAcquireRelease(); - MemOperand mem_op(base.X(), offset); - if (is_volatile) { - if (use_acquire_release) { - codegen->LoadAcquire(invoke, trg, mem_op); - } else { - codegen->Load(type, trg, mem_op); + if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { + // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case. + UseScratchRegisterScope temps(masm); + Register temp = temps.AcquireW(); + codegen->GenerateArrayLoadWithBakerReadBarrier( + invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + if (is_volatile && !use_acquire_release) { __ Dmb(InnerShareable, BarrierReads); } } else { - codegen->Load(type, trg, mem_op); - } + // Other cases. + MemOperand mem_op(base.X(), offset); + if (is_volatile) { + if (use_acquire_release) { + codegen->LoadAcquire(invoke, trg, mem_op, /* needs_null_check */ true); + } else { + codegen->Load(type, trg, mem_op); + __ Dmb(InnerShareable, BarrierReads); + } + } else { + codegen->Load(type, trg, mem_op); + } - if (type == Primitive::kPrimNot) { - DCHECK(trg.IsW()); - codegen->MaybeGenerateReadBarrier(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc); + if (type == Primitive::kPrimNot) { + DCHECK(trg.IsW()); + codegen->MaybeGenerateReadBarrierSlow(invoke, trg_loc, trg_loc, base_loc, 0U, offset_loc); + } } } @@ -1026,10 +1038,15 @@ static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGenerat vixl::Label loop_head, exit_loop; if (use_acquire_release) { __ Bind(&loop_head); - __ Ldaxr(tmp_value, MemOperand(tmp_ptr)); - // TODO: Do we need a read barrier here when `type == Primitive::kPrimNot`? + // TODO: When `type == Primitive::kPrimNot`, add a read barrier for + // the reference stored in the object before attempting the CAS, + // similar to the one in the art::Unsafe_compareAndSwapObject JNI + // implementation. + // // Note that this code is not (yet) used when read barriers are // enabled (see IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject). + DCHECK(!(type == Primitive::kPrimNot && kEmitCompilerReadBarrier)); + __ Ldaxr(tmp_value, MemOperand(tmp_ptr)); __ Cmp(tmp_value, expected); __ B(&exit_loop, ne); __ Stlxr(tmp_32, value, MemOperand(tmp_ptr)); @@ -1447,10 +1464,8 @@ void IntrinsicLocationsBuilderARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNU void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ } -UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) -UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) -UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(LongRotateRight) +UNIMPLEMENTED_INTRINSIC(IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(LongBitCount) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) @@ -1474,6 +1489,26 @@ UNIMPLEMENTED_INTRINSIC(MathSinh) UNIMPLEMENTED_INTRINSIC(MathTan) UNIMPLEMENTED_INTRINSIC(MathTanh) +UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) + +UNIMPLEMENTED_INTRINSIC(IntegerCompare) +UNIMPLEMENTED_INTRINSIC(LongCompare) +UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerSignum) +UNIMPLEMENTED_INTRINSIC(LongSignum) + +// Rotate operations are handled as HRor instructions. +UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) +UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) +UNIMPLEMENTED_INTRINSIC(LongRotateLeft) +UNIMPLEMENTED_INTRINSIC(LongRotateRight) + #undef UNIMPLEMENTED_INTRINSIC #undef __ diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 2e87546282..88217b308e 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -23,21 +23,35 @@ #define INTRINSICS_LIST(V) \ V(DoubleDoubleToRawLongBits, 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(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(IntegerReverse, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(IntegerReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(IntegerBitCount, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(IntegerCompare, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(IntegerHighestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(IntegerLowestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(IntegerNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(IntegerNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(IntegerRotateRight, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(IntegerRotateLeft, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(IntegerSignum, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(LongReverse, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(LongReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(LongBitCount, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(LongCompare, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(LongHighestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(LongLowestOneBit, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(LongNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(LongNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(LongRotateRight, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(LongRotateLeft, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ + V(LongSignum, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(ShortReverseBytes, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(MathAbsDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ V(MathAbsFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \ diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index bc126a2716..0d9cf091cc 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -935,6 +935,9 @@ void IntrinsicLocationsBuilderMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUS void IntrinsicCodeGeneratorMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ } +UNIMPLEMENTED_INTRINSIC(IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(LongBitCount) + UNIMPLEMENTED_INTRINSIC(MathAbsDouble) UNIMPLEMENTED_INTRINSIC(MathAbsFloat) UNIMPLEMENTED_INTRINSIC(MathAbsInt) @@ -1010,6 +1013,21 @@ UNIMPLEMENTED_INTRINSIC(MathNextAfter) UNIMPLEMENTED_INTRINSIC(MathSinh) UNIMPLEMENTED_INTRINSIC(MathTan) UNIMPLEMENTED_INTRINSIC(MathTanh) + +UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) + +UNIMPLEMENTED_INTRINSIC(IntegerCompare) +UNIMPLEMENTED_INTRINSIC(LongCompare) +UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerSignum) +UNIMPLEMENTED_INTRINSIC(LongSignum) + #undef UNIMPLEMENTED_INTRINSIC #undef __ diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 8b45ea7c4f..cba84fa3e3 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1141,7 +1141,8 @@ static void GenUnsafePut(LocationSummary* locations, } if (type == Primitive::kPrimNot) { - codegen->MarkGCCard(base, value); + bool value_can_be_null = true; // TODO: Worth finding out this information? + codegen->MarkGCCard(base, value, value_can_be_null); } } @@ -1287,6 +1288,12 @@ static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGenerat DCHECK_NE(offset, out); DCHECK_NE(expected, out); + if (type == Primitive::kPrimNot) { + // Mark card for object assuming new value is stored. + bool value_can_be_null = true; // TODO: Worth finding out this information? + codegen->MarkGCCard(base, value, value_can_be_null); + } + // do { // tmp_value = [tmp_ptr] - expected; // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value)); @@ -1724,6 +1731,9 @@ void IntrinsicLocationsBuilderMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UN void IntrinsicCodeGeneratorMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ } +UNIMPLEMENTED_INTRINSIC(IntegerBitCount) +UNIMPLEMENTED_INTRINSIC(LongBitCount) + UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) @@ -1750,6 +1760,20 @@ UNIMPLEMENTED_INTRINSIC(MathSinh) UNIMPLEMENTED_INTRINSIC(MathTan) UNIMPLEMENTED_INTRINSIC(MathTanh) +UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) + +UNIMPLEMENTED_INTRINSIC(IntegerCompare) +UNIMPLEMENTED_INTRINSIC(LongCompare) +UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerSignum) +UNIMPLEMENTED_INTRINSIC(LongSignum) + #undef UNIMPLEMENTED_INTRINSIC #undef __ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 677f2e9c81..acc40bc998 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2303,6 +2303,81 @@ void IntrinsicCodeGeneratorX86::VisitLongReverse(HInvoke* invoke) { SwapBits(reg_high, temp, 4, 0x0f0f0f0f, assembler); } +static void CreateBitCountLocations( + ArenaAllocator* arena, CodeGeneratorX86* codegen, HInvoke* invoke, bool is_long) { + if (!codegen->GetInstructionSetFeatures().HasPopCnt()) { + // Do nothing if there is no popcnt support. This results in generating + // a call for the intrinsic rather than direct code. + return; + } + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + if (is_long) { + locations->AddTemp(Location::RequiresRegister()); + } + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::RequiresRegister()); +} + +static void GenBitCount(X86Assembler* assembler, HInvoke* invoke, bool is_long) { + LocationSummary* locations = invoke->GetLocations(); + Location src = locations->InAt(0); + Register out = locations->Out().AsRegister<Register>(); + + if (invoke->InputAt(0)->IsConstant()) { + // Evaluate this at compile time. + int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); + value = is_long + ? POPCOUNT(static_cast<uint64_t>(value)) + : POPCOUNT(static_cast<uint32_t>(value)); + if (value == 0) { + __ xorl(out, out); + } else { + __ movl(out, Immediate(value)); + } + return; + } + + // Handle the non-constant cases. + if (!is_long) { + if (src.IsRegister()) { + __ popcntl(out, src.AsRegister<Register>()); + } else { + DCHECK(src.IsStackSlot()); + __ popcntl(out, Address(ESP, src.GetStackIndex())); + } + } else { + // The 64-bit case needs to worry about two parts. + Register temp = locations->GetTemp(0).AsRegister<Register>(); + if (src.IsRegisterPair()) { + __ popcntl(temp, src.AsRegisterPairLow<Register>()); + __ popcntl(out, src.AsRegisterPairHigh<Register>()); + } else { + DCHECK(src.IsDoubleStackSlot()); + __ popcntl(temp, Address(ESP, src.GetStackIndex())); + __ popcntl(out, Address(ESP, src.GetHighStackIndex(kX86WordSize))); + } + __ addl(out, temp); + } +} + +void IntrinsicLocationsBuilderX86::VisitIntegerBitCount(HInvoke* invoke) { + CreateBitCountLocations(arena_, codegen_, invoke, /* is_long */ false); +} + +void IntrinsicCodeGeneratorX86::VisitIntegerBitCount(HInvoke* invoke) { + GenBitCount(GetAssembler(), invoke, /* is_long */ false); +} + +void IntrinsicLocationsBuilderX86::VisitLongBitCount(HInvoke* invoke) { + CreateBitCountLocations(arena_, codegen_, invoke, /* is_long */ true); +} + +void IntrinsicCodeGeneratorX86::VisitLongBitCount(HInvoke* invoke) { + GenBitCount(GetAssembler(), invoke, /* is_long */ true); +} + static void CreateLeadingZeroLocations(ArenaAllocator* arena, HInvoke* invoke, bool is_long) { LocationSummary* locations = new (arena) LocationSummary(invoke, LocationSummary::kNoCall, @@ -2518,11 +2593,27 @@ 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(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) + +UNIMPLEMENTED_INTRINSIC(IntegerCompare) +UNIMPLEMENTED_INTRINSIC(LongCompare) +UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerSignum) +UNIMPLEMENTED_INTRINSIC(LongSignum) + +// Rotate operations are handled as HRor instructions. UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) UNIMPLEMENTED_INTRINSIC(LongRotateRight) UNIMPLEMENTED_INTRINSIC(LongRotateLeft) -UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) #undef UNIMPLEMENTED_INTRINSIC diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 690cf3d413..6ccc5d1e01 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2368,6 +2368,70 @@ void IntrinsicCodeGeneratorX86_64::VisitLongReverse(HInvoke* invoke) { SwapBits64(reg, temp1, temp2, 4, INT64_C(0x0f0f0f0f0f0f0f0f), assembler); } +static void CreateBitCountLocations( + ArenaAllocator* arena, CodeGeneratorX86_64* codegen, HInvoke* invoke) { + if (!codegen->GetInstructionSetFeatures().HasPopCnt()) { + // Do nothing if there is no popcnt support. This results in generating + // a call for the intrinsic rather than direct code. + return; + } + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::RequiresRegister()); +} + +static void GenBitCount(X86_64Assembler* assembler, HInvoke* invoke, bool is_long) { + LocationSummary* locations = invoke->GetLocations(); + Location src = locations->InAt(0); + CpuRegister out = locations->Out().AsRegister<CpuRegister>(); + + if (invoke->InputAt(0)->IsConstant()) { + // Evaluate this at compile time. + int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant()); + value = is_long + ? POPCOUNT(static_cast<uint64_t>(value)) + : POPCOUNT(static_cast<uint32_t>(value)); + if (value == 0) { + __ xorl(out, out); + } else { + __ movl(out, Immediate(value)); + } + return; + } + + if (src.IsRegister()) { + if (is_long) { + __ popcntq(out, src.AsRegister<CpuRegister>()); + } else { + __ popcntl(out, src.AsRegister<CpuRegister>()); + } + } else if (is_long) { + DCHECK(src.IsDoubleStackSlot()); + __ popcntq(out, Address(CpuRegister(RSP), src.GetStackIndex())); + } else { + DCHECK(src.IsStackSlot()); + __ popcntl(out, Address(CpuRegister(RSP), src.GetStackIndex())); + } +} + +void IntrinsicLocationsBuilderX86_64::VisitIntegerBitCount(HInvoke* invoke) { + CreateBitCountLocations(arena_, codegen_, invoke); +} + +void IntrinsicCodeGeneratorX86_64::VisitIntegerBitCount(HInvoke* invoke) { + GenBitCount(GetAssembler(), invoke, /* is_long */ false); +} + +void IntrinsicLocationsBuilderX86_64::VisitLongBitCount(HInvoke* invoke) { + CreateBitCountLocations(arena_, codegen_, invoke); +} + +void IntrinsicCodeGeneratorX86_64::VisitLongBitCount(HInvoke* invoke) { + GenBitCount(GetAssembler(), invoke, /* is_long */ true); +} + static void CreateLeadingZeroLocations(ArenaAllocator* arena, HInvoke* invoke) { LocationSummary* locations = new (arena) LocationSummary(invoke, LocationSummary::kNoCall, @@ -2528,6 +2592,22 @@ void IntrinsicCodeGeneratorX86_64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE } UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) + +UNIMPLEMENTED_INTRINSIC(FloatIsInfinite) +UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(FloatIsNaN) +UNIMPLEMENTED_INTRINSIC(DoubleIsNaN) + +UNIMPLEMENTED_INTRINSIC(IntegerCompare) +UNIMPLEMENTED_INTRINSIC(LongCompare) +UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit) +UNIMPLEMENTED_INTRINSIC(LongHighestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit) +UNIMPLEMENTED_INTRINSIC(LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(IntegerSignum) +UNIMPLEMENTED_INTRINSIC(LongSignum) + +// Rotate operations are handled as HRor instructions. UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) UNIMPLEMENTED_INTRINSIC(LongRotateLeft) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 2eabadf861..92f758d61d 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -288,9 +288,10 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { // Make sure the loop has only one pre header. This simplifies SSA building by having // to just look at the pre header to know which locals are initialized at entry of the - // loop. + // loop. Also, don't allow the entry block to be a pre header: this simplifies inlining + // this graph. size_t number_of_incomings = header->GetPredecessors().size() - info->NumberOfBackEdges(); - if (number_of_incomings != 1) { + if (number_of_incomings != 1 || (GetEntryBlock()->GetSingleSuccessor() == header)) { HBasicBlock* pre_header = new (arena_) HBasicBlock(this, header->GetDexPc()); AddBlock(pre_header); pre_header->AddInstruction(new (arena_) HGoto(header->GetDexPc())); @@ -1837,6 +1838,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { DCHECK(GetBlocks()[0]->IsEntryBlock()); DCHECK(GetBlocks()[2]->IsExitBlock()); DCHECK(!body->IsExitBlock()); + DCHECK(!body->IsInLoop()); HInstruction* last = body->GetLastInstruction(); invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions()); @@ -1895,7 +1897,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { // Update the meta information surrounding blocks: // (1) the graph they are now in, // (2) the reverse post order of that graph, - // (3) the potential loop information they are now in, + // (3) their potential loop information, inner and outer, // (4) try block membership. // Note that we do not need to update catch phi inputs because they // correspond to the register file of the outer method which the inlinee @@ -1924,15 +1926,24 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) { HBasicBlock* current = it.Current(); if (current != exit_block_ && current != entry_block_ && current != first) { - DCHECK(!current->IsInLoop()); DCHECK(current->GetTryCatchInformation() == nullptr); DCHECK(current->GetGraph() == this); current->SetGraph(outer_graph); outer_graph->AddBlock(current); outer_graph->reverse_post_order_[++index_of_at] = current; - if (loop_info != nullptr) { + if (!current->IsInLoop()) { current->SetLoopInformation(loop_info); - for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { + } else if (current->IsLoopHeader()) { + // Clear the information of which blocks are contained in that loop. Since the + // information is stored as a bit vector based on block ids, we have to update + // it, as those block ids were specific to the callee graph and we are now adding + // these blocks to the caller graph. + current->GetLoopInformation()->ClearAllBlocks(); + } + if (current->IsInLoop()) { + for (HLoopInformationOutwardIterator loop_it(*current); + !loop_it.Done(); + loop_it.Advance()) { loop_it.Current()->Add(current); } } @@ -1945,7 +1956,9 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { outer_graph->AddBlock(to); outer_graph->reverse_post_order_[++index_of_at] = to; if (loop_info != nullptr) { - to->SetLoopInformation(loop_info); + if (!to->IsInLoop()) { + to->SetLoopInformation(loop_info); + } for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { loop_it.Current()->Add(to); } @@ -2208,10 +2221,7 @@ void HInvoke::SetIntrinsic(Intrinsics intrinsic, SetSideEffects(GetSideEffects().Union(SideEffects::CanTriggerGC())); } // Adjust method's exception status from intrinsic table. - switch (exceptions) { - case kNoThrow: SetCanThrow(false); break; - case kCanThrow: SetCanThrow(true); break; - } + SetCanThrow(exceptions == kCanThrow); } bool HNewInstance::IsStringAlloc() const { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index e222ef7260..41c2f17cd9 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -159,7 +159,7 @@ class ReferenceTypeInfo : ValueObject { static ReferenceTypeInfo CreateInvalid() { return ReferenceTypeInfo(); } - static bool IsValidHandle(TypeHandle handle) SHARED_REQUIRES(Locks::mutator_lock_) { + static bool IsValidHandle(TypeHandle handle) { return handle.GetReference() != nullptr; } @@ -689,6 +689,10 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { void Add(HBasicBlock* block); void Remove(HBasicBlock* block); + void ClearAllBlocks() { + blocks_.ClearAllBits(); + } + private: // Internal recursive implementation of `Populate`. void PopulateRecursive(HBasicBlock* block); @@ -3689,19 +3693,13 @@ class HInvokeStaticOrDirect : public HInvoke { DCHECK(!IsStaticWithExplicitClinitCheck()); } - HNewInstance* GetThisArgumentOfStringInit() const { - DCHECK(IsStringInit()); - size_t index = InputCount() - 1; - DCHECK(InputAt(index)->IsNewInstance()); - return InputAt(index)->AsNewInstance(); - } - - void RemoveThisArgumentOfStringInit() { + HInstruction* GetAndRemoveThisArgumentOfStringInit() { DCHECK(IsStringInit()); size_t index = InputCount() - 1; - DCHECK(InputAt(index)->IsNewInstance()); + HInstruction* input = InputAt(index); RemoveAsUserOfInput(index); inputs_.pop_back(); + return input; } // Is this a call to a static method whose declaring class has an diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index fffd00535c..3fac914017 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -676,7 +676,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, return nullptr; } codegen->GetAssembler()->cfi().SetEnabled( - compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()); + compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo()); PassObserver pass_observer(graph, codegen.get(), diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 527c242916..779f319e61 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -23,21 +23,41 @@ namespace art { -class RTPVisitor : public HGraphDelegateVisitor { +static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollection* handles, + ClassLinker::ClassRoot class_root, + ReferenceTypeInfo::TypeHandle* cache) { + if (!ReferenceTypeInfo::IsValidHandle(*cache)) { + // Mutator lock is required for NewHandle. + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + ScopedObjectAccess soa(Thread::Current()); + *cache = handles->NewHandle(linker->GetClassRoot(class_root)); + } + return *cache; +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() { + return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_); +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetClassClassHandle() { + return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_); +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() { + return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_); +} + +ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowableClassHandle() { + return GetRootHandle(handles_, ClassLinker::kJavaLangThrowable, &throwable_class_handle_); +} + +class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { public: RTPVisitor(HGraph* graph, - StackHandleScopeCollection* handles, - ArenaVector<HInstruction*>* worklist, - ReferenceTypeInfo::TypeHandle object_class_handle, - ReferenceTypeInfo::TypeHandle class_class_handle, - ReferenceTypeInfo::TypeHandle string_class_handle, - ReferenceTypeInfo::TypeHandle throwable_class_handle) + HandleCache* handle_cache, + ArenaVector<HInstruction*>* worklist) : HGraphDelegateVisitor(graph), - handles_(handles), - object_class_handle_(object_class_handle), - class_class_handle_(class_class_handle), - string_class_handle_(string_class_handle), - throwable_class_handle_(throwable_class_handle), + handle_cache_(handle_cache), worklist_(worklist) {} void VisitNewInstance(HNewInstance* new_instance) OVERRIDE; @@ -64,11 +84,7 @@ class RTPVisitor : public HGraphDelegateVisitor { bool is_exact); private: - StackHandleScopeCollection* handles_; - ReferenceTypeInfo::TypeHandle object_class_handle_; - ReferenceTypeInfo::TypeHandle class_class_handle_; - ReferenceTypeInfo::TypeHandle string_class_handle_; - ReferenceTypeInfo::TypeHandle throwable_class_handle_; + HandleCache* handle_cache_; ArenaVector<HInstruction*>* worklist_; }; @@ -76,24 +92,9 @@ ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph, StackHandleScopeCollection* handles, const char* name) : HOptimization(graph, name), - handles_(handles), + handle_cache_(handles), worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)) { worklist_.reserve(kDefaultWorklistSize); - // Mutator lock is required for NewHandle, but annotalysis ignores constructors. - ScopedObjectAccess soa(Thread::Current()); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - object_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)); - string_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangString)); - class_class_handle_ = handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangClass)); - throwable_class_handle_ = - handles_->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangThrowable)); - - if (kIsDebugBuild) { - DCHECK(ReferenceTypeInfo::IsValidHandle(object_class_handle_)); - DCHECK(ReferenceTypeInfo::IsValidHandle(class_class_handle_)); - DCHECK(ReferenceTypeInfo::IsValidHandle(string_class_handle_)); - DCHECK(ReferenceTypeInfo::IsValidHandle(throwable_class_handle_)); - } } void ReferenceTypePropagation::ValidateTypes() { @@ -137,13 +138,7 @@ void ReferenceTypePropagation::Run() { } void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) { - RTPVisitor visitor(graph_, - handles_, - &worklist_, - object_class_handle_, - class_class_handle_, - string_class_handle_, - throwable_class_handle_); + RTPVisitor visitor(graph_, &handle_cache_, &worklist_); // Handle Phis first as there might be instructions in the same block who depend on them. for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { VisitPhi(it.Current()->AsPhi()); @@ -243,7 +238,7 @@ void ReferenceTypePropagation::BoundTypeForIfNotNull(HBasicBlock* block) { ScopedObjectAccess soa(Thread::Current()); HInstruction* insert_point = notNullBlock->GetFirstInstruction(); ReferenceTypeInfo object_rti = ReferenceTypeInfo::Create( - object_class_handle_, /* is_exact */ true); + handle_cache_.GetObjectClassHandle(), /* is_exact */ true); if (ShouldCreateBoundType(insert_point, obj, object_rti, nullptr, notNullBlock)) { bound_type = new (graph_->GetArena()) HBoundType(obj); bound_type->SetUpperBound(object_rti, /* bound_can_be_null */ false); @@ -395,9 +390,9 @@ void ReferenceTypePropagation::BoundTypeForIfInstanceOf(HBasicBlock* block) { } } -void RTPVisitor::SetClassAsTypeInfo(HInstruction* instr, - mirror::Class* klass, - bool is_exact) { +void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* instr, + mirror::Class* klass, + bool is_exact) { if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) { // Calls to String.<init> are replaced with a StringFactory. if (kIsDebugBuild) { @@ -422,22 +417,22 @@ void RTPVisitor::SetClassAsTypeInfo(HInstruction* instr, << "Expected String.<init>: " << PrettyMethod(method); } instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true)); + ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true)); } else if (klass != nullptr) { ScopedObjectAccess soa(Thread::Current()); - ReferenceTypeInfo::TypeHandle handle = handles_->NewHandle(klass); + ReferenceTypeInfo::TypeHandle handle = handle_cache_->NewHandle(klass); is_exact = is_exact || klass->CannotBeAssignedFromOtherTypes(); instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(handle, is_exact)); } else { instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false)); + ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); } } -void RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr, - uint16_t type_idx, - const DexFile& dex_file, - bool is_exact) { +void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr, + uint16_t type_idx, + const DexFile& dex_file, + bool is_exact) { DCHECK_EQ(instr->GetType(), Primitive::kPrimNot); ScopedObjectAccess soa(Thread::Current()); @@ -447,11 +442,11 @@ void RTPVisitor::UpdateReferenceTypeInfo(HInstruction* instr, SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact); } -void RTPVisitor::VisitNewInstance(HNewInstance* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) { UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } -void RTPVisitor::VisitNewArray(HNewArray* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) { UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true); } @@ -467,7 +462,7 @@ static mirror::Class* GetClassFromDexCache(Thread* self, const DexFile& dex_file return dex_cache->GetResolvedType(type_idx); } -void RTPVisitor::VisitParameterValue(HParameterValue* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) { ScopedObjectAccess soa(Thread::Current()); // We check if the existing type is valid: the inliner may have set it. if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) { @@ -477,8 +472,8 @@ void RTPVisitor::VisitParameterValue(HParameterValue* instr) { } } -void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr, - const FieldInfo& info) { +void ReferenceTypePropagation::RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr, + const FieldInfo& info) { if (instr->GetType() != Primitive::kPrimNot) { return; } @@ -500,57 +495,61 @@ void RTPVisitor::UpdateFieldAccessTypeInfo(HInstruction* instr, SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } -void RTPVisitor::VisitInstanceFieldGet(HInstanceFieldGet* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitInstanceFieldGet(HInstanceFieldGet* instr) { UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo()); } -void RTPVisitor::VisitStaticFieldGet(HStaticFieldGet* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitStaticFieldGet(HStaticFieldGet* instr) { UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo()); } -void RTPVisitor::VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedInstanceFieldGet( + HUnresolvedInstanceFieldGet* instr) { // TODO: Use descriptor to get the actual type. if (instr->GetFieldType() == Primitive::kPrimNot) { instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false)); + ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); } } -void RTPVisitor::VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitUnresolvedStaticFieldGet( + HUnresolvedStaticFieldGet* instr) { // TODO: Use descriptor to get the actual type. if (instr->GetFieldType() == Primitive::kPrimNot) { instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false)); + ReferenceTypeInfo::Create(handle_cache_->GetObjectClassHandle(), /* is_exact */ false)); } } -void RTPVisitor::VisitLoadClass(HLoadClass* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitLoadClass(HLoadClass* instr) { ScopedObjectAccess soa(Thread::Current()); // Get type from dex cache assuming it was populated by the verifier. mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(), instr->GetDexFile(), instr->GetTypeIndex()); if (resolved_class != nullptr) { instr->SetLoadedClassRTI(ReferenceTypeInfo::Create( - handles_->NewHandle(resolved_class), /* is_exact */ true)); + handle_cache_->NewHandle(resolved_class), /* is_exact */ true)); } - instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(class_class_handle_, /* is_exact */ true)); + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(handle_cache_->GetClassClassHandle(), /* is_exact */ true)); } -void RTPVisitor::VisitClinitCheck(HClinitCheck* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitClinitCheck(HClinitCheck* instr) { instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo()); } -void RTPVisitor::VisitLoadString(HLoadString* instr) { - instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(string_class_handle_, /* is_exact */ true)); +void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) { + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true)); } -void RTPVisitor::VisitLoadException(HLoadException* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitLoadException(HLoadException* instr) { DCHECK(instr->GetBlock()->IsCatchBlock()); TryCatchInformation* catch_info = instr->GetBlock()->GetTryCatchInformation(); if (catch_info->IsCatchAllTypeIndex()) { - instr->SetReferenceTypeInfo(ReferenceTypeInfo::Create(throwable_class_handle_, - /* is_exact */ false)); + instr->SetReferenceTypeInfo( + ReferenceTypeInfo::Create(handle_cache_->GetThrowableClassHandle(), /* is_exact */ false)); } else { UpdateReferenceTypeInfo(instr, catch_info->GetCatchTypeIndex(), @@ -559,7 +558,7 @@ void RTPVisitor::VisitLoadException(HLoadException* instr) { } } -void RTPVisitor::VisitNullCheck(HNullCheck* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitNullCheck(HNullCheck* instr) { ScopedObjectAccess soa(Thread::Current()); ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo(); if (parent_rti.IsValid()) { @@ -567,7 +566,7 @@ void RTPVisitor::VisitNullCheck(HNullCheck* instr) { } } -void RTPVisitor::VisitBoundType(HBoundType* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitBoundType(HBoundType* instr) { ScopedObjectAccess soa(Thread::Current()); ReferenceTypeInfo class_rti = instr->GetUpperBound(); @@ -601,7 +600,7 @@ void RTPVisitor::VisitBoundType(HBoundType* instr) { } } -void RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { +void ReferenceTypePropagation::RTPVisitor::VisitCheckCast(HCheckCast* check_cast) { ScopedObjectAccess soa(Thread::Current()); HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass(); @@ -677,7 +676,8 @@ ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo& result_type_handle = b_type_handle; is_exact = false; } else if (!a_is_interface && !b_is_interface) { - result_type_handle = handles_->NewHandle(a_type_handle->GetCommonSuperClass(b_type_handle)); + result_type_handle = + handle_cache_.NewHandle(a_type_handle->GetCommonSuperClass(b_type_handle)); is_exact = false; } else { // This can happen if: @@ -687,17 +687,14 @@ ReferenceTypeInfo ReferenceTypePropagation::MergeTypes(const ReferenceTypeInfo& // void foo(Interface i, boolean cond) { // Object o = cond ? i : new Object(); // } - result_type_handle = object_class_handle_; + result_type_handle = handle_cache_.GetObjectClassHandle(); is_exact = false; } return ReferenceTypeInfo::Create(result_type_handle, is_exact); } -static void UpdateArrayGet(HArrayGet* instr, - StackHandleScopeCollection* handles, - ReferenceTypeInfo::TypeHandle object_class_handle) - SHARED_REQUIRES(Locks::mutator_lock_) { +void ReferenceTypePropagation::UpdateArrayGet(HArrayGet* instr, HandleCache* handle_cache) { DCHECK_EQ(Primitive::kPrimNot, instr->GetType()); ReferenceTypeInfo parent_rti = instr->InputAt(0)->GetReferenceTypeInfo(); @@ -707,13 +704,14 @@ static void UpdateArrayGet(HArrayGet* instr, Handle<mirror::Class> handle = parent_rti.GetTypeHandle(); if (handle->IsObjectArrayClass()) { - ReferenceTypeInfo::TypeHandle component_handle = handles->NewHandle(handle->GetComponentType()); + ReferenceTypeInfo::TypeHandle component_handle = + handle_cache->NewHandle(handle->GetComponentType()); instr->SetReferenceTypeInfo( ReferenceTypeInfo::Create(component_handle, /* is_exact */ false)); } else { // We don't know what the parent actually is, so we fallback to object. instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(object_class_handle, /* is_exact */ false)); + ReferenceTypeInfo::Create(handle_cache->GetObjectClassHandle(), /* is_exact */ false)); } } @@ -733,7 +731,7 @@ bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr) { } else if (instr->IsArrayGet()) { // TODO: consider if it's worth "looking back" and binding the input object // to an array type. - UpdateArrayGet(instr->AsArrayGet(), handles_, object_class_handle_); + UpdateArrayGet(instr->AsArrayGet(), &handle_cache_); } else { LOG(FATAL) << "Invalid instruction (should not get here)"; } @@ -741,7 +739,7 @@ bool ReferenceTypePropagation::UpdateReferenceTypeInfo(HInstruction* instr) { return !previous_rti.IsEqual(instr->GetReferenceTypeInfo()); } -void RTPVisitor::VisitInvoke(HInvoke* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitInvoke(HInvoke* instr) { if (instr->GetType() != Primitive::kPrimNot) { return; } @@ -755,13 +753,13 @@ void RTPVisitor::VisitInvoke(HInvoke* instr) { SetClassAsTypeInfo(instr, klass, /* is_exact */ false); } -void RTPVisitor::VisitArrayGet(HArrayGet* instr) { +void ReferenceTypePropagation::RTPVisitor::VisitArrayGet(HArrayGet* instr) { if (instr->GetType() != Primitive::kPrimNot) { return; } ScopedObjectAccess soa(Thread::Current()); - UpdateArrayGet(instr, handles_, object_class_handle_); + UpdateArrayGet(instr, handle_cache_); if (!instr->GetReferenceTypeInfo().IsValid()) { worklist_->push_back(instr); } @@ -796,7 +794,7 @@ void ReferenceTypePropagation::UpdatePhi(HPhi* instr) { // All inputs are NullConstants, set the type to object. // This may happen in the presence of inlining. instr->SetReferenceTypeInfo( - ReferenceTypeInfo::Create(object_class_handle_, /* is_exact */ false)); + ReferenceTypeInfo::Create(handle_cache_.GetObjectClassHandle(), /* is_exact */ false)); return; } @@ -886,4 +884,5 @@ void ReferenceTypePropagation::AddDependentInstructionsToWorklist(HInstruction* } } } + } // namespace art diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 5c05592726..47ba0277ae 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -40,6 +40,31 @@ class ReferenceTypePropagation : public HOptimization { static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation"; private: + class HandleCache { + public: + explicit HandleCache(StackHandleScopeCollection* handles) : handles_(handles) { } + + template <typename T> + MutableHandle<T> NewHandle(T* object) SHARED_REQUIRES(Locks::mutator_lock_) { + return handles_->NewHandle(object); + } + + ReferenceTypeInfo::TypeHandle GetObjectClassHandle(); + ReferenceTypeInfo::TypeHandle GetClassClassHandle(); + ReferenceTypeInfo::TypeHandle GetStringClassHandle(); + ReferenceTypeInfo::TypeHandle GetThrowableClassHandle(); + + private: + StackHandleScopeCollection* handles_; + + ReferenceTypeInfo::TypeHandle object_class_handle_; + ReferenceTypeInfo::TypeHandle class_class_handle_; + ReferenceTypeInfo::TypeHandle string_class_handle_; + ReferenceTypeInfo::TypeHandle throwable_class_handle_; + }; + + class RTPVisitor; + void VisitPhi(HPhi* phi); void VisitBasicBlock(HBasicBlock* block); void UpdateBoundType(HBoundType* bound_type) SHARED_REQUIRES(Locks::mutator_lock_); @@ -53,19 +78,18 @@ class ReferenceTypePropagation : public HOptimization { bool UpdateNullability(HInstruction* instr); bool UpdateReferenceTypeInfo(HInstruction* instr); + static void UpdateArrayGet(HArrayGet* instr, HandleCache* handle_cache) + SHARED_REQUIRES(Locks::mutator_lock_); + ReferenceTypeInfo MergeTypes(const ReferenceTypeInfo& a, const ReferenceTypeInfo& b) SHARED_REQUIRES(Locks::mutator_lock_); void ValidateTypes(); - StackHandleScopeCollection* handles_; + HandleCache handle_cache_; ArenaVector<HInstruction*> worklist_; - ReferenceTypeInfo::TypeHandle object_class_handle_; - ReferenceTypeInfo::TypeHandle class_class_handle_; - ReferenceTypeInfo::TypeHandle string_class_handle_; - ReferenceTypeInfo::TypeHandle throwable_class_handle_; static constexpr size_t kDefaultWorklistSize = 8; diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index a966b62b4f..d77639d608 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -1734,6 +1734,12 @@ void RegisterAllocator::ConnectSiblings(LiveInterval* interval) { } } +static bool IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop( + HInstruction* instruction) { + return instruction->GetBlock()->GetGraph()->HasIrreducibleLoops() && + (instruction->IsConstant() || instruction->IsCurrentMethod()); +} + void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval, HBasicBlock* from, HBasicBlock* to) const { @@ -1750,7 +1756,19 @@ void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval, // Interval was not split. return; } - DCHECK(destination != nullptr && source != nullptr); + + LiveInterval* parent = interval->GetParent(); + HInstruction* defined_by = parent->GetDefinedBy(); + if (destination == nullptr) { + // Our live_in fixed point calculation has found that the instruction is live + // in the `to` block because it will eventually enter an irreducible loop. Our + // live interval computation however does not compute a fixed point, and + // therefore will not have a location for that instruction for `to`. + // Because the instruction is a constant or the ArtMethod, we don't need to + // do anything: it will be materialized in the irreducible loop. + DCHECK(IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(defined_by)); + return; + } if (!destination->HasRegister()) { // Values are eagerly spilled. Spill slot already contains appropriate value. @@ -1761,13 +1779,13 @@ void RegisterAllocator::ConnectSplitSiblings(LiveInterval* interval, // we need to put the moves at the entry of `to`. if (from->GetNormalSuccessors().size() == 1) { InsertParallelMoveAtExitOf(from, - interval->GetParent()->GetDefinedBy(), + defined_by, source->ToLocation(), destination->ToLocation()); } else { DCHECK_EQ(to->GetPredecessors().size(), 1u); InsertParallelMoveAtEntryOf(to, - interval->GetParent()->GetDefinedBy(), + defined_by, source->ToLocation(), destination->ToLocation()); } diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index c0011cde8d..165d09d1a5 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -927,16 +927,21 @@ void SsaBuilder::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { if (invoke->IsStringInit()) { // This is a StringFactory call which acts as a String constructor. Its // result replaces the empty String pre-allocated by NewInstance. - HNewInstance* new_instance = invoke->GetThisArgumentOfStringInit(); - invoke->RemoveThisArgumentOfStringInit(); + HInstruction* arg_this = invoke->GetAndRemoveThisArgumentOfStringInit(); // Replacing the NewInstance might render it redundant. Keep a list of these // to be visited once it is clear whether it is has remaining uses. - uninitialized_strings_.push_back(new_instance); + if (arg_this->IsNewInstance()) { + uninitialized_strings_.push_back(arg_this->AsNewInstance()); + } else { + DCHECK(arg_this->IsPhi()); + // NewInstance is not the direct input of the StringFactory call. It might + // be redundant but optimizing this case is not worth the effort. + } - // Walk over all vregs and replace any occurrence of `new_instance` with `invoke`. + // Walk over all vregs and replace any occurrence of `arg_this` with `invoke`. for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) { - if ((*current_locals_)[vreg] == new_instance) { + if ((*current_locals_)[vreg] == arg_this) { (*current_locals_)[vreg] = invoke; } } diff --git a/compiler/profile_assistant.cc b/compiler/profile_assistant.cc index 81f2a5692d..85335efcc4 100644 --- a/compiler/profile_assistant.cc +++ b/compiler/profile_assistant.cc @@ -16,54 +16,154 @@ #include "profile_assistant.h" +#include "base/unix_file/fd_file.h" +#include "os.h" + namespace art { // Minimum number of new methods that profiles must contain to enable recompilation. static constexpr const uint32_t kMinNewMethodsForCompilation = 10; -bool ProfileAssistant::ProcessProfiles( - const std::vector<std::string>& profile_files, - const std::vector<std::string>& reference_profile_files, - /*out*/ ProfileCompilationInfo** profile_compilation_info) { +bool ProfileAssistant::ProcessProfilesInternal( + const std::vector<ScopedFlock>& profile_files, + const std::vector<ScopedFlock>& reference_profile_files, + /*out*/ ProfileCompilationInfo** profile_compilation_info) { DCHECK(!profile_files.empty()); - DCHECK(reference_profile_files.empty() || + DCHECK(!reference_profile_files.empty() || (profile_files.size() == reference_profile_files.size())); std::vector<ProfileCompilationInfo> new_info(profile_files.size()); bool should_compile = false; // Read the main profile files. - for (size_t i = 0; i < profile_files.size(); i++) { - if (!new_info[i].Load(profile_files[i])) { - LOG(WARNING) << "Could not load profile file: " << profile_files[i]; + for (size_t i = 0; i < new_info.size(); i++) { + if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) { + LOG(WARNING) << "Could not load profile file at index " << i; return false; } // Do we have enough new profiled methods that will make the compilation worthwhile? should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation); } + if (!should_compile) { - *profile_compilation_info = nullptr; return true; } std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo()); + // Merge information. for (size_t i = 0; i < new_info.size(); i++) { + if (!reference_profile_files.empty()) { + if (!new_info[i].Load(reference_profile_files[i].GetFile()->Fd())) { + LOG(WARNING) << "Could not load reference profile file at index " << i; + return false; + } + } // Merge all data into a single object. - result->Load(new_info[i]); - // If we have any reference profile information merge their information with - // the current profiles and save them back to disk. + if (!result->Load(new_info[i])) { + LOG(WARNING) << "Could not merge profile data at index " << i; + return false; + } + } + // We were successful in merging all profile information. Update the files. + for (size_t i = 0; i < new_info.size(); i++) { if (!reference_profile_files.empty()) { - if (!new_info[i].Load(reference_profile_files[i])) { - LOG(WARNING) << "Could not load reference profile file: " << reference_profile_files[i]; + if (!reference_profile_files[i].GetFile()->ClearContent()) { + PLOG(WARNING) << "Could not clear reference profile file at index " << i; + return false; + } + if (!new_info[i].Save(reference_profile_files[i].GetFile()->Fd())) { + LOG(WARNING) << "Could not save reference profile file at index " << i; return false; } - if (!new_info[i].Save(reference_profile_files[i])) { - LOG(WARNING) << "Could not save reference profile file: " << reference_profile_files[i]; + if (!profile_files[i].GetFile()->ClearContent()) { + PLOG(WARNING) << "Could not clear profile file at index " << i; return false; } } } + *profile_compilation_info = result.release(); return true; } +class ScopedCollectionFlock { + public: + explicit ScopedCollectionFlock(size_t size) : flocks_(size) {} + + // Will block until all the locks are acquired. + bool Init(const std::vector<std::string>& filenames, /* out */ std::string* error) { + for (size_t i = 0; i < filenames.size(); i++) { + if (!flocks_[i].Init(filenames[i].c_str(), O_RDWR, /* block */ true, error)) { + *error += " (index=" + std::to_string(i) + ")"; + return false; + } + } + return true; + } + + // Will block until all the locks are acquired. + bool Init(const std::vector<uint32_t>& fds, /* out */ std::string* error) { + for (size_t i = 0; i < fds.size(); i++) { + // We do not own the descriptor, so disable auto-close and don't check usage. + File file(fds[i], false); + file.DisableAutoClose(); + if (!flocks_[i].Init(&file, error)) { + *error += " (index=" + std::to_string(i) + ")"; + return false; + } + } + return true; + } + + const std::vector<ScopedFlock>& Get() const { return flocks_; } + + private: + std::vector<ScopedFlock> flocks_; +}; + +bool ProfileAssistant::ProcessProfiles( + const std::vector<uint32_t>& profile_files_fd, + const std::vector<uint32_t>& reference_profile_files_fd, + /*out*/ ProfileCompilationInfo** profile_compilation_info) { + *profile_compilation_info = nullptr; + + std::string error; + ScopedCollectionFlock profile_files_flocks(profile_files_fd.size()); + if (!profile_files_flocks.Init(profile_files_fd, &error)) { + LOG(WARNING) << "Could not lock profile files: " << error; + return false; + } + ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files_fd.size()); + if (!reference_profile_files_flocks.Init(reference_profile_files_fd, &error)) { + LOG(WARNING) << "Could not lock reference profile files: " << error; + return false; + } + + return ProcessProfilesInternal(profile_files_flocks.Get(), + reference_profile_files_flocks.Get(), + profile_compilation_info); +} + +bool ProfileAssistant::ProcessProfiles( + const std::vector<std::string>& profile_files, + const std::vector<std::string>& reference_profile_files, + /*out*/ ProfileCompilationInfo** profile_compilation_info) { + *profile_compilation_info = nullptr; + + std::string error; + ScopedCollectionFlock profile_files_flocks(profile_files.size()); + if (!profile_files_flocks.Init(profile_files, &error)) { + LOG(WARNING) << "Could not lock profile files: " << error; + return false; + } + ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size()); + if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) { + LOG(WARNING) << "Could not lock reference profile files: " << error; + return false; + } + + return ProcessProfilesInternal(profile_files_flocks.Get(), + reference_profile_files_flocks.Get(), + profile_compilation_info); +} + } // namespace art diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h index 088c8bd1c7..ad5e2163cf 100644 --- a/compiler/profile_assistant.h +++ b/compiler/profile_assistant.h @@ -20,6 +20,7 @@ #include <string> #include <vector> +#include "base/scoped_flock.h" #include "jit/offline_profiling_info.cc" namespace art { @@ -52,7 +53,17 @@ class ProfileAssistant { const std::vector<std::string>& reference_profile_files, /*out*/ ProfileCompilationInfo** profile_compilation_info); + static bool ProcessProfiles( + const std::vector<uint32_t>& profile_files_fd_, + const std::vector<uint32_t>& reference_profile_files_fd_, + /*out*/ ProfileCompilationInfo** profile_compilation_info); + private: + static bool ProcessProfilesInternal( + const std::vector<ScopedFlock>& profile_files, + const std::vector<ScopedFlock>& reference_profile_files, + /*out*/ ProfileCompilationInfo** profile_compilation_info); + DISALLOW_COPY_AND_ASSIGN(ProfileAssistant); }; diff --git a/compiler/profile_assistant_test.cc b/compiler/profile_assistant_test.cc new file mode 100644 index 0000000000..58b7513377 --- /dev/null +++ b/compiler/profile_assistant_test.cc @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "base/unix_file/fd_file.h" +#include "common_runtime_test.h" +#include "compiler/profile_assistant.h" +#include "jit/offline_profiling_info.h" + +namespace art { + +class ProfileAssistantTest : public CommonRuntimeTest { + protected: + void SetupProfile(const std::string& id, + uint32_t checksum, + uint16_t number_of_methods, + const ScratchFile& profile, + ProfileCompilationInfo* info, + uint16_t start_method_index = 0) { + std::string dex_location1 = "location1" + id; + uint32_t dex_location_checksum1 = checksum; + std::string dex_location2 = "location2" + id; + uint32_t dex_location_checksum2 = 10 * checksum; + for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) { + ASSERT_TRUE(info->AddData(dex_location1, dex_location_checksum1, i)); + ASSERT_TRUE(info->AddData(dex_location2, dex_location_checksum2, i)); + } + ASSERT_TRUE(info->Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + } + + uint32_t GetFd(const ScratchFile& file) const { + return static_cast<uint32_t>(file.GetFd()); + } +}; + +TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { + ScratchFile profile1; + ScratchFile profile2; + ScratchFile reference_profile1; + ScratchFile reference_profile2; + + std::vector<uint32_t> profile_fds({ + GetFd(profile1), + GetFd(profile2)}); + std::vector<uint32_t> reference_profile_fds({ + GetFd(reference_profile1), + GetFd(reference_profile2)}); + + const uint16_t kNumberOfMethodsToEnableCompilation = 100; + ProfileCompilationInfo info1; + SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1); + ProfileCompilationInfo info2; + SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); + + // We should advise compilation. + ProfileCompilationInfo* result; + ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); + ASSERT_TRUE(result != nullptr); + + // The resulting compilation info must be equal to the merge of the inputs. + ProfileCompilationInfo expected; + ASSERT_TRUE(expected.Load(info1)); + ASSERT_TRUE(expected.Load(info2)); + ASSERT_TRUE(expected.Equals(*result)); + + // The information from profiles must be transfered to the reference profiles. + ProfileCompilationInfo file_info1; + ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); + ASSERT_TRUE(file_info1.Equals(info1)); + + ProfileCompilationInfo file_info2; + ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); + ASSERT_TRUE(file_info2.Equals(info2)); + + // Initial profiles must be cleared. + ASSERT_EQ(0, profile1.GetFile()->GetLength()); + ASSERT_EQ(0, profile2.GetFile()->GetLength()); +} + +TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { + ScratchFile profile1; + ScratchFile profile2; + ScratchFile reference_profile1; + ScratchFile reference_profile2; + + std::vector<uint32_t> profile_fds({ + GetFd(profile1), + GetFd(profile2)}); + std::vector<uint32_t> reference_profile_fds({ + GetFd(reference_profile1), + GetFd(reference_profile2)}); + + // The new profile info will contain the methods with indices 0-100. + const uint16_t kNumberOfMethodsToEnableCompilation = 100; + ProfileCompilationInfo info1; + SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1); + ProfileCompilationInfo info2; + SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); + + + // The reference profile info will contain the methods with indices 50-150. + const uint16_t kNumberOfMethodsAlreadyCompiled = 100; + ProfileCompilationInfo reference_info1; + SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile1, + &reference_info1, kNumberOfMethodsToEnableCompilation / 2); + ProfileCompilationInfo reference_info2; + SetupProfile("p2", 2, kNumberOfMethodsAlreadyCompiled, reference_profile2, + &reference_info2, kNumberOfMethodsToEnableCompilation / 2); + + // We should advise compilation. + ProfileCompilationInfo* result; + ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); + ASSERT_TRUE(result != nullptr); + + // The resulting compilation info must be equal to the merge of the inputs + ProfileCompilationInfo expected; + ASSERT_TRUE(expected.Load(info1)); + ASSERT_TRUE(expected.Load(info2)); + ASSERT_TRUE(expected.Load(reference_info1)); + ASSERT_TRUE(expected.Load(reference_info2)); + ASSERT_TRUE(expected.Equals(*result)); + + // The information from profiles must be transfered to the reference profiles. + ProfileCompilationInfo file_info1; + ProfileCompilationInfo merge1; + ASSERT_TRUE(merge1.Load(info1)); + ASSERT_TRUE(merge1.Load(reference_info1)); + ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1))); + ASSERT_TRUE(file_info1.Equals(merge1)); + + ProfileCompilationInfo file_info2; + ProfileCompilationInfo merge2; + ASSERT_TRUE(merge2.Load(info2)); + ASSERT_TRUE(merge2.Load(reference_info2)); + ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2))); + ASSERT_TRUE(file_info2.Equals(merge2)); + + // Initial profiles must be cleared. + ASSERT_EQ(0, profile1.GetFile()->GetLength()); + ASSERT_EQ(0, profile2.GetFile()->GetLength()); +} + +TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { + ScratchFile profile1; + ScratchFile profile2; + ScratchFile reference_profile1; + ScratchFile reference_profile2; + + std::vector<uint32_t> profile_fds({ + GetFd(profile1), + GetFd(profile2)}); + std::vector<uint32_t> reference_profile_fds({ + GetFd(reference_profile1), + GetFd(reference_profile2)}); + + const uint16_t kNumberOfMethodsToSkipCompilation = 1; + ProfileCompilationInfo info1; + SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, profile1, &info1); + ProfileCompilationInfo info2; + SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2); + + // We should not advise compilation. + ProfileCompilationInfo* result = nullptr; + ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); + ASSERT_TRUE(result == nullptr); + + // The information from profiles must remain the same. + ProfileCompilationInfo file_info1; + ASSERT_TRUE(profile1.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info1.Load(GetFd(profile1))); + ASSERT_TRUE(file_info1.Equals(info1)); + + ProfileCompilationInfo file_info2; + ASSERT_TRUE(profile2.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info2.Load(GetFd(profile2))); + ASSERT_TRUE(file_info2.Equals(info2)); + + // Reference profile files must remain empty. + ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); +} + +TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { + ScratchFile profile1; + ScratchFile profile2; + ScratchFile reference_profile1; + ScratchFile reference_profile2; + + std::vector<uint32_t> profile_fds({ + GetFd(profile1), + GetFd(profile2)}); + std::vector<uint32_t> reference_profile_fds({ + GetFd(reference_profile1), + GetFd(reference_profile2)}); + + const uint16_t kNumberOfMethodsToEnableCompilation = 100; + // Assign different hashes for the same dex file. This will make merging of information to fail. + ProfileCompilationInfo info1; + SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1); + ProfileCompilationInfo info2; + SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2); + + // We should fail processing. + ProfileCompilationInfo* result = nullptr; + ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); + ASSERT_TRUE(result == nullptr); + + // The information from profiles must still remain the same. + ProfileCompilationInfo file_info1; + ASSERT_TRUE(profile1.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info1.Load(GetFd(profile1))); + ASSERT_TRUE(file_info1.Equals(info1)); + + ProfileCompilationInfo file_info2; + ASSERT_TRUE(profile2.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info2.Load(GetFd(profile2))); + ASSERT_TRUE(file_info2.Equals(info2)); + + // Reference profile files must still remain empty. + ASSERT_EQ(0, reference_profile1.GetFile()->GetLength()); + ASSERT_EQ(0, reference_profile2.GetFile()->GetLength()); +} + +TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { + ScratchFile profile1; + ScratchFile reference_profile; + + std::vector<uint32_t> profile_fds({ + GetFd(profile1)}); + std::vector<uint32_t> reference_profile_fds({ + GetFd(reference_profile)}); + + const uint16_t kNumberOfMethodsToEnableCompilation = 100; + // Assign different hashes for the same dex file. This will make merging of information to fail. + ProfileCompilationInfo info1; + SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, profile1, &info1); + ProfileCompilationInfo reference_info; + SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info); + + // We should not advise compilation. + ProfileCompilationInfo* result = nullptr; + ASSERT_TRUE(profile1.GetFile()->ResetOffset()); + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result)); + ASSERT_TRUE(result == nullptr); + + // The information from profiles must still remain the same. + ProfileCompilationInfo file_info1; + ASSERT_TRUE(profile1.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info1.Load(GetFd(profile1))); + ASSERT_TRUE(file_info1.Equals(info1)); + + ProfileCompilationInfo file_info2; + ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); + ASSERT_TRUE(file_info2.Load(GetFd(reference_profile))); + ASSERT_TRUE(file_info2.Equals(reference_info)); +} + +} // namespace art diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index b6a228c13c..e57a540669 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -21,6 +21,7 @@ #include <set> #include <map> #include <vector> +#include <zlib.h> #include "base/bit_utils.h" #include "base/logging.h" @@ -161,7 +162,6 @@ class TestDexFileBuilder { uint32_t total_size = data_section_offset + data_section_size; dex_file_data_.resize(total_size); - std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); for (const auto& entry : strings_) { CHECK_LT(entry.first.size(), 128u); @@ -210,7 +210,12 @@ class TestDexFileBuilder { Write32(raw_offset + 4u, GetStringIdx(entry.first.name)); } - // Leave checksum and signature as zeros. + // Leave signature as zeros. + + header->file_size_ = dex_file_data_.size(); + size_t skip = sizeof(header->magic_) + sizeof(header->checksum_); + header->checksum_ = adler32(0u, dex_file_data_.data() + skip, dex_file_data_.size() - skip); + std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); std::string error_msg; std::unique_ptr<const DexFile> dex_file(DexFile::Open( diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index d6caa3c338..7138a46890 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -186,6 +186,22 @@ void X86Assembler::bsrl(Register dst, const Address& src) { EmitOperand(dst, src); } +void X86Assembler::popcntl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0xB8); + EmitRegisterOperand(dst, src); +} + +void X86Assembler::popcntl(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0xB8); + EmitOperand(dst, src); +} + void X86Assembler::movzxb(Register dst, ByteRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 655af9c184..759a41e80e 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -330,11 +330,15 @@ class X86Assembler FINAL : public Assembler { void movntl(const Address& dst, Register src); void bswapl(Register dst); + void bsfl(Register dst, Register src); void bsfl(Register dst, const Address& src); void bsrl(Register dst, Register src); void bsrl(Register dst, const Address& src); + void popcntl(Register dst, Register src); + void popcntl(Register dst, const Address& src); + void rorl(Register reg, const Immediate& imm); void rorl(Register operand, Register shifter); void roll(Register reg, const Immediate& imm); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index a9b991c7a0..0fd098227a 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -260,6 +260,19 @@ TEST_F(AssemblerX86Test, BsrlAddress) { DriverStr(expected, "bsrl_address"); } +TEST_F(AssemblerX86Test, Popcntl) { + DriverStr(RepeatRR(&x86::X86Assembler::popcntl, "popcntl %{reg2}, %{reg1}"), "popcntl"); +} + +TEST_F(AssemblerX86Test, PopcntlAddress) { + GetAssembler()->popcntl(x86::Register(x86::EDI), x86::Address( + x86::Register(x86::EDI), x86::Register(x86::EBX), x86::TIMES_4, 12)); + const char* expected = + "popcntl 0xc(%EDI,%EBX,4), %EDI\n"; + + DriverStr(expected, "popcntl_address"); +} + // Rorl only allows CL as the shift count. std::string rorl_fn(AssemblerX86Test::Base* assembler_test, x86::X86Assembler* assembler) { std::ostringstream str; diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index db072678ef..10f5a005e1 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -2247,6 +2247,42 @@ void X86_64Assembler::bsrq(CpuRegister dst, const Address& src) { EmitOperand(dst.LowBits(), src); } +void X86_64Assembler::popcntl(CpuRegister dst, CpuRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xB8); + EmitRegisterOperand(dst.LowBits(), src.LowBits()); +} + +void X86_64Assembler::popcntl(CpuRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xB8); + EmitOperand(dst.LowBits(), src); +} + +void X86_64Assembler::popcntq(CpuRegister dst, CpuRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitRex64(dst, src); + EmitUint8(0x0F); + EmitUint8(0xB8); + EmitRegisterOperand(dst.LowBits(), src.LowBits()); +} + +void X86_64Assembler::popcntq(CpuRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitRex64(dst, src); + EmitUint8(0x0F); + EmitUint8(0xB8); + EmitOperand(dst.LowBits(), src); +} + void X86_64Assembler::repne_scasw() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 01d28e305d..6f0847eb61 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -647,6 +647,11 @@ class X86_64Assembler FINAL : public Assembler { void bsrq(CpuRegister dst, CpuRegister src); void bsrq(CpuRegister dst, const Address& src); + void popcntl(CpuRegister dst, CpuRegister src); + void popcntl(CpuRegister dst, const Address& src); + void popcntq(CpuRegister dst, CpuRegister src); + void popcntq(CpuRegister dst, const Address& src); + void rorl(CpuRegister reg, const Immediate& imm); void rorl(CpuRegister operand, CpuRegister shifter); void roll(CpuRegister reg, const Immediate& imm); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 00bb5ca36b..8a87fca96a 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1333,6 +1333,44 @@ TEST_F(AssemblerX86_64Test, BsrqAddress) { DriverStr(expected, "bsrq_address"); } +TEST_F(AssemblerX86_64Test, Popcntl) { + DriverStr(Repeatrr(&x86_64::X86_64Assembler::popcntl, "popcntl %{reg2}, %{reg1}"), "popcntl"); +} + +TEST_F(AssemblerX86_64Test, PopcntlAddress) { + GetAssembler()->popcntl(x86_64::CpuRegister(x86_64::R10), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); + GetAssembler()->popcntl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); + GetAssembler()->popcntl(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); + const char* expected = + "popcntl 0xc(%RDI,%RBX,4), %R10d\n" + "popcntl 0xc(%R10,%RBX,4), %edi\n" + "popcntl 0xc(%RDI,%R9,4), %edi\n"; + + DriverStr(expected, "popcntl_address"); +} + +TEST_F(AssemblerX86_64Test, Popcntq) { + DriverStr(RepeatRR(&x86_64::X86_64Assembler::popcntq, "popcntq %{reg2}, %{reg1}"), "popcntq"); +} + +TEST_F(AssemblerX86_64Test, PopcntqAddress) { + GetAssembler()->popcntq(x86_64::CpuRegister(x86_64::R10), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); + GetAssembler()->popcntq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); + GetAssembler()->popcntq(x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12)); + const char* expected = + "popcntq 0xc(%RDI,%RBX,4), %R10\n" + "popcntq 0xc(%R10,%RBX,4), %RDI\n" + "popcntq 0xc(%RDI,%R9,4), %RDI\n"; + + DriverStr(expected, "popcntq_address"); +} + ///////////////// // Near labels // ///////////////// diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index dbb7341fbd..00941edc17 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -318,6 +318,11 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(""); UsageError(" --no-generate-debug-info: Do not generate debug information for native debugging."); UsageError(""); + UsageError(" --generate-mini-debug-info: Generate minimal amount of LZMA-compressed"); + UsageError(" debug information necessary to print backtraces. (disabled by default)"); + UsageError(""); + UsageError(" --no-generate-mini-debug-info: Do do generated backtrace info."); + UsageError(""); UsageError(" --debuggable: Produce code debuggable with Java debugger."); UsageError(""); UsageError(" --native-debuggable: Produce code debuggable with native debugger (like LLDB)."); @@ -340,6 +345,12 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" --profile-file will be merged into --reference-profile-file. Valid only when"); UsageError(" specified together with --profile-file."); UsageError(""); + UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor."); + UsageError(" Cannot be used together with --profile-file."); + UsageError(""); + UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); + UsageError(" accepts a file descriptor. Cannot be used together with"); + UsageError(" --reference-profile-file."); UsageError(" --print-pass-names: print a list of pass names"); UsageError(""); UsageError(" --disable-passes=<pass-names>: disable one or more passes separated by comma."); @@ -497,12 +508,24 @@ static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) { return dex_files_size >= kMinDexFileCumulativeSizeForSwap; } +static void CloseAllFds(const std::vector<uint32_t>& fds, const char* descriptor) { + for (size_t i = 0; i < fds.size(); i++) { + if (close(fds[i]) < 0) { + PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i; + } + } +} + class Dex2Oat FINAL { public: explicit Dex2Oat(TimingLogger* timings) : compiler_kind_(Compiler::kOptimizing), instruction_set_(kRuntimeISA), // Take the default set of instruction features from the build. + image_file_location_oat_checksum_(0), + image_file_location_oat_data_begin_(0), + image_patch_delta_(0), + key_value_store_(nullptr), verification_results_(nullptr), method_inliner_map_(), runtime_(nullptr), @@ -522,22 +545,23 @@ class Dex2Oat FINAL { boot_image_(false), multi_image_(false), is_host_(false), + class_loader_(nullptr), + elf_writers_(), + oat_writers_(), + rodata_(), image_writer_(nullptr), driver_(nullptr), + opened_dex_files_maps_(), + opened_dex_files_(), dump_stats_(false), dump_passes_(false), dump_timing_(false), dump_slow_timing_(kIsDebugBuild), swap_fd_(-1), - app_image_fd_(kInvalidImageFd), + app_image_fd_(kInvalidFd), timings_(timings) {} ~Dex2Oat() { - // Free opened dex files before deleting the runtime_, because ~DexFile - // uses MemMap, which is shut down by ~Runtime. - class_path_files_.clear(); - opened_dex_files_.clear(); - // Log completion time before deleting the runtime_, because this accesses // the runtime. LogCompletionTime(); @@ -550,6 +574,9 @@ class Dex2Oat FINAL { for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files_) { dex_file.release(); } + for (std::unique_ptr<MemMap>& map : opened_dex_files_maps_) { + map.release(); + } for (std::unique_ptr<File>& oat_file : oat_files_) { oat_file.release(); } @@ -575,6 +602,14 @@ class Dex2Oat FINAL { ParseUintOption(option, "--oat-fd", &oat_fd_, Usage); } + void ParseFdForCollection(const StringPiece& option, + const char* arg_name, + std::vector<uint32_t>* fds) { + uint32_t fd; + ParseUintOption(option, arg_name, &fd, Usage); + fds->push_back(fd); + } + void ParseJ(const StringPiece& option) { ParseUintOption(option, "-j", &thread_count_, Usage, /* is_long_option */ false); } @@ -778,11 +813,25 @@ class Dex2Oat FINAL { } } + if (!profile_files_.empty() && !profile_files_fd_.empty()) { + Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); + } if (!profile_files_.empty()) { if (!reference_profile_files_.empty() && (reference_profile_files_.size() != profile_files_.size())) { Usage("If specified, --reference-profile-file should match the number of --profile-file."); } + } else if (!reference_profile_files_.empty()) { + Usage("--reference-profile-file should only be supplied with --profile-file"); + } + if (!profile_files_fd_.empty()) { + if (!reference_profile_files_fd_.empty() && + (reference_profile_files_fd_.size() != profile_files_fd_.size())) { + Usage("If specified, --reference-profile-file-fd should match the number", + " of --profile-file-fd."); + } + } else if (!reference_profile_files_fd_.empty()) { + Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd"); } if (!parser_options->oat_symbols.empty()) { @@ -1076,6 +1125,10 @@ class Dex2Oat FINAL { } else if (option.starts_with("--reference-profile-file=")) { reference_profile_files_.push_back( option.substr(strlen("--reference-profile-file=")).ToString()); + } else if (option.starts_with("--profile-file-fd=")) { + ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_); + } else if (option.starts_with("--reference-profile-file-fd=")) { + ParseFdForCollection(option, "--reference_profile-file-fd", &reference_profile_files_fd_); } else if (option == "--no-profile-file") { // No profile } else if (option == "--host") { @@ -1125,6 +1178,9 @@ class Dex2Oat FINAL { // Check whether the oat output files are writable, and open them for later. Also open a swap // file, if a name is given. bool OpenFile() { + // Prune non-existent dex files now so that we don't create empty oat files for multi-image. + PruneNonExistentDexFiles(); + // Expand oat and image filenames for multi image. if (IsBootImage() && multi_image_) { ExpandOatAndImageFilenames(); @@ -1196,9 +1252,6 @@ class Dex2Oat FINAL { } // Note that dex2oat won't close the swap_fd_. The compiler driver's swap space will do that. - // Organize inputs, handling multi-dex and multiple oat file outputs. - CreateDexOatMappings(); - return true; } @@ -1241,89 +1294,135 @@ class Dex2Oat FINAL { return false; } + CreateOatWriters(); + if (!AddDexFileSources()) { + return false; + } + + if (IsBootImage() && image_filenames_.size() > 1) { + // If we're compiling the boot image, store the boot classpath into the Key-Value store. + // We need this for the multi-image case. + key_value_store_->Put(OatHeader::kBootClassPath, GetMultiImageBootClassPath()); + } + + if (!IsBootImage()) { + // When compiling an app, create the runtime early to retrieve + // the image location key needed for the oat header. + if (!CreateRuntime(std::move(runtime_options))) { + return false; + } + + { + TimingLogger::ScopedTiming t3("Loading image checksum", timings_); + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum(); + image_file_location_oat_data_begin_ = + reinterpret_cast<uintptr_t>(image_spaces[0]->GetImageHeader().GetOatDataBegin()); + image_patch_delta_ = image_spaces[0]->GetImageHeader().GetPatchDelta(); + // Store the boot image filename(s). + std::vector<std::string> image_filenames; + for (const gc::space::ImageSpace* image_space : image_spaces) { + image_filenames.push_back(image_space->GetImageFilename()); + } + std::string image_file_location = Join(image_filenames, ':'); + if (!image_file_location.empty()) { + key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location); + } + } + + // Open dex files for class path. + const std::vector<std::string> class_path_locations = + GetClassPathLocations(runtime_->GetClassPathString()); + OpenClassPathFiles(class_path_locations, &class_path_files_); + + // Store the classpath we have right now. + std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_); + key_value_store_->Put(OatHeader::kClassPathKey, + OatFile::EncodeDexFileDependencies(class_path_files)); + } + + // Now that we have finalized key_value_store_, start writing the oat file. { - TimingLogger::ScopedTiming t_runtime("Create runtime", timings_); + TimingLogger::ScopedTiming t_dex("Writing and opening dex files", timings_); + rodata_.reserve(oat_writers_.size()); + for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) { + rodata_.push_back(elf_writers_[i]->StartRoData()); + // Unzip or copy dex files straight to the oat file. + std::unique_ptr<MemMap> opened_dex_files_map; + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + if (!oat_writers_[i]->WriteAndOpenDexFiles(rodata_.back(), + oat_files_[i].get(), + instruction_set_, + instruction_set_features_.get(), + key_value_store_.get(), + &opened_dex_files_map, + &opened_dex_files)) { + return false; + } + dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files)); + if (opened_dex_files_map != nullptr) { + opened_dex_files_maps_.push_back(std::move(opened_dex_files_map)); + for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) { + dex_file_oat_filename_map_.emplace(dex_file.get(), oat_filenames_[i]); + opened_dex_files_.push_back(std::move(dex_file)); + } + } else { + DCHECK(opened_dex_files.empty()); + } + } + } + + dex_files_ = MakeNonOwningPointerVector(opened_dex_files_); + if (IsBootImage()) { + // For boot image, pass opened dex files to the Runtime::Create(). + // Note: Runtime acquires ownership of these dex files. + runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_); if (!CreateRuntime(std::move(runtime_options))) { return false; } } - // Runtime::Create acquired the mutator_lock_ that is normally given away when we - // Runtime::Start, give it away now so that we don't starve GC. - Thread* self = Thread::Current(); - self->TransitionFromRunnableToSuspended(kNative); // If we're doing the image, override the compiler filter to force full compilation. Must be // done ahead of WellKnownClasses::Init that causes verification. Note: doesn't force // compilation of class initializers. // Whilst we're in native take the opportunity to initialize well known classes. + Thread* self = Thread::Current(); WellKnownClasses::Init(self->GetJniEnv()); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - if (boot_image_filename_.empty()) { - dex_files_ = class_linker->GetBootClassPath(); - // Prune invalid dex locations. - for (size_t i = 0; i < dex_locations_.size(); i++) { - const char* dex_location = dex_locations_[i]; - bool contains = false; - for (const DexFile* dex_file : dex_files_) { - if (strcmp(dex_location, dex_file->GetLocation().c_str()) == 0) { - contains = true; - break; - } - } - if (!contains) { - dex_locations_.erase(dex_locations_.begin() + i); - i--; - } - } - } else { - TimingLogger::ScopedTiming t_dex("Opening dex files", timings_); - if (dex_filenames_.empty()) { - ATRACE_BEGIN("Opening zip archive from file descriptor"); - std::string error_msg; - std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd_, - zip_location_.c_str(), - &error_msg)); - if (zip_archive.get() == nullptr) { - LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location_ << "': " - << error_msg; - return false; - } - if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location_, &error_msg, &opened_dex_files_)) { - LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location_ - << "': " << error_msg; - return false; - } - for (auto& dex_file : opened_dex_files_) { - dex_files_.push_back(dex_file.get()); - } - ATRACE_END(); - } else { - size_t failure_count = OpenDexFiles(dex_filenames_, dex_locations_, &opened_dex_files_); - if (failure_count > 0) { - LOG(ERROR) << "Failed to open some dex files: " << failure_count; - return false; - } - for (auto& dex_file : opened_dex_files_) { - dex_files_.push_back(dex_file.get()); - } - } - + if (!IsBootImage()) { constexpr bool kSaveDexInput = false; if (kSaveDexInput) { SaveDexInput(); } + + // Handle and ClassLoader creation needs to come after Runtime::Create. + ScopedObjectAccess soa(self); + + // Classpath: first the class-path given. + std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_); + + // Then the dex files we'll compile. Thus we'll resolve the class-path first. + class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end()); + + class_loader_ = class_linker->CreatePathClassLoader(self, class_path_files); + } + + // Ensure opened dex files are writable for dex-to-dex transformations. + for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) { + if (!map->Protect(PROT_READ | PROT_WRITE)) { + PLOG(ERROR) << "Failed to make .dex files writeable."; + return false; + } } - // Ensure opened dex files are writable for dex-to-dex transformations. Also ensure that - // the dex caches stay live since we don't want class unloading to occur during compilation. + + // Ensure that the dex caches stay live since we don't want class unloading + // to occur during compilation. for (const auto& dex_file : dex_files_) { - if (!dex_file->EnableWrite()) { - PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n"; - } ScopedObjectAccess soa(self); dex_caches_.push_back(soa.AddLocalReference<jobject>( class_linker->RegisterDexFile(*dex_file, Runtime::Current()->GetLinearAlloc()))); - dex_file->CreateTypeLookupTable(); } /* @@ -1348,30 +1447,9 @@ class Dex2Oat FINAL { return true; } - void CreateDexOatMappings() { - if (oat_files_.size() > 1) { - size_t index = 0; - for (size_t i = 0; i < oat_files_.size(); ++i) { - std::vector<const DexFile*> dex_files; - if (index < dex_files_.size()) { - dex_files.push_back(dex_files_[index]); - dex_file_oat_filename_map_.emplace(dex_files_[index], oat_filenames_[i]); - index++; - while (index < dex_files_.size() && - (dex_files_[index]->GetBaseLocation() == dex_files_[index - 1]->GetBaseLocation())) { - dex_file_oat_filename_map_.emplace(dex_files_[index], oat_filenames_[i]); - dex_files.push_back(dex_files_[index]); - index++; - } - } - dex_files_per_oat_file_.push_back(std::move(dex_files)); - } - } else { - dex_files_per_oat_file_.push_back(dex_files_); - for (const DexFile* dex_file : dex_files_) { - dex_file_oat_filename_map_.emplace(dex_file, oat_filenames_[0]); - } - } + // If we need to keep the oat file open for the image writer. + bool ShouldKeepOatFileOpen() const { + return IsImage() && oat_fd_ != kInvalidFd; } // Create and invoke the compiler driver. This will compile all the dex files. @@ -1379,28 +1457,6 @@ class Dex2Oat FINAL { TimingLogger::ScopedTiming t("dex2oat Compile", timings_); compiler_phases_timings_.reset(new CumulativeLogger("compilation times")); - // Handle and ClassLoader creation needs to come after Runtime::Create - jobject class_loader = nullptr; - Thread* self = Thread::Current(); - - if (!boot_image_filename_.empty()) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_); - ScopedObjectAccess soa(self); - - // Classpath: first the class-path given. - std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_); - - // Store the classpath we have right now. - key_value_store_->Put(OatHeader::kClassPathKey, - OatFile::EncodeDexFileDependencies(class_path_files)); - - // Then the dex files we'll compile. Thus we'll resolve the class-path first. - class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end()); - - class_loader = class_linker->CreatePathClassLoader(self, class_path_files); - } - // Find the dex file we should not inline from. // For now, on the host always have core-oj removed. @@ -1448,49 +1504,6 @@ class Dex2Oat FINAL { } } - if (IsBootImage() && image_filenames_.size() > 1) { - // If we're compiling the boot image, store the boot classpath into the Key-Value store. If - // the image filename was adapted (e.g., for our tests), we need to change this here, too, but - // need to strip all path components (they will be re-established when loading). - // We need this for the multi-image case. - std::ostringstream bootcp_oss; - bool first_bootcp = true; - for (size_t i = 0; i < dex_locations_.size(); ++i) { - if (!first_bootcp) { - bootcp_oss << ":"; - } - - std::string dex_loc = dex_locations_[i]; - std::string image_filename = image_filenames_[i]; - - // Use the dex_loc path, but the image_filename name (without path elements). - size_t dex_last_slash = dex_loc.rfind('/'); - - // npos is max(size_t). That makes this a bit ugly. - size_t image_last_slash = image_filename.rfind('/'); - size_t image_last_at = image_filename.rfind('@'); - size_t image_last_sep = (image_last_slash == std::string::npos) - ? image_last_at - : (image_last_at == std::string::npos) - ? std::string::npos - : std::max(image_last_slash, image_last_at); - // Note: whenever image_last_sep == npos, +1 overflow means using the full string. - - if (dex_last_slash == std::string::npos) { - dex_loc = image_filename.substr(image_last_sep + 1); - } else { - dex_loc = dex_loc.substr(0, dex_last_slash + 1) + - image_filename.substr(image_last_sep + 1); - } - - // Image filenames already end with .art, no need to replace. - - bootcp_oss << dex_loc; - first_bootcp = false; - } - key_value_store_->Put(OatHeader::kBootClassPath, bootcp_oss.str()); - } - driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), &method_inliner_map_, @@ -1509,7 +1522,7 @@ class Dex2Oat FINAL { &dex_file_oat_filename_map_, profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); - driver_->CompileAll(class_loader, dex_files_, timings_); + driver_->CompileAll(class_loader_, dex_files_, timings_); } // Notes on the interleaving of creating the images and oat files to @@ -1577,71 +1590,42 @@ class Dex2Oat FINAL { // ImageWriter, if necessary. // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure // case (when the file will be explicitly erased). - bool CreateOatFiles() { - CHECK(key_value_store_.get() != nullptr); - + bool WriteOatFiles() { TimingLogger::ScopedTiming t("dex2oat Oat", timings_); - std::vector<std::unique_ptr<OatWriter>> oat_writers; - { - TimingLogger::ScopedTiming t2("dex2oat OatWriter", timings_); - std::string image_file_location; - uint32_t image_file_location_oat_checksum = 0; - uintptr_t image_file_location_oat_data_begin = 0; - int32_t image_patch_delta = 0; + // Sync the data to the file, in case we did dex2dex transformations. + for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) { + if (!map->Sync()) { + PLOG(ERROR) << "Failed to Sync() dex2dex output. Map: " << map->GetName(); + return false; + } + } + if (IsImage()) { if (app_image_ && image_base_ == 0) { - std::vector<gc::space::ImageSpace*> image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - for (gc::space::ImageSpace* image_space : image_spaces) { + gc::Heap* const heap = Runtime::Current()->GetHeap(); + for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) { image_base_ = std::max(image_base_, RoundUp( reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()), kPageSize)); } + // The non moving space is right after the oat file. Put the preferred app image location + // right after the non moving space so that we ideally get a continuous immune region for + // the GC. + const size_t non_moving_space_capacity = heap->GetNonMovingSpace()->Capacity(); + image_base_ += non_moving_space_capacity; VLOG(compiler) << "App image base=" << reinterpret_cast<void*>(image_base_); } - if (IsImage()) { - PrepareImageWriter(image_base_); - } - - if (!IsBootImage()) { - TimingLogger::ScopedTiming t3("Loading image checksum", timings_); - std::vector<gc::space::ImageSpace*> image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - image_file_location_oat_checksum = image_spaces[0]->GetImageHeader().GetOatChecksum(); - image_file_location_oat_data_begin = - reinterpret_cast<uintptr_t>(image_spaces[0]->GetImageHeader().GetOatDataBegin()); - image_patch_delta = image_spaces[0]->GetImageHeader().GetPatchDelta(); - std::vector<std::string> image_filenames; - for (const gc::space::ImageSpace* image_space : image_spaces) { - image_filenames.push_back(image_space->GetImageFilename()); - } - image_file_location = Join(image_filenames, ':'); - } - - if (!image_file_location.empty()) { - key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location); - } + image_writer_.reset(new ImageWriter(*driver_, + image_base_, + compiler_options_->GetCompilePic(), + IsAppImage(), + image_storage_mode_, + oat_filenames_, + dex_file_oat_filename_map_)); - for (size_t i = 0; i < oat_files_.size(); ++i) { - std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i]; - std::unique_ptr<OatWriter> oat_writer(new OatWriter(dex_files, - image_file_location_oat_checksum, - image_file_location_oat_data_begin, - image_patch_delta, - driver_.get(), - image_writer_.get(), - IsBootImage(), - timings_, - key_value_store_.get())); - oat_writers.push_back(std::move(oat_writer)); - } - } - - if (IsImage()) { - // The OatWriter constructor has already updated offsets in methods and we need to - // prepare method offsets in the image address space for direct method patching. + // We need to prepare method offsets in the image address space for direct method patching. TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_); if (!image_writer_->PrepareImageAddressSpace()) { LOG(ERROR) << "Failed to prepare image address space."; @@ -1651,20 +1635,22 @@ class Dex2Oat FINAL { { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); - for (size_t i = 0; i < oat_files_.size(); ++i) { + for (size_t i = 0, size = oat_files_.size(); i != size; ++i) { std::unique_ptr<File>& oat_file = oat_files_[i]; - std::unique_ptr<OatWriter>& oat_writer = oat_writers[i]; - std::unique_ptr<ElfWriter> elf_writer = - CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get()); + std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i]; + std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i]; - elf_writer->Start(); + std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i]; + oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files); - OutputStream* rodata = elf_writer->StartRoData(); + OutputStream*& rodata = rodata_[i]; + DCHECK(rodata != nullptr); if (!oat_writer->WriteRodata(rodata)) { LOG(ERROR) << "Failed to write .rodata section to the ELF file " << oat_file->GetPath(); return false; } elf_writer->EndRoData(rodata); + rodata = nullptr; OutputStream* text = elf_writer->StartText(); if (!oat_writer->WriteCode(text)) { @@ -1673,6 +1659,14 @@ class Dex2Oat FINAL { } elf_writer->EndText(text); + if (!oat_writer->WriteHeader(elf_writer->GetStream(), + image_file_location_oat_checksum_, + image_file_location_oat_data_begin_, + image_patch_delta_)) { + LOG(ERROR) << "Failed to write oat header to the ELF file " << oat_file->GetPath(); + return false; + } + elf_writer->SetBssSize(oat_writer->GetBssSize()); elf_writer->WriteDynamicSection(); elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); @@ -1698,6 +1692,9 @@ class Dex2Oat FINAL { } VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i]; + + oat_writer.reset(); + elf_writer.reset(); } } @@ -1815,17 +1812,27 @@ class Dex2Oat FINAL { } bool UseProfileGuidedCompilation() const { - return !profile_files_.empty(); + return !profile_files_.empty() || !profile_files_fd_.empty(); } bool ProcessProfiles() { DCHECK(UseProfileGuidedCompilation()); ProfileCompilationInfo* info = nullptr; - if (ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_files_, &info)) { - profile_compilation_info_.reset(info); - return true; + bool result = false; + if (profile_files_.empty()) { + DCHECK(!profile_files_fd_.empty()); + result = ProfileAssistant::ProcessProfiles( + profile_files_fd_, reference_profile_files_fd_, &info); + CloseAllFds(profile_files_fd_, "profile_files_fd_"); + CloseAllFds(reference_profile_files_fd_, "reference_profile_files_fd_"); + } else { + result = ProfileAssistant::ProcessProfiles( + profile_files_, reference_profile_files_, &info); } - return false; + + profile_compilation_info_.reset(info); + + return result; } bool ShouldCompileBasedOnProfiles() const { @@ -1845,65 +1852,78 @@ class Dex2Oat FINAL { return result; } - static size_t OpenDexFiles(std::vector<const char*>& dex_filenames, - std::vector<const char*>& dex_locations, - std::vector<std::unique_ptr<const DexFile>>* dex_files) { - DCHECK(dex_files != nullptr) << "OpenDexFiles out-param is nullptr"; - size_t failure_count = 0; - for (size_t i = 0; i < dex_filenames.size(); i++) { - const char* dex_filename = dex_filenames[i]; - const char* dex_location = dex_locations[i]; - ATRACE_BEGIN(StringPrintf("Opening dex file '%s'", dex_filenames[i]).c_str()); - std::string error_msg; - if (!OS::FileExists(dex_filename)) { - LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; - dex_filenames.erase(dex_filenames.begin() + i); - dex_locations.erase(dex_locations.begin() + i); - i--; - continue; - } - if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) { - LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; - ++failure_count; - } - ATRACE_END(); - } - return failure_count; - } - - // Returns true if dex_files has a dex with the named location. We compare canonical locations, - // so that relative and absolute paths will match. Not caching for the dex_files isn't very - // efficient, but under normal circumstances the list is neither large nor is this part too - // sensitive. - static bool DexFilesContains(const std::vector<const DexFile*>& dex_files, - const std::string& location) { - std::string canonical_location(DexFile::GetDexCanonicalLocation(location.c_str())); - for (size_t i = 0; i < dex_files.size(); ++i) { - if (DexFile::GetDexCanonicalLocation(dex_files[i]->GetLocation().c_str()) == - canonical_location) { - return true; + std::string GetMultiImageBootClassPath() { + DCHECK(IsBootImage()); + DCHECK_GT(oat_filenames_.size(), 1u); + // If the image filename was adapted (e.g., for our tests), we need to change this here, + // too, but need to strip all path components (they will be re-established when loading). + std::ostringstream bootcp_oss; + bool first_bootcp = true; + for (size_t i = 0; i < dex_locations_.size(); ++i) { + if (!first_bootcp) { + bootcp_oss << ":"; + } + + std::string dex_loc = dex_locations_[i]; + std::string image_filename = image_filenames_[i]; + + // Use the dex_loc path, but the image_filename name (without path elements). + size_t dex_last_slash = dex_loc.rfind('/'); + + // npos is max(size_t). That makes this a bit ugly. + size_t image_last_slash = image_filename.rfind('/'); + size_t image_last_at = image_filename.rfind('@'); + size_t image_last_sep = (image_last_slash == std::string::npos) + ? image_last_at + : (image_last_at == std::string::npos) + ? std::string::npos + : std::max(image_last_slash, image_last_at); + // Note: whenever image_last_sep == npos, +1 overflow means using the full string. + + if (dex_last_slash == std::string::npos) { + dex_loc = image_filename.substr(image_last_sep + 1); + } else { + dex_loc = dex_loc.substr(0, dex_last_slash + 1) + + image_filename.substr(image_last_sep + 1); } + + // Image filenames already end with .art, no need to replace. + + bootcp_oss << dex_loc; + first_bootcp = false; } - return false; + return bootcp_oss.str(); } - // Appends to opened_dex_files any elements of class_path that dex_files - // doesn't already contain. This will open those dex files as necessary. - static void OpenClassPathFiles(const std::string& class_path, - std::vector<const DexFile*> dex_files, - std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { - DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is nullptr"; + std::vector<std::string> GetClassPathLocations(const std::string& class_path) { + // This function is used only for apps and for an app we have exactly one oat file. + DCHECK(!IsBootImage()); + DCHECK_EQ(oat_writers_.size(), 1u); + std::vector<std::string> dex_files_canonical_locations; + for (const char* location : oat_writers_[0]->GetSourceLocations()) { + dex_files_canonical_locations.push_back(DexFile::GetDexCanonicalLocation(location)); + } + std::vector<std::string> parsed; Split(class_path, ':', &parsed); - // Take Locks::mutator_lock_ so that lock ordering on the ClassLinker::dex_lock_ is maintained. - ScopedObjectAccess soa(Thread::Current()); - for (size_t i = 0; i < parsed.size(); ++i) { - if (DexFilesContains(dex_files, parsed[i])) { - continue; - } + auto kept_it = std::remove_if(parsed.begin(), + parsed.end(), + [dex_files_canonical_locations](const std::string& location) { + return ContainsElement(dex_files_canonical_locations, + DexFile::GetDexCanonicalLocation(location.c_str())); + }); + parsed.erase(kept_it, parsed.end()); + return parsed; + } + + // Opens requested class path files and appends them to opened_dex_files. + static void OpenClassPathFiles(const std::vector<std::string>& class_path_locations, + std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { + DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is nullptr"; + for (const std::string& location : class_path_locations) { std::string error_msg; - if (!DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg, opened_dex_files)) { - LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg; + if (!DexFile::Open(location.c_str(), location.c_str(), &error_msg, opened_dex_files)) { + LOG(WARNING) << "Failed to open dex file '" << location << "': " << error_msg; } } } @@ -1978,6 +1998,63 @@ class Dex2Oat FINAL { return true; } + void PruneNonExistentDexFiles() { + DCHECK_EQ(dex_filenames_.size(), dex_locations_.size()); + size_t kept = 0u; + for (size_t i = 0, size = dex_filenames_.size(); i != size; ++i) { + if (!OS::FileExists(dex_filenames_[i])) { + LOG(WARNING) << "Skipping non-existent dex file '" << dex_filenames_[i] << "'"; + } else { + dex_filenames_[kept] = dex_filenames_[i]; + dex_locations_[kept] = dex_locations_[i]; + ++kept; + } + } + dex_filenames_.resize(kept); + dex_locations_.resize(kept); + } + + bool AddDexFileSources() { + TimingLogger::ScopedTiming t2("AddDexFileSources", timings_); + if (zip_fd_ != -1) { + DCHECK_EQ(oat_writers_.size(), 1u); + if (!oat_writers_[0]->AddZippedDexFilesSource(ScopedFd(zip_fd_), zip_location_.c_str())) { + return false; + } + } else if (oat_writers_.size() > 1u) { + // Multi-image. + DCHECK_EQ(oat_writers_.size(), dex_filenames_.size()); + DCHECK_EQ(oat_writers_.size(), dex_locations_.size()); + for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) { + if (!oat_writers_[i]->AddDexFileSource(dex_filenames_[i], dex_locations_[i])) { + return false; + } + } + } else { + DCHECK_EQ(oat_writers_.size(), 1u); + DCHECK_EQ(dex_filenames_.size(), dex_locations_.size()); + DCHECK_NE(dex_filenames_.size(), 0u); + for (size_t i = 0; i != dex_filenames_.size(); ++i) { + if (!oat_writers_[0]->AddDexFileSource(dex_filenames_[i], dex_locations_[i])) { + return false; + } + } + } + return true; + } + + void CreateOatWriters() { + TimingLogger::ScopedTiming t2("CreateOatWriters", timings_); + elf_writers_.reserve(oat_files_.size()); + oat_writers_.reserve(oat_files_.size()); + for (const std::unique_ptr<File>& oat_file : oat_files_) { + elf_writers_.emplace_back( + CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get())); + elf_writers_.back()->Start(); + oat_writers_.emplace_back(new OatWriter(IsBootImage(), timings_)); + } + } + void SaveDexInput() { for (size_t i = 0; i < dex_files_.size(); ++i) { const DexFile* dex_file = dex_files_[i]; @@ -2028,6 +2105,10 @@ class Dex2Oat FINAL { // Disable libsigchain. We don't don't need it during compilation and it prevents us // from getting a statically linked version of dex2oat (because of dlsym and RTLD_NEXT). raw_options.push_back(std::make_pair("-Xno-sig-chain", nullptr)); + // Disable Hspace compaction to save heap size virtual space. + // Only need disable Hspace for OOM becasue background collector is equal to + // foreground collector by default for dex2oat. + raw_options.push_back(std::make_pair("-XX:DisableHSpaceCompactForOOM", nullptr)); if (!Runtime::ParseOptions(raw_options, false, runtime_options)) { LOG(ERROR) << "Failed to parse runtime options"; @@ -2037,8 +2118,8 @@ class Dex2Oat FINAL { } // Create a runtime necessary for compilation. - bool CreateRuntime(RuntimeArgumentMap&& runtime_options) - SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) { + bool CreateRuntime(RuntimeArgumentMap&& runtime_options) { + TimingLogger::ScopedTiming t_runtime("Create runtime", timings_); if (!Runtime::Create(std::move(runtime_options))) { LOG(ERROR) << "Failed to create runtime"; return false; @@ -2059,18 +2140,12 @@ class Dex2Oat FINAL { runtime_->GetClassLinker()->RunRootClinits(); - return true; - } + // Runtime::Create acquired the mutator_lock_ that is normally given away when we + // Runtime::Start, give it away now so that we don't starve GC. + Thread* self = Thread::Current(); + self->TransitionFromRunnableToSuspended(kNative); - void PrepareImageWriter(uintptr_t image_base) { - DCHECK(IsImage()); - image_writer_.reset(new ImageWriter(*driver_, - image_base, - compiler_options_->GetCompilePic(), - IsAppImage(), - image_storage_mode_, - oat_filenames_, - dex_file_oat_filename_map_)); + return true; } // Let the ImageWriter write the image files. If we do not compile PIC, also fix up the oat files. @@ -2078,9 +2153,14 @@ class Dex2Oat FINAL { REQUIRES(!Locks::mutator_lock_) { CHECK(image_writer_ != nullptr); if (!IsBootImage()) { + CHECK(image_filenames_.empty()); image_filenames_.push_back(app_image_file_name_.c_str()); } - if (!image_writer_->Write(app_image_fd_, image_filenames_, oat_filenames_)) { + if (!image_writer_->Write(app_image_fd_, + image_filenames_, + oat_fd_, + oat_filenames_, + oat_location_)) { LOG(ERROR) << "Failure during image file creation"; return false; } @@ -2249,6 +2329,9 @@ class Dex2Oat FINAL { InstructionSet instruction_set_; std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; + uint32_t image_file_location_oat_checksum_; + uintptr_t image_file_location_oat_data_begin_; + int32_t image_patch_delta_; std::unique_ptr<SafeMap<std::string, std::string> > key_value_store_; std::unique_ptr<VerificationResults> verification_results_; @@ -2256,11 +2339,11 @@ class Dex2Oat FINAL { DexFileToMethodInlinerMap method_inliner_map_; std::unique_ptr<QuickCompilerCallbacks> callbacks_; + std::unique_ptr<Runtime> runtime_; + // Ownership for the class path files. std::vector<std::unique_ptr<const DexFile>> class_path_files_; - std::unique_ptr<Runtime> runtime_; - size_t thread_count_; uint64_t start_ns_; std::unique_ptr<WatchDog> watchdog_; @@ -2295,11 +2378,17 @@ class Dex2Oat FINAL { std::vector<const DexFile*> dex_files_; std::string no_inline_from_string_; std::vector<jobject> dex_caches_; - std::vector<std::unique_ptr<const DexFile>> opened_dex_files_; + jobject class_loader_; + std::vector<std::unique_ptr<ElfWriter>> elf_writers_; + std::vector<std::unique_ptr<OatWriter>> oat_writers_; + std::vector<OutputStream*> rodata_; std::unique_ptr<ImageWriter> image_writer_; std::unique_ptr<CompilerDriver> driver_; + std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_; + std::vector<std::unique_ptr<const DexFile>> opened_dex_files_; + std::vector<std::string> verbose_methods_; bool dump_stats_; bool dump_passes_; @@ -2311,6 +2400,8 @@ class Dex2Oat FINAL { int app_image_fd_; std::vector<std::string> profile_files_; std::vector<std::string> reference_profile_files_; + std::vector<uint32_t> profile_files_fd_; + std::vector<uint32_t> reference_profile_files_fd_; std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_; TimingLogger* timings_; std::unique_ptr<CumulativeLogger> compiler_phases_timings_; @@ -2346,14 +2437,19 @@ static void b13564922() { static int CompileImage(Dex2Oat& dex2oat) { dex2oat.Compile(); - if (!dex2oat.CreateOatFiles()) { + if (!dex2oat.WriteOatFiles()) { dex2oat.EraseOatFiles(); return EXIT_FAILURE; } - // Close the image oat files. We always expect the output file by name, and it will be - // re-opened from the unstripped name. Note: it's easier to *flush* and close... - if (!dex2oat.FlushCloseOatFiles()) { + // Flush boot.oat. We always expect the output file by name, and it will be re-opened from the + // unstripped name. Do not close the file if we are compiling the image with an oat fd since the + // image writer will require this fd to generate the image. + if (dex2oat.ShouldKeepOatFileOpen()) { + if (!dex2oat.FlushOatFiles()) { + return EXIT_FAILURE; + } + } else if (!dex2oat.FlushCloseOatFiles()) { return EXIT_FAILURE; } @@ -2385,7 +2481,7 @@ static int CompileImage(Dex2Oat& dex2oat) { static int CompileApp(Dex2Oat& dex2oat) { dex2oat.Compile(); - if (!dex2oat.CreateOatFiles()) { + if (!dex2oat.WriteOatFiles()) { dex2oat.EraseOatFiles(); return EXIT_FAILURE; } @@ -2442,6 +2538,11 @@ static int dex2oat(int argc, char** argv) { } } + // Check early that the result of compilation can be written + if (!dex2oat.OpenFile()) { + return EXIT_FAILURE; + } + // Print the complete line when any of the following is true: // 1) Debug build // 2) Compiling an image @@ -2455,11 +2556,6 @@ static int dex2oat(int argc, char** argv) { } if (!dex2oat.Setup()) { - return EXIT_FAILURE; - } - - // Check early that the result of compilation can be written - if (!dex2oat.OpenFile()) { dex2oat.EraseOatFiles(); return EXIT_FAILURE; } diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index d4bef0fe7b..1f74c93045 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -938,6 +938,11 @@ DISASSEMBLER_ENTRY(cmp, has_modrm = true; load = true; break; + case 0xB8: + opcode1 = "popcnt"; + has_modrm = true; + load = true; + break; case 0xBE: opcode1 = "movsxb"; has_modrm = true; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 7b9ce5bd1b..7a0decbdb4 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -40,7 +40,7 @@ #include "gc/space/image_space.h" #include "gc/space/large_object_space.h" #include "gc/space/space-inl.h" -#include "image.h" +#include "image-inl.h" #include "indenter.h" #include "linker/buffered_output_stream.h" #include "linker/file_output_stream.h" @@ -142,9 +142,7 @@ class OatSymbolizer FINAL { text->End(); if (oat_file_->BssSize() != 0) { - bss->Start(); - bss->SetSize(oat_file_->BssSize()); - bss->End(); + bss->WriteNoBitsSection(oat_file_->BssSize()); } builder_->WriteDynamicSection(elf_file->GetPath()); diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index d836532ed2..1668dc5f25 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -35,7 +35,7 @@ #include "elf_file.h" #include "elf_file_impl.h" #include "gc/space/image_space.h" -#include "image.h" +#include "image-inl.h" #include "mirror/abstract_method.h" #include "mirror/object-inl.h" #include "mirror/method.h" @@ -547,6 +547,9 @@ void PatchOat::PatchInternedStrings(const ImageHeader* image_header) { void PatchOat::PatchClassTable(const ImageHeader* image_header) { const auto& section = image_header->GetImageSection(ImageHeader::kSectionClassTable); + if (section.Size() == 0) { + return; + } // Note that we require that ReadFromMemory does not make an internal copy of the elements. // This also relies on visit roots not doing any verification which could fail after we update // the roots to be the image addresses. diff --git a/runtime/Android.mk b/runtime/Android.mk index 04645d16d2..7bf6d21040 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -274,7 +274,6 @@ LIBART_TARGET_SRC_FILES_arm64 := \ arch/arm64/fault_handler_arm64.cc LIBART_SRC_FILES_x86 := \ - interpreter/mterp/mterp_stub.cc \ arch/x86/context_x86.cc \ arch/x86/entrypoints_init_x86.cc \ arch/x86/jni_entrypoints_x86.S \ @@ -286,6 +285,20 @@ LIBART_SRC_FILES_x86 := \ LIBART_TARGET_SRC_FILES_x86 := \ $(LIBART_SRC_FILES_x86) +# Darwin uses non-standard x86 assembly syntax. Don't build x86 Darwin host mterp there. +ifeq ($(HOST_OS),darwin) + LIBART_SRC_FILES_x86 += \ + interpreter/mterp/mterp_stub.cc +else + LIBART_SRC_FILES_x86 += \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_x86.S +endif +# But do support x86 mterp for target build regardless of host platform. +LIBART_TARGET_SRC_FILES_x86 += \ + interpreter/mterp/mterp.cc \ + interpreter/mterp/out/mterp_x86.S + # 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 := \ diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc index 42f5df467d..b97a8dbbc1 100644 --- a/runtime/arch/x86/instruction_set_features_x86.cc +++ b/runtime/arch/x86/instruction_set_features_x86.cc @@ -50,6 +50,10 @@ static constexpr const char* x86_variants_prefer_locked_add_sync[] = { "silvermont", }; +static constexpr const char* x86_variants_with_popcnt[] = { + "silvermont", +}; + const X86InstructionSetFeatures* X86InstructionSetFeatures::FromVariant( const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED, bool x86_64) { @@ -69,6 +73,11 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromVariant( arraysize(x86_variants_prefer_locked_add_sync), variant); + bool has_POPCNT = FindVariantInArray(x86_variants_with_popcnt, + arraysize(x86_variants_with_popcnt), + variant); + + // Verify that variant is known. bool known_variant = FindVariantInArray(x86_known_variants, arraysize(x86_known_variants), variant); if (!known_variant && variant != "default") { @@ -77,10 +86,10 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromVariant( if (x86_64) { return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } else { return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } } @@ -93,12 +102,15 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromBitmap(uint32_t bool has_AVX = (bitmap & kAvxBitfield) != 0; bool has_AVX2 = (bitmap & kAvxBitfield) != 0; bool prefers_locked_add = (bitmap & kPrefersLockedAdd) != 0; + bool has_POPCNT = (bitmap & kPopCntBitfield) != 0; if (x86_64) { return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, - has_AVX, has_AVX2, prefers_locked_add); + has_AVX, has_AVX2, prefers_locked_add, + has_POPCNT); } else { return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, - has_AVX, has_AVX2, prefers_locked_add); + has_AVX, has_AVX2, prefers_locked_add, + has_POPCNT); } } @@ -138,12 +150,18 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCppDefines(bool // No #define for memory synchronization preference. const bool prefers_locked_add = false; +#ifndef __POPCNT__ + const bool has_POPCNT = false; +#else + const bool has_POPCNT = true; +#endif + if (x86_64) { return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } else { return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } } @@ -158,6 +176,7 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCpuInfo(bool x86 bool has_AVX2 = false; // No cpuinfo for memory synchronization preference. const bool prefers_locked_add = false; + bool has_POPCNT = false; std::ifstream in("/proc/cpuinfo"); if (!in.fail()) { @@ -183,6 +202,9 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCpuInfo(bool x86 if (line.find("avx2") != std::string::npos) { has_AVX2 = true; } + if (line.find("popcnt") != std::string::npos) { + has_POPCNT = true; + } } else if (line.find("processor") != std::string::npos && line.find(": 1") != std::string::npos) { smp = true; @@ -195,10 +217,10 @@ const X86InstructionSetFeatures* X86InstructionSetFeatures::FromCpuInfo(bool x86 } if (x86_64) { return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } else { return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } } @@ -223,7 +245,8 @@ bool X86InstructionSetFeatures::Equals(const InstructionSetFeatures* other) cons (has_SSE4_2_ == other_as_x86->has_SSE4_2_) && (has_AVX_ == other_as_x86->has_AVX_) && (has_AVX2_ == other_as_x86->has_AVX2_) && - (prefers_locked_add_ == other_as_x86->prefers_locked_add_); + (prefers_locked_add_ == other_as_x86->prefers_locked_add_) && + (has_POPCNT_ == other_as_x86->has_POPCNT_); } uint32_t X86InstructionSetFeatures::AsBitmap() const { @@ -233,7 +256,8 @@ uint32_t X86InstructionSetFeatures::AsBitmap() const { (has_SSE4_2_ ? kSse4_2Bitfield : 0) | (has_AVX_ ? kAvxBitfield : 0) | (has_AVX2_ ? kAvx2Bitfield : 0) | - (prefers_locked_add_ ? kPrefersLockedAdd : 0); + (prefers_locked_add_ ? kPrefersLockedAdd : 0) | + (has_POPCNT_ ? kPopCntBitfield : 0); } std::string X86InstructionSetFeatures::GetFeatureString() const { @@ -273,6 +297,11 @@ std::string X86InstructionSetFeatures::GetFeatureString() const { } else { result += ",-lock_add"; } + if (has_POPCNT_) { + result += ",popcnt"; + } else { + result += ",-popcnt"; + } return result; } @@ -285,6 +314,7 @@ const InstructionSetFeatures* X86InstructionSetFeatures::AddFeaturesFromSplitStr bool has_AVX = has_AVX_; bool has_AVX2 = has_AVX2_; bool prefers_locked_add = prefers_locked_add_; + bool has_POPCNT = has_POPCNT_; for (auto i = features.begin(); i != features.end(); i++) { std::string feature = Trim(*i); if (feature == "ssse3") { @@ -311,6 +341,10 @@ const InstructionSetFeatures* X86InstructionSetFeatures::AddFeaturesFromSplitStr prefers_locked_add = true; } else if (feature == "-lock_add") { prefers_locked_add = false; + } else if (feature == "popcnt") { + has_POPCNT = true; + } else if (feature == "-popcnt") { + has_POPCNT = false; } else { *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); return nullptr; @@ -318,10 +352,10 @@ const InstructionSetFeatures* X86InstructionSetFeatures::AddFeaturesFromSplitStr } if (x86_64) { return new X86_64InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } else { return new X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add); + has_AVX2, prefers_locked_add, has_POPCNT); } } diff --git a/runtime/arch/x86/instruction_set_features_x86.h b/runtime/arch/x86/instruction_set_features_x86.h index 2b845f8dcc..1819654bef 100644 --- a/runtime/arch/x86/instruction_set_features_x86.h +++ b/runtime/arch/x86/instruction_set_features_x86.h @@ -62,6 +62,8 @@ class X86InstructionSetFeatures : public InstructionSetFeatures { bool PrefersLockedAddSynchronization() const { return prefers_locked_add_; } + bool HasPopCnt() const { return has_POPCNT_; } + protected: // Parse a string of the form "ssse3" adding these to a new InstructionSetFeatures. virtual const InstructionSetFeatures* @@ -75,10 +77,17 @@ class X86InstructionSetFeatures : public InstructionSetFeatures { bool x86_64, std::string* error_msg) const; X86InstructionSetFeatures(bool smp, bool has_SSSE3, bool has_SSE4_1, bool has_SSE4_2, - bool has_AVX, bool has_AVX2, bool prefers_locked_add) - : InstructionSetFeatures(smp), has_SSSE3_(has_SSSE3), has_SSE4_1_(has_SSE4_1), - has_SSE4_2_(has_SSE4_2), has_AVX_(has_AVX), has_AVX2_(has_AVX2), - prefers_locked_add_(prefers_locked_add) { + bool has_AVX, bool has_AVX2, + bool prefers_locked_add, + bool has_POPCNT) + : InstructionSetFeatures(smp), + has_SSSE3_(has_SSSE3), + has_SSE4_1_(has_SSE4_1), + has_SSE4_2_(has_SSE4_2), + has_AVX_(has_AVX), + has_AVX2_(has_AVX2), + prefers_locked_add_(prefers_locked_add), + has_POPCNT_(has_POPCNT) { } private: @@ -91,6 +100,7 @@ class X86InstructionSetFeatures : public InstructionSetFeatures { kAvxBitfield = 16, kAvx2Bitfield = 32, kPrefersLockedAdd = 64, + kPopCntBitfield = 128, }; const bool has_SSSE3_; // x86 128bit SIMD - Supplemental SSE. @@ -99,6 +109,7 @@ class X86InstructionSetFeatures : public InstructionSetFeatures { const bool has_AVX_; // x86 256bit SIMD AVX. const bool has_AVX2_; // x86 256bit SIMD AVX 2.0. const bool prefers_locked_add_; // x86 use locked add for memory synchronization. + const bool has_POPCNT_; // x86 population count DISALLOW_COPY_AND_ASSIGN(X86InstructionSetFeatures); }; diff --git a/runtime/arch/x86/instruction_set_features_x86_test.cc b/runtime/arch/x86/instruction_set_features_x86_test.cc index e8d01e6c14..a062c12892 100644 --- a/runtime/arch/x86/instruction_set_features_x86_test.cc +++ b/runtime/arch/x86/instruction_set_features_x86_test.cc @@ -27,7 +27,7 @@ TEST(X86InstructionSetFeaturesTest, X86FeaturesFromDefaultVariant) { ASSERT_TRUE(x86_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_features->GetInstructionSet(), kX86); EXPECT_TRUE(x86_features->Equals(x86_features.get())); - EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add", + EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add,-popcnt", x86_features->GetFeatureString().c_str()); EXPECT_EQ(x86_features->AsBitmap(), 1U); } @@ -40,7 +40,7 @@ TEST(X86InstructionSetFeaturesTest, X86FeaturesFromAtomVariant) { ASSERT_TRUE(x86_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_features->GetInstructionSet(), kX86); EXPECT_TRUE(x86_features->Equals(x86_features.get())); - EXPECT_STREQ("smp,ssse3,-sse4.1,-sse4.2,-avx,-avx2,lock_add", + EXPECT_STREQ("smp,ssse3,-sse4.1,-sse4.2,-avx,-avx2,lock_add,-popcnt", x86_features->GetFeatureString().c_str()); EXPECT_EQ(x86_features->AsBitmap(), 67U); @@ -50,7 +50,7 @@ TEST(X86InstructionSetFeaturesTest, X86FeaturesFromAtomVariant) { ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_default_features->GetInstructionSet(), kX86); EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get())); - EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add", + EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add,-popcnt", x86_default_features->GetFeatureString().c_str()); EXPECT_EQ(x86_default_features->AsBitmap(), 1U); @@ -60,7 +60,7 @@ TEST(X86InstructionSetFeaturesTest, X86FeaturesFromAtomVariant) { ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64); EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get())); - EXPECT_STREQ("smp,ssse3,-sse4.1,-sse4.2,-avx,-avx2,lock_add", + EXPECT_STREQ("smp,ssse3,-sse4.1,-sse4.2,-avx,-avx2,lock_add,-popcnt", x86_64_features->GetFeatureString().c_str()); EXPECT_EQ(x86_64_features->AsBitmap(), 67U); @@ -77,9 +77,9 @@ TEST(X86InstructionSetFeaturesTest, X86FeaturesFromSilvermontVariant) { ASSERT_TRUE(x86_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_features->GetInstructionSet(), kX86); EXPECT_TRUE(x86_features->Equals(x86_features.get())); - EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2,lock_add", + EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2,lock_add,popcnt", x86_features->GetFeatureString().c_str()); - EXPECT_EQ(x86_features->AsBitmap(), 79U); + EXPECT_EQ(x86_features->AsBitmap(), 207U); // Build features for a 32-bit x86 default processor. std::unique_ptr<const InstructionSetFeatures> x86_default_features( @@ -87,7 +87,7 @@ TEST(X86InstructionSetFeaturesTest, X86FeaturesFromSilvermontVariant) { ASSERT_TRUE(x86_default_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_default_features->GetInstructionSet(), kX86); EXPECT_TRUE(x86_default_features->Equals(x86_default_features.get())); - EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add", + EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add,-popcnt", x86_default_features->GetFeatureString().c_str()); EXPECT_EQ(x86_default_features->AsBitmap(), 1U); @@ -97,9 +97,9 @@ TEST(X86InstructionSetFeaturesTest, X86FeaturesFromSilvermontVariant) { ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64); EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get())); - EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2,lock_add", + EXPECT_STREQ("smp,ssse3,sse4.1,sse4.2,-avx,-avx2,lock_add,popcnt", x86_64_features->GetFeatureString().c_str()); - EXPECT_EQ(x86_64_features->AsBitmap(), 79U); + EXPECT_EQ(x86_64_features->AsBitmap(), 207U); EXPECT_FALSE(x86_64_features->Equals(x86_features.get())); EXPECT_FALSE(x86_64_features->Equals(x86_default_features.get())); diff --git a/runtime/arch/x86_64/instruction_set_features_x86_64.h b/runtime/arch/x86_64/instruction_set_features_x86_64.h index b8000d0001..aba72348f8 100644 --- a/runtime/arch/x86_64/instruction_set_features_x86_64.h +++ b/runtime/arch/x86_64/instruction_set_features_x86_64.h @@ -74,9 +74,10 @@ class X86_64InstructionSetFeatures FINAL : public X86InstructionSetFeatures { private: X86_64InstructionSetFeatures(bool smp, bool has_SSSE3, bool has_SSE4_1, bool has_SSE4_2, - bool has_AVX, bool has_AVX2, bool prefers_locked_add) + bool has_AVX, bool has_AVX2, bool prefers_locked_add, + bool has_POPCNT) : X86InstructionSetFeatures(smp, has_SSSE3, has_SSE4_1, has_SSE4_2, has_AVX, - has_AVX2, prefers_locked_add) { + has_AVX2, prefers_locked_add, has_POPCNT) { } friend class X86InstructionSetFeatures; diff --git a/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc b/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc index 4562c64bc9..78aeacf214 100644 --- a/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc +++ b/runtime/arch/x86_64/instruction_set_features_x86_64_test.cc @@ -27,7 +27,7 @@ TEST(X86_64InstructionSetFeaturesTest, X86Features) { ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg; EXPECT_EQ(x86_64_features->GetInstructionSet(), kX86_64); EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get())); - EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add", + EXPECT_STREQ("smp,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,-lock_add,-popcnt", x86_64_features->GetFeatureString().c_str()); EXPECT_EQ(x86_64_features->AsBitmap(), 1U); } diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 4166e22daa..3463b0d842 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -37,7 +37,7 @@ inline mirror::Class* ArtField::GetDeclaringClass() { GcRootSource gc_root_source(this); mirror::Class* result = declaring_class_.Read(&gc_root_source); DCHECK(result != nullptr); - DCHECK(result->IsLoaded() || result->IsErroneous()); + DCHECK(result->IsLoaded() || result->IsErroneous()) << result->GetStatus(); return result; } @@ -334,6 +334,15 @@ inline void ArtField::VisitRoots(RootVisitorType& visitor) { visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); } +template <typename Visitor> +inline void ArtField::UpdateObjects(const Visitor& visitor) { + mirror::Class* old_class = DeclaringClassRoot().Read<kWithoutReadBarrier>(); + mirror::Class* new_class = visitor(old_class); + if (old_class != new_class) { + SetDeclaringClass(new_class); + } +} + } // namespace art #endif // ART_RUNTIME_ART_FIELD_INL_H_ diff --git a/runtime/art_field.h b/runtime/art_field.h index a943a34174..ee1ba1fb53 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -190,6 +190,11 @@ class ArtField FINAL { return declaring_class_; } + // Update the declaring class with the passed in visitor. Does not use read barrier. + template <typename Visitor> + ALWAYS_INLINE void UpdateObjects(const Visitor& visitor) + SHARED_REQUIRES(Locks::mutator_lock_); + private: mirror::Class* ProxyFindSystemClass(const char* descriptor) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index a5f5c49068..74eb7227dc 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -467,6 +467,43 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { } } +template <typename Visitor> +inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor) { + mirror::Class* old_class = GetDeclaringClassNoBarrier(); + mirror::Class* new_class = visitor(old_class); + if (old_class != new_class) { + SetDeclaringClass(new_class); + } + ArtMethod** old_methods = GetDexCacheResolvedMethods(sizeof(void*)); + ArtMethod** new_methods = visitor(old_methods); + if (old_methods != new_methods) { + SetDexCacheResolvedMethods(new_methods, sizeof(void*)); + } + GcRoot<mirror::Class>* old_types = GetDexCacheResolvedTypes(sizeof(void*)); + GcRoot<mirror::Class>* new_types = visitor(old_types); + if (old_types != new_types) { + SetDexCacheResolvedTypes(new_types, sizeof(void*)); + } +} + +template <typename Visitor> +inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor) { + if (IsNative()) { + const void* old_native_code = GetEntryPointFromJni(); + const void* new_native_code = visitor(old_native_code); + if (old_native_code != new_native_code) { + SetEntryPointFromJni(new_native_code); + } + } else { + DCHECK(GetEntryPointFromJni() == nullptr); + } + const void* old_code = GetEntryPointFromQuickCompiledCode(); + const void* new_code = visitor(old_code); + if (old_code != new_code) { + SetEntryPointFromQuickCompiledCode(new_code); + } +} + } // namespace art #endif // ART_RUNTIME_ART_METHOD_INL_H_ diff --git a/runtime/art_method.h b/runtime/art_method.h index 0be2fa20ac..440e796f46 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -477,6 +477,17 @@ class ArtMethod FINAL { // Returns whether the method has any compiled code, JIT or AOT. bool HasAnyCompiledCode() SHARED_REQUIRES(Locks::mutator_lock_); + + // Update heap objects and non-entrypoint pointers by the passed in visitor for image relocation. + // Does not use read barrier. + template <typename Visitor> + ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Update entry points by passing them through the visitor. + template <typename Visitor> + ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor); + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 115c26073d..de46b0c118 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -53,6 +53,7 @@ struct LogVerbosity { bool third_party_jni; // Enabled with "-verbose:third-party-jni". bool threads; bool verifier; + bool image; }; // Global log verbosity setting, initialized by InitLogging. diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index 71e0590272..814cbd093a 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -26,16 +26,25 @@ namespace art { bool ScopedFlock::Init(const char* filename, std::string* error_msg) { + return Init(filename, O_CREAT | O_RDWR, true, error_msg); +} + +bool ScopedFlock::Init(const char* filename, int flags, bool block, std::string* error_msg) { while (true) { if (file_.get() != nullptr) { UNUSED(file_->FlushCloseOrErase()); // Ignore result. } - file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR)); + file_.reset(OS::OpenFileWithFlags(filename, flags)); if (file_.get() == nullptr) { *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno)); return false; } - int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX)); + int operation = block ? LOCK_EX : (LOCK_EX | LOCK_NB); + int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), operation)); + if (flock_result == EWOULDBLOCK) { + // File is locked by someone else and we are required not to block; + return false; + } if (flock_result != 0) { *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno)); return false; @@ -51,11 +60,23 @@ bool ScopedFlock::Init(const char* filename, std::string* error_msg) { if (stat_result != 0) { PLOG(WARNING) << "Failed to stat, will retry: " << filename; // ENOENT can happen if someone racing with us unlinks the file we created so just retry. - continue; + if (block) { + continue; + } else { + // Note that in theory we could race with someone here for a long time and end up retrying + // over and over again. This potential behavior does not fit well in the non-blocking + // semantics. Thus, if we are not require to block return failure when racing. + return false; + } } if (fstat_stat.st_dev != stat_stat.st_dev || fstat_stat.st_ino != stat_stat.st_ino) { LOG(WARNING) << "File changed while locking, will retry: " << filename; - continue; + if (block) { + continue; + } else { + // See comment above. + return false; + } } return true; } @@ -78,7 +99,7 @@ bool ScopedFlock::Init(File* file, std::string* error_msg) { return true; } -File* ScopedFlock::GetFile() { +File* ScopedFlock::GetFile() const { CHECK(file_.get() != nullptr); return file_.get(); } diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h index 08612e3016..cc22056443 100644 --- a/runtime/base/scoped_flock.h +++ b/runtime/base/scoped_flock.h @@ -32,10 +32,15 @@ class ScopedFlock { // Attempts to acquire an exclusive file lock (see flock(2)) on the file // at filename, and blocks until it can do so. // - // Returns true if the lock could be acquired, or false if an error - // occurred. It is an error if the file does not exist, or if its inode - // changed (usually due to a new file being created at the same path) - // between attempts to lock it. + // Returns true if the lock could be acquired, or false if an error occurred. + // It is an error if its inode changed (usually due to a new file being + // created at the same path) between attempts to lock it. In blocking mode, + // locking will be retried if the file changed. In non-blocking mode, false + // is returned and no attempt is made to re-acquire the lock. + // + // The file is opened with the provided flags. + bool Init(const char* filename, int flags, bool block, std::string* error_msg); + // Calls Init(filename, O_CREAT | O_RDWR, true, errror_msg) bool Init(const char* filename, std::string* error_msg); // Attempt to acquire an exclusive file lock (see flock(2)) on 'file'. // Returns true if the lock could be acquired or false if an error @@ -43,7 +48,7 @@ class ScopedFlock { bool Init(File* file, std::string* error_msg); // Returns the (locked) file associated with this instance. - File* GetFile(); + File* GetFile() const; // Returns whether a file is held. bool HasFile(); diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 78bc3d5f9f..e17bebb4fb 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -316,4 +316,21 @@ void FdFile::MarkUnchecked() { guard_state_ = GuardState::kNoCheck; } +bool FdFile::ClearContent() { + if (SetLength(0) < 0) { + PLOG(art::ERROR) << "Failed to reset the length"; + return false; + } + return ResetOffset(); +} + +bool FdFile::ResetOffset() { + off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET)); + if (rc == static_cast<off_t>(-1)) { + PLOG(art::ERROR) << "Failed to reset the offset"; + return false; + } + return true; +} + } // namespace unix_file diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index 231a1ab145..1e2d8af151 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -79,6 +79,11 @@ class FdFile : public RandomAccessFile { // Copy data from another file. bool Copy(FdFile* input_file, int64_t offset, int64_t size); + // Clears the file content and resets the file offset to 0. + // Returns true upon success, false otherwise. + bool ClearContent(); + // Resets the file offset to the beginning of the file. + bool ResetOffset(); // This enum is public so that we can define the << operator over it. enum class GuardState { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ed833c4335..bb709e8774 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -53,6 +53,7 @@ #include "gc/heap.h" #include "gc/space/image_space.h" #include "handle_scope-inl.h" +#include "image-inl.h" #include "intern_table.h" #include "interpreter/interpreter.h" #include "jit/jit.h" @@ -336,6 +337,10 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // Use the pointer size from the runtime since we are probably creating the image. image_pointer_size_ = InstructionSetPointerSize(runtime->GetInstructionSet()); + if (!ValidPointerSize(image_pointer_size_)) { + *error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_); + return false; + } // java_lang_Class comes first, it's needed for AllocClass // The GC can't handle an object with a null class since we can't get the size of this object. @@ -489,7 +494,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b return false; } AppendToBootClassPath(self, *dex_file); - opened_dex_files_.push_back(std::move(dex_file)); + boot_dex_files_.push_back(std::move(dex_file)); } // now we can use FindSystemClass @@ -878,6 +883,7 @@ struct TrampolineCheckData { ArtMethod* m; bool error; }; + static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_ANALYSIS { if (obj->IsClass()) { mirror::Class* klass = obj->AsClass(); @@ -896,8 +902,8 @@ static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_AN } } -bool ClassLinker::InitFromImage(std::string* error_msg) { - VLOG(startup) << "ClassLinker::InitFromImage entering"; +bool ClassLinker::InitFromBootImage(std::string* error_msg) { + VLOG(startup) << __FUNCTION__ << " entering"; CHECK(!init_done_); Runtime* const runtime = Runtime::Current(); @@ -906,6 +912,21 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces(); CHECK(!spaces.empty()); image_pointer_size_ = spaces[0]->GetImageHeader().GetPointerSize(); + if (!ValidPointerSize(image_pointer_size_)) { + *error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_); + return false; + } + if (!runtime->IsAotCompiler()) { + // Only the Aot compiler supports having an image with a different pointer size than the + // runtime. This happens on the host for compiling 32 bit tests since we use a 64 bit libart + // compiler. We may also use 32 bit dex2oat on a system with 64 bit apps. + if (image_pointer_size_ != sizeof(void*)) { + *error_msg = StringPrintf("Runtime must use current image pointer size: %zu vs %zu", + image_pointer_size_, + sizeof(void*)); + return false; + } + } dex_cache_boot_image_class_lookup_required_ = true; std::vector<const OatFile*> oat_files = runtime->GetOatFileManager().RegisterImageOatFiles(spaces); @@ -957,19 +978,10 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { } } - StackHandleScopeCollection handles(self); - std::vector<Handle<mirror::ObjectArray<mirror::DexCache>>> dex_caches_vector; - for (gc::space::ImageSpace* space : spaces) { - Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(handles.NewHandle( - space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches)-> - AsObjectArray<mirror::DexCache>())); - dex_caches_vector.push_back(dex_caches); - } - - Handle<mirror::ObjectArray<mirror::Class>> class_roots(handles.NewHandle( - spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> - AsObjectArray<mirror::Class>())); - class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); + class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>( + down_cast<mirror::ObjectArray<mirror::Class>*>( + spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots))); + mirror::Class::SetClassClass(class_roots_.Read()->Get(kJavaLangClass)); // Special case of setting up the String class early so that we can test arbitrary objects // as being Strings or not @@ -982,162 +994,595 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { runtime->SetSentinel(heap->AllocNonMovableObject<true>( self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor())); - uint32_t dex_file_count = 0; - for (const OatFile* oat_file : oat_files) { - dex_file_count += oat_file->GetOatHeader().GetDexFileCount(); + // reinit array_iftable_ from any array class instance, they should be == + array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable()); + DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); + // String class root was set above + mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField)); + mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); + mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); + mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); + mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); + mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); + mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); + mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); + mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); + mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass)); + mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); + mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); + mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass)); + mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); + mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); + mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); + mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); + + for (gc::space::ImageSpace* image_space : spaces) { + // Boot class loader, use a null handle. + std::vector<std::unique_ptr<const DexFile>> dex_files; + if (!AddImageSpace(image_space, + ScopedNullHandle<mirror::ClassLoader>(), + /*dex_elements*/nullptr, + /*dex_location*/nullptr, + /*out*/&dex_files, + error_msg)) { + return false; + } + // Append opened dex files at the end. + boot_dex_files_.insert(boot_dex_files_.end(), + std::make_move_iterator(dex_files.begin()), + std::make_move_iterator(dex_files.end())); } - uint32_t dex_caches_count = 0; - for (auto dex_caches : dex_caches_vector) { - dex_caches_count += dex_caches->GetLength(); + FinishInit(self); + + VLOG(startup) << __FUNCTION__ << " exiting"; + return true; +} + +static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, + mirror::ClassLoader* class_loader) + SHARED_REQUIRES(Locks::mutator_lock_) { + return class_loader == nullptr || + class_loader->GetClass() == + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); +} + +static mirror::String* GetDexPathListElementName(ScopedObjectAccessUnchecked& soa, + mirror::Object* element) + SHARED_REQUIRES(Locks::mutator_lock_) { + ArtField* const dex_file_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + ArtField* const dex_file_name_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_fileName); + DCHECK(dex_file_field != nullptr); + DCHECK(dex_file_name_field != nullptr); + DCHECK(element != nullptr); + CHECK_EQ(dex_file_field->GetDeclaringClass(), element->GetClass()) << PrettyTypeOf(element); + mirror::Object* dex_file = dex_file_field->GetObject(element); + if (dex_file == nullptr) { + return nullptr; } - if (dex_file_count != dex_caches_count) { - *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from " - "image"; - return false; + mirror::Object* const name_object = dex_file_name_field->GetObject(dex_file); + if (name_object != nullptr) { + return name_object->AsString(); } - for (auto dex_caches : dex_caches_vector) { - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - StackHandleScope<1> hs2(self); - Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i))); - const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); - const OatFile::OatDexFile* oat_dex_file = nullptr; - for (const OatFile* oat_file : oat_files) { - const OatFile::OatDexFile* oat_dex = - oat_file->GetOatDexFile(dex_file_location.c_str(), nullptr, false); - if (oat_dex != nullptr) { - DCHECK(oat_dex_file == nullptr); - oat_dex_file = oat_dex; + return nullptr; +} + +static bool FlattenPathClassLoader(mirror::ClassLoader* class_loader, + std::list<mirror::String*>* out_dex_file_names, + std::string* error_msg) + SHARED_REQUIRES(Locks::mutator_lock_) { + DCHECK(out_dex_file_names != nullptr); + DCHECK(error_msg != nullptr); + ScopedObjectAccessUnchecked soa(Thread::Current()); + ArtField* const dex_path_list_field = + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList); + ArtField* const dex_elements_field = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements); + CHECK(dex_path_list_field != nullptr); + CHECK(dex_elements_field != nullptr); + while (!IsBootClassLoader(soa, class_loader)) { + if (class_loader->GetClass() != + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) { + *error_msg = StringPrintf("Unknown class loader type %s", PrettyTypeOf(class_loader).c_str()); + // Unsupported class loader. + return false; + } + mirror::Object* dex_path_list = dex_path_list_field->GetObject(class_loader); + if (dex_path_list != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + mirror::Object* dex_elements_obj = dex_elements_field->GetObject(dex_path_list); + // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look + // at the mCookie which is a DexFile vector. + if (dex_elements_obj != nullptr) { + mirror::ObjectArray<mirror::Object>* dex_elements = + dex_elements_obj->AsObjectArray<mirror::Object>(); + // Reverse order since we insert the parent at the front. + for (int32_t i = dex_elements->GetLength() - 1; i >= 0; --i) { + mirror::Object* const element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + *error_msg = StringPrintf("Null dex element at index %d", i); + return false; + } + mirror::String* const name = GetDexPathListElementName(soa, element); + if (name == nullptr) { + *error_msg = StringPrintf("Null name for dex element at index %d", i); + return false; + } + out_dex_file_names->push_front(name); } } + } + class_loader = class_loader->GetParent(); + } + return true; +} - if (oat_dex_file == nullptr) { - *error_msg = StringPrintf("Failed finding oat dex file for %s", - dex_file_location.c_str()); - return false; +class FixupArtMethodArrayVisitor : public ArtMethodVisitor { + public: + explicit FixupArtMethodArrayVisitor(const ImageHeader& header) : header_(header) {} + + virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) { + GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*)); + const bool is_miranda = method->IsMiranda(); + if (resolved_types != nullptr) { + bool in_image_space = false; + if (kIsDebugBuild || is_miranda) { + in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( + reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin()); } - std::string inner_error_msg; - std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg); - if (dex_file == nullptr) { - *error_msg = StringPrintf("Failed to open dex file %s error '%s'", - dex_file_location.c_str(), - inner_error_msg.c_str()); - return false; + // Must be in image space for non-miranda method. + DCHECK(is_miranda || in_image_space) + << resolved_types << " is not in image starting at " + << reinterpret_cast<void*>(header_.GetImageBegin()); + if (!is_miranda || in_image_space) { + // Go through the array so that we don't need to do a slow map lookup. + method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types), + sizeof(void*)); } - - if (kSanityCheckObjects) { - SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), - dex_cache->NumResolvedMethods(), - image_pointer_size_, - spaces); + } + ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*)); + if (resolved_methods != nullptr) { + bool in_image_space = false; + if (kIsDebugBuild || is_miranda) { + in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains( + reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin()); } + // Must be in image space for non-miranda method. + DCHECK(is_miranda || in_image_space) + << resolved_methods << " is not in image starting at " + << reinterpret_cast<void*>(header_.GetImageBegin()); + if (!is_miranda || in_image_space) { + // Go through the array so that we don't need to do a slow map lookup. + method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods), + sizeof(void*)); + } + } + } - if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) { - *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x", - dex_file_location.c_str(), - dex_file->GetLocationChecksum(), - oat_dex_file->GetDexFileLocationChecksum()); - return false; + private: + const ImageHeader& header_; +}; + +class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor { + public: + explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {} + + virtual void Visit(ArtMethod* method) + SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_) { + mirror::Class* klass = method->GetDeclaringClass(); + if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) { + CHECK_EQ(table_->LookupByDescriptor(klass), klass) << PrettyClass(klass); + } + } + + private: + ClassTable* const table_; +}; + +void ClassLinker::UpdateAppImageClassLoadersAndDexCaches( + gc::space::ImageSpace* space, + Handle<mirror::ClassLoader> class_loader, + Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches, + bool added_class_table) { + Thread* const self = Thread::Current(); + gc::Heap* const heap = Runtime::Current()->GetHeap(); + const ImageHeader& header = space->GetImageHeader(); + // Add image classes into the class table for the class loader, and fixup the dex caches and + // class loader fields. + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* table = InsertClassTableForClassLoader(class_loader.Get()); + // TODO: Store class table in the image to avoid manually adding the classes. + for (int32_t i = 0, num_dex_caches = dex_caches->GetLength(); i < num_dex_caches; i++) { + mirror::DexCache* const dex_cache = dex_caches->Get(i); + const DexFile* const dex_file = dex_cache->GetDexFile(); + // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and + // copy over the arrays. + DCHECK(dex_file != nullptr); + const size_t num_strings = dex_file->NumStringIds(); + const size_t num_types = dex_file->NumTypeIds(); + const size_t num_methods = dex_file->NumMethodIds(); + const size_t num_fields = dex_file->NumFieldIds(); + CHECK_EQ(num_strings, dex_cache->NumStrings()); + CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); + CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); + CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); + if (dex_file->GetOatDexFile() != nullptr && + dex_file->GetOatDexFile()->GetDexCacheArrays() != nullptr) { + DexCacheArraysLayout layout(image_pointer_size_, dex_file); + uint8_t* const raw_arrays = dex_file->GetOatDexFile()->GetDexCacheArrays(); + // The space is not yet visible to the GC, we can avoid the read barriers and use + // std::copy_n. + if (num_strings != 0u) { + GcRoot<mirror::String>* const strings = + reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) { + DCHECK(strings[j].IsNull()); + } + std::copy_n(dex_cache->GetStrings(), num_strings, strings); + dex_cache->SetStrings(strings); } - AppendToBootClassPath(*dex_file.get(), dex_cache); - opened_dex_files_.push_back(std::move(dex_file)); + if (num_types != 0u) { + GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes(); + GcRoot<mirror::Class>* const types = + reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) { + DCHECK(types[j].IsNull()); + } + std::copy_n(image_resolved_types, num_types, types); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + // This leaves random garbage at the start of the dex cache array, but nobody should ever + // read from it again. + *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types; + dex_cache->SetResolvedTypes(types); + } + if (num_methods != 0u) { + ArtMethod** const methods = reinterpret_cast<ArtMethod**>( + raw_arrays + layout.MethodsOffset()); + ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods(); + for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) { + DCHECK(methods[j] == nullptr); + } + std::copy_n(image_resolved_methods, num_methods, methods); + // Store a pointer to the new location for fast ArtMethod patching without requiring map. + *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods; + dex_cache->SetResolvedMethods(methods); + } + if (num_fields != 0u) { + ArtField** const fields = reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) { + DCHECK(fields[j] == nullptr); + } + std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields); + dex_cache->SetResolvedFields(fields); + } } + { + WriterMutexLock mu2(self, dex_lock_); + // Make sure to do this after we update the arrays since we store the resolved types array + // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the + // BSS. + mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self, + *dex_file, + /*allow_failure*/true); + CHECK(existing_dex_cache == nullptr); + StackHandleScope<1> hs3(self); + RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache)); + } + GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes(); + if (!added_class_table) { + for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read<kWithoutReadBarrier>(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + // Update the class loader from the one in the image class loader to the one that loaded + // the app image. + klass->SetClassLoader(class_loader.Get()); + // If there are multiple dex caches, there may be the same class multiple times + // in different dex caches. Check for this since inserting will add duplicates + // otherwise. + if (num_dex_caches > 1) { + mirror::Class* existing = table->LookupByDescriptor(klass); + if (existing != nullptr) { + DCHECK_EQ(existing, klass) << PrettyClass(klass); + } else { + table->Insert(klass); + } + } else { + table->Insert(klass); + } + // Double checked VLOG to avoid overhead. + if (VLOG_IS_ON(image)) { + VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus(); + if (!klass->IsArrayClass()) { + VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation(); + } + VLOG(image) << "Direct methods"; + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } + VLOG(image) << "Virtual methods"; + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + VLOG(image) << PrettyMethod(&m); + } + } + } + } + } + if (kIsDebugBuild) { + for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) { + // The image space is not yet added to the heap, avoid read barriers. + mirror::Class* klass = types[j].Read<kWithoutReadBarrier>(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + if (kIsDebugBuild) { + DCHECK_EQ(table->LookupByDescriptor(klass), klass); + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) { + CHECK_EQ(table->LookupByDescriptor(super_class), super_class); + } + } + DCHECK_EQ(klass->GetClassLoader(), class_loader.Get()); + if (kIsDebugBuild) { + for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } + } + VLOG(image) << "Virtual methods"; + for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) { + const void* code = m.GetEntryPointFromQuickCompiledCode(); + const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code; + if (!IsQuickResolutionStub(code) && + !IsQuickGenericJniStub(code) && + !IsQuickToInterpreterBridge(code) && + !m.IsNative()) { + DCHECK_EQ(code, oat_code) << PrettyMethod(&m); + } + } + } + } + } + } + } + { + FixupArtMethodArrayVisitor visitor(header); + header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &visitor, space->Begin(), sizeof(void*)); + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get()); + } + if (kIsDebugBuild) { + ClassTable* const class_table = class_loader.Get()->GetClassTable(); + VerifyClassInTableArtMethodVisitor visitor2(class_table); + header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &visitor2, space->Begin(), sizeof(void*)); } +} - if (!ValidPointerSize(image_pointer_size_)) { - *error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_); +bool ClassLinker::AddImageSpace( + gc::space::ImageSpace* space, + Handle<mirror::ClassLoader> class_loader, + jobjectArray dex_elements, + const char* dex_location, + std::vector<std::unique_ptr<const DexFile>>* out_dex_files, + std::string* error_msg) { + DCHECK(out_dex_files != nullptr); + DCHECK(error_msg != nullptr); + const uint64_t start_time = NanoTime(); + const bool app_image = class_loader.Get() != nullptr; + const ImageHeader& header = space->GetImageHeader(); + mirror::Object* dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches); + DCHECK(dex_caches_object != nullptr); + Runtime* const runtime = Runtime::Current(); + gc::Heap* const heap = runtime->GetHeap(); + Thread* const self = Thread::Current(); + StackHandleScope<2> hs(self); + Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches( + hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>())); + Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( + header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>())); + const OatFile* oat_file = space->GetOatFile(); + std::unordered_set<mirror::ClassLoader*> image_class_loaders; + // Check that the image is what we are expecting. + if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) { + *error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu", + static_cast<size_t>(space->GetImageHeader().GetPointerSize()), + image_pointer_size_); + return false; + } + DCHECK(class_roots.Get() != nullptr); + if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) { + *error_msg = StringPrintf("Expected %d class roots but got %d", + class_roots->GetLength(), + static_cast<int32_t>(kClassRootsMax)); + return false; + } + // Check against existing class roots to make sure they match the ones in the boot image. + for (size_t i = 0; i < kClassRootsMax; i++) { + if (class_roots->Get(i) != GetClassRoot(static_cast<ClassRoot>(i))) { + *error_msg = "App image class roots must have pointer equality with runtime ones."; + return false; + } + } + if (oat_file->GetOatHeader().GetDexFileCount() != + static_cast<uint32_t>(dex_caches->GetLength())) { + *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from " + "image"; return false; } - // Set classes on AbstractMethod early so that IsMethod tests can be performed during the live - // bitmap walk. - if (!runtime->IsAotCompiler()) { - // Only the Aot compiler supports having an image with a different pointer size than the - // runtime. This happens on the host for compile 32 bit tests since we use a 64 bit libart - // compiler. We may also use 32 bit dex2oat on a system with 64 bit apps. - if (image_pointer_size_ != sizeof(void*)) { - *error_msg = StringPrintf("Runtime must use current image pointer size: %zu vs %zu", - image_pointer_size_ , - sizeof(void*)); + StackHandleScope<1> hs2(self); + MutableHandle<mirror::DexCache> h_dex_cache(hs2.NewHandle<mirror::DexCache>(nullptr)); + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + h_dex_cache.Assign(dex_caches->Get(i)); + std::string dex_file_location(h_dex_cache->GetLocation()->ToModifiedUtf8()); + // TODO: Only store qualified paths. + // If non qualified, qualify it. + if (dex_file_location.find('/') == std::string::npos) { + std::string dex_location_path = dex_location; + const size_t pos = dex_location_path.find_last_of('/'); + CHECK_NE(pos, std::string::npos); + dex_location_path = dex_location_path.substr(0, pos + 1); // Keep trailing '/' + dex_file_location = dex_location_path + dex_file_location; + } + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(), + nullptr); + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("Failed finding oat dex file for %s %s", + oat_file->GetLocation().c_str(), + dex_file_location.c_str()); return false; } + std::string inner_error_msg; + std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg); + if (dex_file == nullptr) { + *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'", + dex_file_location.c_str(), + oat_file->GetLocation().c_str(), + inner_error_msg.c_str()); + return false; + } + + if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x", + dex_file_location.c_str(), + dex_file->GetLocationChecksum(), + oat_dex_file->GetDexFileLocationChecksum()); + return false; + } + + if (app_image) { + // The current dex file field is bogus, overwrite it so that we can get the dex file in the + // loop below. + h_dex_cache->SetDexFile(dex_file.get()); + // Check that each class loader resolved the same way. + // TODO: Store image class loaders as image roots. + GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes(); + for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) { + mirror::Class* klass = types[j].Read(); + if (klass != nullptr) { + DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError); + mirror::ClassLoader* image_class_loader = klass->GetClassLoader(); + image_class_loaders.insert(image_class_loader); + } + } + } else { + if (kSanityCheckObjects) { + SanityCheckArtMethodPointerArray(h_dex_cache->GetResolvedMethods(), + h_dex_cache->NumResolvedMethods(), + image_pointer_size_, + heap->GetBootImageSpaces()); + } + // Register dex files, keep track of existing ones that are conflicts. + AppendToBootClassPath(*dex_file.get(), h_dex_cache); + } + out_dex_files->push_back(std::move(dex_file)); + } + + if (app_image) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + // Check that the class loader resolves the same way as the ones in the image. + // Image class loader [A][B][C][image dex files] + // Class loader = [???][dex_elements][image dex files] + // Need to ensure that [???][dex_elements] == [A][B][C]. + // For each class loader, PathClassLoader, the laoder checks the parent first. Also the logic + // for PathClassLoader does this by looping through the array of dex files. To ensure they + // resolve the same way, simply flatten the hierarchy in the way the resolution order would be, + // and check that the dex file names are the same. + for (mirror::ClassLoader* image_class_loader : image_class_loaders) { + std::list<mirror::String*> image_dex_file_names; + std::string temp_error_msg; + if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) { + *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'", + temp_error_msg.c_str()); + return false; + } + std::list<mirror::String*> loader_dex_file_names; + if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) { + *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'", + temp_error_msg.c_str()); + return false; + } + // Add the temporary dex path list elements at the end. + auto* elements = soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements); + for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) { + mirror::Object* element = elements->GetWithoutChecks(i); + if (element != nullptr) { + // If we are somewhere in the middle of the array, there may be nulls at the end. + loader_dex_file_names.push_back(GetDexPathListElementName(soa, element)); + } + } + // Ignore the number of image dex files since we are adding those to the class loader anyways. + CHECK_GE(static_cast<size_t>(image_dex_file_names.size()), + static_cast<size_t>(dex_caches->GetLength())); + size_t image_count = image_dex_file_names.size() - dex_caches->GetLength(); + // Check that the dex file names match. + bool equal = image_count == loader_dex_file_names.size(); + if (equal) { + auto it1 = image_dex_file_names.begin(); + auto it2 = loader_dex_file_names.begin(); + for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) { + equal = equal && (*it1)->Equals(*it2); + } + } + if (!equal) { + *error_msg = "Rejecting application image due to class loader mismatch"; + return false; + } + } } if (kSanityCheckObjects) { - for (auto dex_caches : dex_caches_vector) { - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - auto* dex_cache = dex_caches->Get(i); - for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) { - auto* field = dex_cache->GetResolvedField(j, image_pointer_size_); - if (field != nullptr) { - CHECK(field->GetDeclaringClass()->GetClass() != nullptr); - } + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + auto* dex_cache = dex_caches->Get(i); + for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) { + auto* field = dex_cache->GetResolvedField(j, image_pointer_size_); + if (field != nullptr) { + CHECK(field->GetDeclaringClass()->GetClass() != nullptr); } } } - heap->VisitObjects(SanityCheckObjectsCallback, nullptr); + if (!app_image) { + heap->VisitObjects(SanityCheckObjectsCallback, nullptr); + } } // Set entry point to interpreter if in InterpretOnly mode. if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) { - for (gc::space::ImageSpace* space : spaces) { - const ImageHeader& header = space->GetImageHeader(); - const ImageSection& methods = header.GetMethodsSection(); - SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_); - methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); - } - } - - // reinit class_roots_ - mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass)); - class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); - - // reinit array_iftable_ from any array class instance, they should be == - array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable()); - DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); - // String class root was set above - mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField)); - mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); - mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor)); - mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass)); - mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod)); - mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); - mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); - mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); - mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); - mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass)); - mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass)); - mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass)); - mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass)); - mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass)); - mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass)); - mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); - mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); - - size_t class_tables_added = 0; - for (gc::space::ImageSpace* space : spaces) { - const ImageHeader& header = space->GetImageHeader(); - const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable); - if (section.Size() > 0u) { - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* const class_table = InsertClassTableForClassLoader(nullptr); - class_table->ReadFromMemory(space->Begin() + section.Offset()); - ++class_tables_added; + const ImageSection& methods = header.GetMethodsSection(); + SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_); + methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); + } + + const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable); + bool added_class_table = false; + if (app_image) { + GetOrCreateAllocatorForClassLoader(class_loader.Get()); // Make sure we have a linear alloc. + } + if (class_table_section.Size() > 0u) { + const uint64_t start_time2 = NanoTime(); + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); + class_table->ReadFromMemory(space->Begin() + class_table_section.Offset()); + if (app_image) { + class_table->SetClassLoader(class_loader.Get()); + } else { + dex_cache_boot_image_class_lookup_required_ = false; } + VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2); + added_class_table = true; } - if (class_tables_added != 0) { - // Either all of the image spaces have an empty class section or none do. In the case where - // an image space has no classes, it will still have a non-empty class section that contains - // metadata. - CHECK_EQ(spaces.size(), class_tables_added) - << "Expected non-empty class section for each image space."; - dex_cache_boot_image_class_lookup_required_ = false; + if (app_image) { + UpdateAppImageClassLoadersAndDexCaches(space, class_loader, dex_caches, added_class_table); } - - FinishInit(self); - - VLOG(startup) << "ClassLinker::InitFromImage exiting"; - + VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time); return true; } @@ -1527,14 +1972,6 @@ ClassPathEntry FindInClassPath(const char* descriptor, return ClassPathEntry(nullptr, nullptr); } -static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa, - mirror::ClassLoader* class_loader) - SHARED_REQUIRES(Locks::mutator_lock_) { - return class_loader == nullptr || - class_loader->GetClass() == - soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader); -} - bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa, Thread* self, const char* descriptor, @@ -1820,6 +2257,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, // inserted before we allocate / fill in these fields. LoadClass(self, dex_file, dex_class_def, klass); if (self->IsExceptionPending()) { + VLOG(class_linker) << self->GetException()->Dump(); // An exception occured during load, set status to erroneous while holding klass' lock in case // notification is necessary. if (!klass->IsErroneous()) { @@ -2487,8 +2925,21 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, Thread* const self = Thread::Current(); dex_lock_.AssertExclusiveHeld(self); CHECK(dex_cache.Get() != nullptr) << dex_file.GetLocation(); - CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation())) - << dex_cache->GetLocation()->ToModifiedUtf8() << " " << dex_file.GetLocation(); + // For app images, the dex cache location may be a suffix of the dex file location since the + // dex file location is an absolute path. + const std::string dex_cache_location = dex_cache->GetLocation()->ToModifiedUtf8(); + const size_t dex_cache_length = dex_cache_location.length(); + CHECK_GT(dex_cache_length, 0u) << dex_file.GetLocation(); + std::string dex_file_location = dex_file.GetLocation(); + CHECK_GE(dex_file_location.length(), dex_cache_length) + << dex_cache_location << " " << dex_file.GetLocation(); + // Take suffix. + const std::string dex_file_suffix = dex_file_location.substr( + dex_file_location.length() - dex_cache_length, + dex_cache_length); + // Example dex_cache location is SettingsProvider.apk and + // dex file location is /system/priv-app/SettingsProvider/SettingsProvider.apk + CHECK_EQ(dex_cache_location, dex_file_suffix); // Clean up pass to remove null dex caches. // Null dex caches can occur due to class unloading and we are lazily removing null entries. JavaVMExt* const vm = self->GetJniEnv()->vm; @@ -5523,15 +5974,6 @@ bool ClassLinker::LinkInterfaceMethods( Runtime* const runtime = Runtime::Current(); const bool is_interface = klass->IsInterface(); - // TODO It might in the future prove useful to make interfaces have full iftables, allowing a - // faster invoke-super implementation in the interpreter/across dex-files. - // We will just skip doing any of this on non-debug builds for speed. - if (is_interface && - !kIsDebugBuild && - !runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) { - return true; - } - const bool has_superclass = klass->HasSuperClass(); const bool fill_tables = !is_interface; const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U; @@ -6931,10 +7373,13 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi ArtField* cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie); DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->GetType<false>()); + ArtField* file_name_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_fileName); + DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->GetType<false>()); + // Fill the elements array. int32_t index = 0; for (const DexFile* dex_file : dex_files) { - StackHandleScope<3> hs2(self); + StackHandleScope<4> hs2(self); // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the // oat file but we can leave it null. @@ -6949,6 +7394,11 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi DCHECK(h_dex_file.Get() != nullptr); cookie_field->SetObject<false>(h_dex_file.Get(), h_long_array.Get()); + Handle<mirror::String> h_file_name = hs2.NewHandle( + mirror::String::AllocFromModifiedUtf8(self, dex_file->GetLocation().c_str())); + DCHECK(h_file_name.Get() != nullptr); + file_name_field->SetObject<false>(h_dex_file.Get(), h_file_name.Get()); + Handle<mirror::Object> h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self)); DCHECK(h_element.Get() != nullptr); element_file_field->SetObject<false>(h_element.Get(), h_dex_file.Get()); @@ -7048,6 +7498,7 @@ void ClassLinker::CleanupClassLoaders() { if (class_loader != nullptr) { ++it; } else { + VLOG(class_linker) << "Freeing class loader"; DeleteClassLoader(self, data); it = class_loaders_.erase(it); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f1fd0c38f1..4975c29742 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -120,11 +120,25 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); - // Initialize class linker from one or more images. - bool InitFromImage(std::string* error_msg) + // Initialize class linker from one or more boot images. + bool InitFromBootImage(std::string* error_msg) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_); + // Add an image space to the class linker, may fix up classloader fields and dex cache fields. + // The dex files that were newly opened for the space are placed in the out argument + // out_dex_files. Returns true if the operation succeeded. + // The space must be already added to the heap before calling AddImageSpace since we need to + // properly handle read barriers and object marking. + bool AddImageSpace(gc::space::ImageSpace* space, + Handle<mirror::ClassLoader> class_loader, + jobjectArray dex_elements, + const char* dex_location, + std::vector<std::unique_ptr<const DexFile>>* out_dex_files, + std::string* error_msg) + REQUIRES(!dex_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Finds a class by its descriptor, loading it if necessary. // If class_loader is null, searches boot_class_path_. mirror::Class* FindClass(Thread* self, @@ -985,8 +999,16 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_); + void UpdateAppImageClassLoadersAndDexCaches( + gc::space::ImageSpace* space, + Handle<mirror::ClassLoader> class_loader, + Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches, + bool added_class_table) + REQUIRES(!dex_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + std::vector<const DexFile*> boot_class_path_; - std::vector<std::unique_ptr<const DexFile>> opened_dex_files_; + std::vector<std::unique_ptr<const DexFile>> boot_dex_files_; mutable ReaderWriterMutex dex_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; // JNI weak globals and side data to allow dex caches to get unloaded. We lazily delete weak @@ -1041,8 +1063,8 @@ class ClassLinker { friend class ImageWriter; // for GetClassRoots friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub + ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for DexLock, and RegisterDexFileLocked ART_FRIEND_TEST(mirror::DexCacheTest, Open); // for AllocDexCache - DISALLOW_COPY_AND_ASSIGN(ClassLinker); }; diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 471d7ca5e6..40dfda9316 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -1201,4 +1201,42 @@ TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) { EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded()); } +// Regression test for b/26799552. +TEST_F(ClassLinkerTest, RegisterDexFileName) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr)); + { + ReaderMutexLock mu(soa.Self(), *class_linker->DexLock()); + for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { + dex_cache.Assign(down_cast<mirror::DexCache*>(soa.Self()->DecodeJObject(data.weak_root))); + if (dex_cache.Get() != nullptr) { + break; + } + } + ASSERT_TRUE(dex_cache.Get() != nullptr); + } + // Make a copy of the dex cache and change the name. + dex_cache.Assign(dex_cache->Clone(soa.Self())->AsDexCache()); + const uint16_t data[] = { 0x20AC, 0x20A1 }; + Handle<mirror::String> location(hs.NewHandle(mirror::String::AllocFromUtf16(soa.Self(), + arraysize(data), + data))); + dex_cache->SetLocation(location.Get()); + const DexFile* old_dex_file = dex_cache->GetDexFile(); + + DexFile* dex_file = new DexFile(old_dex_file->Begin(), + old_dex_file->Size(), + location->ToModifiedUtf8(), + 0u, + nullptr, + nullptr); + { + WriterMutexLock mu(soa.Self(), *class_linker->DexLock()); + // Check that inserting with a UTF16 name works. + class_linker->RegisterDexFileLocked(*dex_file, dex_cache); + } +} + } // namespace art diff --git a/runtime/class_table.cc b/runtime/class_table.cc index df2dbf416c..2a4f0e01af 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -40,6 +40,16 @@ bool ClassTable::Contains(mirror::Class* klass) { return false; } +mirror::Class* ClassTable::LookupByDescriptor(mirror::Class* klass) { + for (ClassSet& class_set : classes_) { + auto it = class_set.Find(GcRoot<mirror::Class>(klass)); + if (it != class_set.end()) { + return it->Read(); + } + } + return nullptr; +} + mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* klass, size_t hash) { // Should only be updating latest table. auto existing_it = classes_.back().FindWithHash(descriptor, hash); @@ -173,4 +183,12 @@ size_t ClassTable::ReadFromMemory(uint8_t* ptr) { return read_count; } +void ClassTable::SetClassLoader(mirror::ClassLoader* class_loader) { + for (const ClassSet& class_set : classes_) { + for (const GcRoot<mirror::Class>& root : class_set) { + root.Read()->SetClassLoader(class_loader); + } + } +} + } // namespace art diff --git a/runtime/class_table.h b/runtime/class_table.h index 911f3c22db..0b420352c3 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -84,9 +84,14 @@ class ClassTable { bool Visit(ClassVisitor* visitor) SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + // Return the first class that matches the descriptor. Returns null if there are none. mirror::Class* Lookup(const char* descriptor, size_t hash) SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + // Return the first class that matches the descriptor of klass. Returns null if there are none. + mirror::Class* LookupByDescriptor(mirror::Class* klass) + SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + void Insert(mirror::Class* klass) REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -107,10 +112,17 @@ class ClassTable { // Combines all of the tables into one class set. size_t WriteToMemory(uint8_t* ptr) const SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_); + + // Read a table from ptr and put it at the front of the class set. size_t ReadFromMemory(uint8_t* ptr) REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + // Change the class loader of all the contained classes. + void SetClassLoader(mirror::ClassLoader* class_loader) + REQUIRES(Locks::classlinker_classes_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + private: class ClassDescriptorHashEquals { public: diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 6e11cf88c4..a0f875d6b8 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -230,11 +230,11 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati Dbg::PostException(exception_object); } - // We only care about how many backward branches were executed in the Jit. - void BackwardBranch(Thread* /*thread*/, ArtMethod* method, int32_t dex_pc_offset) + // We only care about branches in the Jit. + void Branch(Thread* /*thread*/, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { - LOG(ERROR) << "Unexpected backward branch event in debugger " << PrettyMethod(method) - << " " << dex_pc_offset; + LOG(ERROR) << "Unexpected branch event in debugger " << PrettyMethod(method) + << " " << dex_pc << ", " << dex_pc_offset; } // We only care about invokes in the Jit. diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index bc8ba97a22..9b93c131df 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -687,8 +687,8 @@ const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx, return nullptr; } -void DexFile::CreateTypeLookupTable() const { - lookup_table_.reset(TypeLookupTable::Create(*this)); +void DexFile::CreateTypeLookupTable(uint8_t* storage) const { + lookup_table_.reset(TypeLookupTable::Create(*this, storage)); } // Given a signature place the type ids into the given vector diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 108b8d2441..200121e61f 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1157,7 +1157,7 @@ class DexFile { return lookup_table_.get(); } - void CreateTypeLookupTable() const; + void CreateTypeLookupTable(uint8_t* storage = nullptr) const; private: // Opens a .dex file @@ -1260,6 +1260,7 @@ class DexFile { mutable std::unique_ptr<TypeLookupTable> lookup_table_; friend class DexFileVerifierTest; + ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor }; struct DexFileReference { diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 727f4fc659..7a852e216e 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -2538,20 +2538,6 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, return false; } - // Only the static initializer may have code in an interface. - // TODO We should have some way determine whether to allow this experimental flag without the - // runtime being started. - // We assume experimental flags are enabled when running without a runtime to enable tools like - // dexdump to handle dex files with these features. - if (((class_access_flags & kAccInterface) != 0) - && !is_clinit_by_name - && Runtime::Current() != nullptr - && !Runtime::Current()->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) { - *error_msg = StringPrintf("Non-clinit interface method %" PRIu32 " should not have code", - method_index); - return false; - } - // Instance constructors must not be synchronized and a few other flags. if (is_init_by_name) { static constexpr uint32_t kInitAllowed = diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 272249c23a..b67af53458 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -686,31 +686,6 @@ TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) { // Set of dex files for interface method tests. As it's not as easy to mutate method names, it's // just easier to break up bad cases. -// Interface with an instance constructor. -// -// .class public interface LInterfaceMethodFlags; -// .super Ljava/lang/Object; -// -// .method public static constructor <clinit>()V -// .registers 1 -// return-void -// .end method -// -// .method public constructor <init>()V -// .registers 1 -// return-void -// .end method -static const char kMethodFlagsInterfaceWithInit[] = - "ZGV4CjAzNQDRNt+hZ6X3I+xe66iVlCW7h9I38HmN4SvUAQAAcAAAAHhWNBIAAAAAAAAAAEwBAAAF" - "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAAAIAQAAzAAAAMwA" - "AADWAAAA3gAAAPYAAAAKAQAAAgAAAAMAAAAEAAAABAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAQAA" - "AAAAAAABAgAAAQAAAAAAAAD/////AAAAADoBAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AFkxJbnRl" - "cmZhY2VNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAAAAAAAAAAAQAAAAAAAAAA" - "AAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAIAAImABJQCAYGABKgCAAALAAAAAAAAAAEA" - "AAAAAAAAAQAAAAUAAABwAAAAAgAAAAMAAACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAA" - "AAEAAACsAAAAAiAAAAUAAADMAAAAAxAAAAEAAAAQAQAAASAAAAIAAAAUAQAAACAAAAEAAAA6AQAA" - "ABAAAAEAAABMAQAA"; - // Standard interface. Use declared-synchronized again for 3B encoding. // // .class public interface LInterfaceMethodFlags; @@ -751,13 +726,6 @@ static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) { } TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) { - // Reject interface with <init>. - VerifyModification( - kMethodFlagsInterfaceWithInit, - "method_flags_interface_with_init", - [](DexFile* dex_file ATTRIBUTE_UNUSED) {}, - "Non-clinit interface method 1 should not have code"); - VerifyModification( kMethodFlagsInterface, "method_flags_interface_ok", diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 08c9b49729..638fdb4f46 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1779,8 +1779,7 @@ class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor { void FinalizeHandleScope(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_); - StackReference<mirror::Object>* GetFirstHandleScopeEntry() - SHARED_REQUIRES(Locks::mutator_lock_) { + StackReference<mirror::Object>* GetFirstHandleScopeEntry() { return handle_scope_->GetHandle(0).GetReference(); } diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h index 2e674e95c6..198f3fa56e 100644 --- a/runtime/experimental_flags.h +++ b/runtime/experimental_flags.h @@ -27,7 +27,6 @@ struct ExperimentalFlags { enum { kNone = 0x0000, kLambdas = 0x0001, - kDefaultMethods = 0x0002, }; constexpr ExperimentalFlags() : value_(0x0000) {} @@ -69,10 +68,6 @@ inline std::ostream& operator<<(std::ostream& stream, const ExperimentalFlags& e stream << (started ? "|" : "") << "kLambdas"; started = true; } - if (e & ExperimentalFlags::kDefaultMethods) { - stream << (started ? "|" : "") << "kDefaultMethods"; - started = true; - } if (!started) { stream << "kNone"; } diff --git a/runtime/gc/accounting/card_table.h b/runtime/gc/accounting/card_table.h index 88a6c6c6e4..b6af90806b 100644 --- a/runtime/gc/accounting/card_table.h +++ b/runtime/gc/accounting/card_table.h @@ -115,6 +115,8 @@ class CardTable { // Resets all of the bytes in the card table to clean. void ClearCardTable(); + + // Clear a range of cards that covers start to end, start and end must be aligned to kCardSize. void ClearCardRange(uint8_t* start, uint8_t* end); // Resets all of the bytes in the card table which do not map to the image space. diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc index edab1b0a60..349d6ffaa9 100644 --- a/runtime/gc/accounting/mod_union_table_test.cc +++ b/runtime/gc/accounting/mod_union_table_test.cc @@ -22,6 +22,7 @@ #include "mirror/array-inl.h" #include "space_bitmap-inl.h" #include "thread-inl.h" +#include "thread_list.h" namespace art { namespace gc { @@ -184,7 +185,11 @@ void ModUnionTableTest::RunTest(ModUnionTableFactory::TableType type) { std::unique_ptr<space::DlMallocSpace> other_space(space::DlMallocSpace::Create( "other space", 128 * KB, 4 * MB, 4 * MB, nullptr, false)); ASSERT_TRUE(other_space.get() != nullptr); - heap->AddSpace(other_space.get()); + { + ScopedThreadSuspension sts(self, kSuspended); + ScopedSuspendAll ssa("Add image space"); + heap->AddSpace(other_space.get()); + } std::unique_ptr<ModUnionTable> table(ModUnionTableFactory::Create( type, space, other_space.get())); ASSERT_TRUE(table.get() != nullptr); @@ -253,6 +258,8 @@ void ModUnionTableTest::RunTest(ModUnionTableFactory::TableType type) { std::ostringstream oss2; table->Dump(oss2); // Remove the space we added so it doesn't persist to the next test. + ScopedThreadSuspension sts(self, kSuspended); + ScopedSuspendAll ssa("Add image space"); heap->RemoveSpace(other_space.get()); } diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index 61c67f86c4..4cf5b4f643 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -167,8 +167,12 @@ inline bool SpaceBitmap<kAlignment>::Modify(const mirror::Object* obj) { uintptr_t* address = &bitmap_begin_[index]; uintptr_t old_word = *address; if (kSetBit) { + // Check the bit before setting the word incase we are trying to mark a read only bitmap + // like an image space bitmap. This bitmap is mapped as read only and will fault if we + // attempt to change any words. Since all of the objects are marked, this will never + // occur if we check before setting the bit. This also prevents dirty pages that would + // occur if the bitmap was read write and we did not check the bit. if ((old_word & mask) == 0) { - // Avoid dirtying the page if possible. *address = old_word | mask; } } else { diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc index e747f00c92..dc4e31253f 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -36,7 +36,7 @@ static void art_heap_usage_error(const char* function, void* p); #pragma GCC diagnostic ignored "-Wredundant-decls" #pragma GCC diagnostic ignored "-Wempty-body" #pragma GCC diagnostic ignored "-Wstrict-aliasing" -#include "../../../bionic/libc/upstream-dlmalloc/malloc.c" +#include "../../../external/dlmalloc/malloc.c" #pragma GCC diagnostic pop static void* art_heap_morecore(void* m, intptr_t increment) { diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h index 0558921a56..50e2622e89 100644 --- a/runtime/gc/allocator/dlmalloc.h +++ b/runtime/gc/allocator/dlmalloc.h @@ -32,7 +32,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" -#include "../../bionic/libc/upstream-dlmalloc/malloc.h" +#include "../../external/dlmalloc/malloc.h" #pragma GCC diagnostic pop #ifdef __ANDROID__ diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index bcfcb89e62..9397c3585a 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -24,6 +24,7 @@ #include "gc/reference_processor.h" #include "gc/space/image_space.h" #include "gc/space/space-inl.h" +#include "image-inl.h" #include "intern_table.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" @@ -2066,8 +2067,9 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref) { } void ConcurrentCopying::FinishPhase() { + Thread* const self = Thread::Current(); { - MutexLock mu(Thread::Current(), mark_stack_lock_); + MutexLock mu(self, mark_stack_lock_); CHECK_EQ(pooled_mark_stacks_.size(), kMarkStackPoolSize); } region_space_ = nullptr; @@ -2075,7 +2077,8 @@ void ConcurrentCopying::FinishPhase() { MutexLock mu(Thread::Current(), skipped_blocks_lock_); skipped_blocks_map_.clear(); } - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + ReaderMutexLock mu(self, *Locks::mutator_lock_); + WriterMutexLock mu2(self, *Locks::heap_bitmap_lock_); heap_->ClearMarkedObjects(); } diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index 5d21c599e4..76315fe7cc 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -57,7 +57,7 @@ class ConcurrentCopying : public GarbageCollector { // Enable the from-space bytes/objects check. static constexpr bool kEnableFromSpaceAccountingCheck = true; // Enable verbose mode. - static constexpr bool kVerboseMode = true; + static constexpr bool kVerboseMode = false; ConcurrentCopying(Heap* heap, const std::string& name_prefix = ""); ~ConcurrentCopying(); diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index 954c80ec7b..580486aa68 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -153,7 +153,9 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark void ResetCumulativeStatistics() REQUIRES(!pause_histogram_lock_); // Swap the live and mark bitmaps of spaces that are active for the collector. For partial GC, // this is the allocation space, for full GC then we swap the zygote bitmaps too. - void SwapBitmaps() REQUIRES(Locks::heap_bitmap_lock_); + void SwapBitmaps() + REQUIRES(Locks::heap_bitmap_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); uint64_t GetTotalPausedTimeNs() REQUIRES(!pause_histogram_lock_); int64_t GetTotalFreedBytes() const { return total_freed_bytes_; diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc index 4884e668c2..ea290dd07d 100644 --- a/runtime/gc/collector/immune_spaces_test.cc +++ b/runtime/gc/collector/immune_spaces_test.cc @@ -112,8 +112,13 @@ class DummyImageSpace : public space::ImageSpace { /*oat_data_begin*/PointerToLowMemUInt32(map->End()), /*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size), /*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size), + /*boot_image_begin*/0u, + /*boot_image_size*/0u, + /*boot_oat_begin*/0u, + /*boot_oat_size*/0u, /*pointer_size*/sizeof(void*), /*compile_pic*/false, + /*is_pic*/false, ImageHeader::kStorageModeUncompressed, /*storage_size*/0u); return new DummyImageSpace(map.release(), live_bitmap.release()); diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 8a12094168..48311570b5 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -106,7 +106,7 @@ class MarkCompact : public GarbageCollector { REQUIRES(Locks::mutator_lock_); // Sweeps unmarked objects to complete the garbage collection. - void Sweep(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_); + void Sweep(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); // Sweeps unmarked objects to complete the garbage collection. void SweepLargeObjects(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 5427f88563..64c8e9af04 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -1467,7 +1467,9 @@ void MarkSweep::FinishPhase() { } CHECK(mark_stack_->IsEmpty()); // Ensure that the mark stack is empty. mark_stack_->Reset(); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, *Locks::mutator_lock_); + WriterMutexLock mu2(self, *Locks::heap_bitmap_lock_); heap_->ClearMarkedObjects(); } diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 245f96bdb3..b61bef7317 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -85,7 +85,7 @@ class MarkSweep : public GarbageCollector { void Init(); // Find the default mark bitmap. - void FindDefaultSpaceBitmap(); + void FindDefaultSpaceBitmap() SHARED_REQUIRES(Locks::mutator_lock_); // Marks all objects in the root set at the start of a garbage collection. void MarkRoots(Thread* self) diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index a905904115..0199e1ae56 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -135,7 +135,9 @@ class SemiSpace : public GarbageCollector { REQUIRES(Locks::mutator_lock_); // Sweeps unmarked objects to complete the garbage collection. - virtual void Sweep(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_); + virtual void Sweep(bool swap_bitmaps) + REQUIRES(Locks::heap_bitmap_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); // Sweeps unmarked objects to complete the garbage collection. void SweepLargeObjects(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index d6c1817f13..137540afd2 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -273,10 +273,11 @@ Heap::Heap(size_t initial_size, std::string& image_name = image_file_names[index]; ATRACE_BEGIN("ImageSpace::Create"); std::string error_msg; - space::ImageSpace* boot_image_space = space::ImageSpace::Create(image_name.c_str(), - image_instruction_set, - index > 0, - &error_msg); + space::ImageSpace* boot_image_space = space::ImageSpace::CreateBootImage( + image_name.c_str(), + image_instruction_set, + index > 0, + &error_msg); ATRACE_END(); if (boot_image_space != nullptr) { AddSpace(boot_image_space); @@ -375,12 +376,15 @@ Heap::Heap(size_t initial_size, } // Attempt to create 2 mem maps at or after the requested begin. if (foreground_collector_type_ != kCollectorTypeCC) { - if (separate_non_moving_space) { - main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, - capacity_, &error_str)); + if (separate_non_moving_space || !is_zygote) { + main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], + request_begin, + capacity_, + &error_str)); } else { - // If no separate non-moving space, the main space must come - // right after the image space to avoid a gap. + // If no separate non-moving space and we are the zygote, the main space must come right + // after the image space to avoid a gap. This is required since we want the zygote space to + // be adjacent to the image space. main_mem_map_1.reset(MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity_, PROT_READ | PROT_WRITE, true, false, &error_str)); @@ -488,7 +492,15 @@ Heap::Heap(size_t initial_size, ATRACE_END(); // Allocate the card table. ATRACE_BEGIN("Create card table"); - card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity)); + // We currently don't support dynamically resizing the card table. + // Since we don't know where in the low_4gb the app image will be located, make the card table + // cover the whole low_4gb. TODO: Extend the card table in AddSpace. + UNUSED(heap_capacity); + // Start at 64 KB, we can be sure there are no spaces mapped this low since the address range is + // reserved by the kernel. + static constexpr size_t kMinHeapAddress = 4 * KB; + card_table_.reset(accounting::CardTable::Create(reinterpret_cast<uint8_t*>(kMinHeapAddress), + 4 * GB - kMinHeapAddress)); CHECK(card_table_.get() != nullptr) << "Failed to create card table"; ATRACE_END(); if (foreground_collector_type_ == kCollectorTypeCC && kUseTableLookupReadBarrier) { @@ -789,6 +801,7 @@ bool Heap::IsCompilingBoot() const { if (!Runtime::Current()->IsAotCompiler()) { return false; } + ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { if (space->IsImageSpace() || space->IsZygoteSpace()) { return false; @@ -1249,10 +1262,6 @@ space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) return FindDiscontinuousSpaceFromObject(obj, fail_ok); } -std::vector<space::ImageSpace*> Heap::GetBootImageSpaces() const { - return boot_image_spaces_; -} - void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) { std::ostringstream oss; size_t total_bytes_free = GetFreeMemory(); @@ -1373,15 +1382,18 @@ void Heap::TrimSpaces(Thread* self) { uint64_t total_alloc_space_allocated = 0; uint64_t total_alloc_space_size = 0; uint64_t managed_reclaimed = 0; - for (const auto& space : continuous_spaces_) { - if (space->IsMallocSpace()) { - gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); - if (malloc_space->IsRosAllocSpace() || !CareAboutPauseTimes()) { - // Don't trim dlmalloc spaces if we care about pauses since this can hold the space lock - // for a long period of time. - managed_reclaimed += malloc_space->Trim(); + { + ScopedObjectAccess soa(self); + for (const auto& space : continuous_spaces_) { + if (space->IsMallocSpace()) { + gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); + if (malloc_space->IsRosAllocSpace() || !CareAboutPauseTimes()) { + // Don't trim dlmalloc spaces if we care about pauses since this can hold the space lock + // for a long period of time. + managed_reclaimed += malloc_space->Trim(); + } + total_alloc_space_size += malloc_space->Size(); } - total_alloc_space_size += malloc_space->Size(); } } total_alloc_space_allocated = GetBytesAllocated(); @@ -1512,6 +1524,7 @@ std::string Heap::DumpSpaces() const { } void Heap::DumpSpaces(std::ostream& stream) const { + ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::ContinuousSpaceBitmap* mark_bitmap = space->GetMarkBitmap(); @@ -1590,6 +1603,9 @@ void Heap::RecordFreeRevoke() { } space::RosAllocSpace* Heap::GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) const { + if (rosalloc_space_ != nullptr && rosalloc_space_->GetRosAlloc() == rosalloc) { + return rosalloc_space_; + } for (const auto& space : continuous_spaces_) { if (space->AsContinuousSpace()->IsRosAllocSpace()) { if (space->AsContinuousSpace()->AsRosAllocSpace()->GetRosAlloc() == rosalloc) { @@ -3191,7 +3207,13 @@ void Heap::ProcessCards(TimingLogger* timings, } else if (process_alloc_space_cards) { TimingLogger::ScopedTiming t2("AllocSpaceClearCards", timings); if (clear_alloc_space_cards) { - card_table_->ClearCardRange(space->Begin(), space->End()); + uint8_t* end = space->End(); + if (space->IsImageSpace()) { + // Image space end is the end of the mirror objects, it is not necessarily page or card + // aligned. Align up so that the check in ClearCardRange does not fail. + end = AlignUp(end, accounting::CardTable::kCardSize); + } + card_table_->ClearCardRange(space->Begin(), end); } else { // No mod union table for the AllocSpace. Age the cards so that the GC knows that these // cards were dirty before the GC started. @@ -3516,7 +3538,8 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran, void Heap::ClampGrowthLimit() { // Use heap bitmap lock to guard against races with BindLiveToMarkBitmap. - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + ScopedObjectAccess soa(Thread::Current()); + WriterMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_); capacity_ = growth_limit_; for (const auto& space : continuous_spaces_) { if (space->IsMallocSpace()) { @@ -3532,6 +3555,7 @@ void Heap::ClampGrowthLimit() { void Heap::ClearGrowthLimit() { growth_limit_ = capacity_; + ScopedObjectAccess soa(Thread::Current()); for (const auto& space : continuous_spaces_) { if (space->IsMallocSpace()) { gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); @@ -3986,5 +4010,43 @@ void Heap::DisableGCForShutdown() { gc_disabled_for_shutdown_ = true; } +bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const { + for (gc::space::ImageSpace* space : boot_image_spaces_) { + if (space->HasAddress(obj)) { + return true; + } + } + return false; +} + +void Heap::GetBootImagesSize(uint32_t* boot_image_begin, + uint32_t* boot_image_end, + uint32_t* boot_oat_begin, + uint32_t* boot_oat_end) { + DCHECK(boot_image_begin != nullptr); + DCHECK(boot_image_end != nullptr); + DCHECK(boot_oat_begin != nullptr); + DCHECK(boot_oat_end != nullptr); + *boot_image_begin = 0u; + *boot_image_end = 0u; + *boot_oat_begin = 0u; + *boot_oat_end = 0u; + for (gc::space::ImageSpace* space_ : GetBootImageSpaces()) { + const uint32_t image_begin = PointerToLowMemUInt32(space_->Begin()); + const uint32_t image_size = space_->GetImageHeader().GetImageSize(); + if (*boot_image_begin == 0 || image_begin < *boot_image_begin) { + *boot_image_begin = image_begin; + } + *boot_image_end = std::max(*boot_image_end, image_begin + image_size); + const OatFile* boot_oat_file = space_->GetOatFile(); + const uint32_t oat_begin = PointerToLowMemUInt32(boot_oat_file->Begin()); + const uint32_t oat_size = boot_oat_file->Size(); + if (*boot_oat_begin == 0 || oat_begin < *boot_oat_begin) { + *boot_oat_begin = oat_begin; + } + *boot_oat_end = std::max(*boot_oat_end, oat_begin + oat_size); + } +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index e7ea983410..c02e2d3864 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -89,7 +89,6 @@ namespace space { class RegionSpace; class RosAllocSpace; class Space; - class SpaceTest; class ZygoteSpace; } // namespace space @@ -309,7 +308,10 @@ class Heap { void ThreadFlipEnd(Thread* self) REQUIRES(!*thread_flip_lock_); // Clear all of the mark bits, doesn't clear bitmaps which have the same live bits as mark bits. - void ClearMarkedObjects() REQUIRES(Locks::heap_bitmap_lock_); + // Mutator lock is required for GetContinuousSpaces. + void ClearMarkedObjects() + REQUIRES(Locks::heap_bitmap_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); // Initiates an explicit garbage collection. void CollectGarbage(bool clear_soft_references) @@ -360,8 +362,12 @@ class Heap { // due to usage by tests. void SetSpaceAsDefault(space::ContinuousSpace* continuous_space) REQUIRES(!Locks::heap_bitmap_lock_); - void AddSpace(space::Space* space) REQUIRES(!Locks::heap_bitmap_lock_); - void RemoveSpace(space::Space* space) REQUIRES(!Locks::heap_bitmap_lock_); + void AddSpace(space::Space* space) + REQUIRES(!Locks::heap_bitmap_lock_) + REQUIRES(Locks::mutator_lock_); + void RemoveSpace(space::Space* space) + REQUIRES(!Locks::heap_bitmap_lock_) + REQUIRES(Locks::mutator_lock_); // Set target ideal heap utilization ratio, implements // dalvik.system.VMRuntime.setTargetHeapUtilization. @@ -379,7 +385,13 @@ class Heap { void UpdateProcessState(ProcessState process_state) REQUIRES(!*pending_task_lock_, !*gc_complete_lock_); - const std::vector<space::ContinuousSpace*>& GetContinuousSpaces() const { + bool HaveContinuousSpaces() const NO_THREAD_SAFETY_ANALYSIS { + // No lock since vector empty is thread safe. + return !continuous_spaces_.empty(); + } + + const std::vector<space::ContinuousSpace*>& GetContinuousSpaces() const + SHARED_REQUIRES(Locks::mutator_lock_) { return continuous_spaces_; } @@ -519,10 +531,13 @@ class Heap { // get the space that corresponds to an object's address. Current implementation searches all // spaces in turn. If fail_ok is false then failing to find a space will cause an abort. // TODO: consider using faster data structure like binary tree. - space::ContinuousSpace* FindContinuousSpaceFromObject(const mirror::Object*, bool fail_ok) const; + space::ContinuousSpace* FindContinuousSpaceFromObject(const mirror::Object*, bool fail_ok) const + SHARED_REQUIRES(Locks::mutator_lock_); space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(const mirror::Object*, - bool fail_ok) const; - space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const; + bool fail_ok) const + SHARED_REQUIRES(Locks::mutator_lock_); + space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const + SHARED_REQUIRES(Locks::mutator_lock_); void DumpForSigQuit(std::ostream& os) REQUIRES(!*gc_complete_lock_); @@ -578,10 +593,22 @@ class Heap { REQUIRES(Locks::heap_bitmap_lock_); // Unbind any bound bitmaps. - void UnBindBitmaps() REQUIRES(Locks::heap_bitmap_lock_); + void UnBindBitmaps() + REQUIRES(Locks::heap_bitmap_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); // Returns the boot image spaces. There may be multiple boot image spaces. - std::vector<space::ImageSpace*> GetBootImageSpaces() const; + const std::vector<space::ImageSpace*>& GetBootImageSpaces() const { + return boot_image_spaces_; + } + + bool ObjectIsInBootImageSpace(mirror::Object* obj) const + SHARED_REQUIRES(Locks::mutator_lock_); + + void GetBootImagesSize(uint32_t* boot_image_begin, + uint32_t* boot_image_end, + uint32_t* boot_oat_begin, + uint32_t* boot_oat_end); // Permenantly disable moving garbage collection. void DisableMovingGc() REQUIRES(!*gc_complete_lock_); @@ -595,7 +622,8 @@ class Heap { } // Return the corresponding rosalloc space. - space::RosAllocSpace* GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) const; + space::RosAllocSpace* GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) const + SHARED_REQUIRES(Locks::mutator_lock_); space::MallocSpace* GetNonMovingSpace() const { return non_moving_space_; @@ -953,7 +981,8 @@ class Heap { void ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards, - bool clear_alloc_space_cards); + bool clear_alloc_space_cards) + SHARED_REQUIRES(Locks::mutator_lock_); // Push an object onto the allocation stack. void PushOnAllocationStack(Thread* self, mirror::Object** obj) @@ -996,10 +1025,10 @@ class Heap { REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_); // All-known continuous spaces, where objects lie within fixed bounds. - std::vector<space::ContinuousSpace*> continuous_spaces_; + std::vector<space::ContinuousSpace*> continuous_spaces_ GUARDED_BY(Locks::mutator_lock_); // All-known discontinuous spaces, where objects may be placed throughout virtual memory. - std::vector<space::DiscontinuousSpace*> discontinuous_spaces_; + std::vector<space::DiscontinuousSpace*> discontinuous_spaces_ GUARDED_BY(Locks::mutator_lock_); // All-known alloc spaces, where objects may be or have been allocated. std::vector<space::AllocSpace*> alloc_spaces_; @@ -1335,7 +1364,6 @@ class Heap { friend class VerifyReferenceCardVisitor; friend class VerifyReferenceVisitor; friend class VerifyObjectVisitor; - friend class space::SpaceTest; DISALLOW_IMPLICIT_CONSTRUCTORS(Heap); }; diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index e754a52e7e..455d28e229 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -319,7 +319,7 @@ void DlMallocSpace::LogFragmentationAllocFailure(std::ostream& os, namespace allocator { // Implement the dlmalloc morecore callback. -void* ArtDlMallocMoreCore(void* mspace, intptr_t increment) { +void* ArtDlMallocMoreCore(void* mspace, intptr_t increment) SHARED_REQUIRES(Locks::mutator_lock_) { Runtime* runtime = Runtime::Current(); Heap* heap = runtime->GetHeap(); ::art::gc::space::DlMallocSpace* dlmalloc_space = heap->GetDlMallocSpace(); diff --git a/runtime/gc/space/dlmalloc_space_base_test.cc b/runtime/gc/space/dlmalloc_space_base_test.cc deleted file mode 100644 index 93fe1559a0..0000000000 --- a/runtime/gc/space/dlmalloc_space_base_test.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2011 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 "space_test.h" - -#include "dlmalloc_space.h" -#include "scoped_thread_state_change.h" - -namespace art { -namespace gc { -namespace space { - -MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, uint8_t* requested_begin) { - return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, false); -} - -TEST_SPACE_CREATE_FN_BASE(DlMallocSpace, CreateDlMallocSpace) - - -} // namespace space -} // namespace gc -} // namespace art diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 5f6bb8ee4b..891e280ab2 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -30,6 +30,7 @@ #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "gc/accounting/space_bitmap-inl.h" +#include "image-inl.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" #include "oat_file.h" @@ -475,10 +476,10 @@ static bool CheckSpace(const std::string& cache_filename, std::string* error_msg return true; } -ImageSpace* ImageSpace::Create(const char* image_location, - const InstructionSet image_isa, - bool secondary_image, - std::string* error_msg) { +ImageSpace* ImageSpace::CreateBootImage(const char* image_location, + const InstructionSet image_isa, + bool secondary_image, + std::string* error_msg) { std::string system_filename; bool has_system = false; std::string cache_filename; @@ -584,8 +585,13 @@ ImageSpace* ImageSpace::Create(const char* image_location, // assume this if we are using a relocated image (i.e. image checksum // matches) since this is only different by the offset. We need this to // make sure that host tests continue to work. - space = ImageSpace::Init(image_filename->c_str(), image_location, - !(is_system || relocated_version_used), error_msg); + // Since we are the boot image, pass null since we load the oat file from the boot image oat + // file name. + space = ImageSpace::Init(image_filename->c_str(), + image_location, + !(is_system || relocated_version_used), + /* oat_file */nullptr, + error_msg); } if (space != nullptr) { return space; @@ -646,7 +652,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, // we leave Create. ScopedFlock image_lock; image_lock.Init(cache_filename.c_str(), error_msg); - space = ImageSpace::Init(cache_filename.c_str(), image_location, true, error_msg); + space = ImageSpace::Init(cache_filename.c_str(), image_location, true, nullptr, error_msg); if (space == nullptr) { *error_msg = StringPrintf("Failed to load generated image '%s': %s", cache_filename.c_str(), error_msg->c_str()); @@ -669,34 +675,495 @@ void ImageSpace::VerifyImageAllocations() { } } -ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location, - bool validate_oat_file, std::string* error_msg) { +// Helper class for relocating from one range of memory to another. +class RelocationRange { + public: + RelocationRange() = default; + RelocationRange(const RelocationRange&) = default; + RelocationRange(uintptr_t source, uintptr_t dest, uintptr_t length) + : source_(source), + dest_(dest), + length_(length) {} + + bool ContainsSource(uintptr_t address) const { + return address - source_ < length_; + } + + // Translate a source address to the destination space. + uintptr_t ToDest(uintptr_t address) const { + DCHECK(ContainsSource(address)); + return address + Delta(); + } + + // Returns the delta between the dest from the source. + off_t Delta() const { + return dest_ - source_; + } + + uintptr_t Source() const { + return source_; + } + + uintptr_t Dest() const { + return dest_; + } + + uintptr_t Length() const { + return length_; + } + + private: + const uintptr_t source_; + const uintptr_t dest_; + const uintptr_t length_; +}; + +class FixupVisitor : public ValueObject { + public: + FixupVisitor(const RelocationRange& boot_image, + const RelocationRange& boot_oat, + const RelocationRange& app_image, + const RelocationRange& app_oat) + : boot_image_(boot_image), + boot_oat_(boot_oat), + app_image_(app_image), + app_oat_(app_oat) {} + + // Return the relocated address of a heap object. + template <typename T> + ALWAYS_INLINE T* ForwardObject(T* src) const { + const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src); + if (boot_image_.ContainsSource(uint_src)) { + return reinterpret_cast<T*>(boot_image_.ToDest(uint_src)); + } + if (app_image_.ContainsSource(uint_src)) { + return reinterpret_cast<T*>(app_image_.ToDest(uint_src)); + } + return src; + } + + // Return the relocated address of a code pointer (contained by an oat file). + ALWAYS_INLINE const void* ForwardCode(const void* src) const { + const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src); + if (boot_oat_.ContainsSource(uint_src)) { + return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src)); + } + if (app_oat_.ContainsSource(uint_src)) { + return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src)); + } + return src; + } + + protected: + // Source section. + const RelocationRange boot_image_; + const RelocationRange boot_oat_; + const RelocationRange app_image_; + const RelocationRange app_oat_; +}; + +std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) { + return os << "(" << reinterpret_cast<const void*>(reloc.Source()) << "-" + << reinterpret_cast<const void*>(reloc.Source() + reloc.Length()) << ")->(" + << reinterpret_cast<const void*>(reloc.Dest()) << "-" + << reinterpret_cast<const void*>(reloc.Dest() + reloc.Length()) << ")"; +} + +// Adapt for mirror::Class::FixupNativePointers. +class FixupObjectAdapter : public FixupVisitor { + public: + template<typename... Args> + explicit FixupObjectAdapter(Args... args) : FixupVisitor(args...) {} + + template <typename T> + T* operator()(T* obj) const { + return ForwardObject(obj); + } +}; + +class FixupClassVisitor : public FixupVisitor { + public: + template<typename... Args> + explicit FixupClassVisitor(Args... args) : FixupVisitor(args...) {} + + // The image space is contained so the GC doesn't need to know about it. Avoid requiring mutator + // lock to prevent possible pauses. + ALWAYS_INLINE void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS { + mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>(); + DCHECK(klass != nullptr) << "Null class in image"; + // No AsClass since our fields aren't quite fixed up yet. + mirror::Class* new_klass = down_cast<mirror::Class*>(ForwardObject(klass)); + // Keep clean if possible. + if (klass != new_klass) { + obj->SetClass<kVerifyNone>(new_klass); + } + } +}; + +class FixupRootVisitor : public FixupVisitor { + public: + template<typename... Args> + explicit FixupRootVisitor(Args... args) : FixupVisitor(args...) {} + + ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const + SHARED_REQUIRES(Locks::mutator_lock_) { + if (!root->IsNull()) { + VisitRoot(root); + } + } + + ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const + SHARED_REQUIRES(Locks::mutator_lock_) { + mirror::Object* ref = root->AsMirrorPtr(); + mirror::Object* new_ref = ForwardObject(ref); + if (ref != new_ref) { + root->Assign(new_ref); + } + } +}; + +class FixupObjectVisitor : public FixupVisitor { + public: + template<typename... Args> + explicit FixupObjectVisitor(Args... args) : FixupVisitor(args...) {} + + // Fix up separately since we also need to fix up method entrypoints. + ALWAYS_INLINE void VisitRootIfNonNull( + mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {} + + ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) + const {} + + ALWAYS_INLINE void operator()(mirror::Object* obj, + MemberOffset offset, + bool is_static ATTRIBUTE_UNUSED) const + NO_THREAD_SAFETY_ANALYSIS { + // There could be overlap between ranges, we must avoid visiting the same reference twice. + // Avoid the class field since we already fixed it up in FixupClassVisitor. + if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) { + // Space is not yet added to the heap, don't do a read barrier. + mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>( + offset); + // Use SetFieldObjectWithoutWriteBarrier to avoid card marking since we are writing to the + // image. + obj->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(offset, ForwardObject(ref)); + } + } + + // java.lang.ref.Reference visitor. + void operator()(mirror::Class* klass ATTRIBUTE_UNUSED, mirror::Reference* ref) const + SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { + mirror::Object* obj = ref->GetReferent<kWithoutReadBarrier>(); + ref->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>( + mirror::Reference::ReferentOffset(), + ForwardObject(obj)); + } + + ALWAYS_INLINE void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS { + obj->VisitReferences</*visit native roots*/false, kVerifyNone, kWithoutReadBarrier>( + *this, + *this); + // We want to use our own class loader and not the one in the image. + if (obj->IsClass<kVerifyNone, kWithoutReadBarrier>()) { + mirror::Class* klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>(); + FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_); + klass->FixupNativePointers(klass, sizeof(void*), visitor); + // Deal with the arrays. + mirror::PointerArray* vtable = klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(); + if (vtable != nullptr) { + vtable->Fixup(vtable, sizeof(void*), visitor); + } + mirror::IfTable* iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>(); + if (iftable != nullptr) { + for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { + if (iftable->GetMethodArrayCount(i) > 0) { + mirror::PointerArray* methods = + iftable->GetMethodArray<kVerifyNone, kWithoutReadBarrier>(i); + DCHECK(methods != nullptr); + methods->Fixup(methods, sizeof(void*), visitor); + } + } + } + } + } +}; + +class ForwardObjectAdapter { + public: + ALWAYS_INLINE ForwardObjectAdapter(const FixupVisitor* visitor) : visitor_(visitor) {} + + template <typename T> + ALWAYS_INLINE T* operator()(T* src) const { + return visitor_->ForwardObject(src); + } + + private: + const FixupVisitor* const visitor_; +}; + +class ForwardCodeAdapter { + public: + ALWAYS_INLINE ForwardCodeAdapter(const FixupVisitor* visitor) : visitor_(visitor) {} + + template <typename T> + ALWAYS_INLINE T* operator()(T* src) const { + return visitor_->ForwardCode(src); + } + + private: + const FixupVisitor* const visitor_; +}; + +class FixupArtMethodVisitor : public FixupVisitor, public ArtMethodVisitor { + public: + template<typename... Args> + explicit FixupArtMethodVisitor(bool fixup_heap_objects, Args... args) + : FixupVisitor(args...), + fixup_heap_objects_(fixup_heap_objects) {} + + virtual void Visit(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS { + if (fixup_heap_objects_) { + method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this)); + } + method->UpdateEntrypoints(ForwardCodeAdapter(this)); + } + + private: + const bool fixup_heap_objects_; +}; + +class FixupArtFieldVisitor : public FixupVisitor, public ArtFieldVisitor { + public: + template<typename... Args> + explicit FixupArtFieldVisitor(Args... args) : FixupVisitor(args...) {} + + virtual void Visit(ArtField* field) NO_THREAD_SAFETY_ANALYSIS { + field->UpdateObjects(ForwardObjectAdapter(this)); + } +}; + +// Relocate an image space mapped at target_base which possibly used to be at a different base +// address. Only needs a single image space, not one for both source and destination. +// In place means modifying a single ImageSpace in place rather than relocating from one ImageSpace +// to another. +static bool RelocateInPlace(ImageHeader& image_header, + uint8_t* target_base, + accounting::ContinuousSpaceBitmap* bitmap, + const OatFile* app_oat_file, + std::string* error_msg) { + DCHECK(error_msg != nullptr); + if (!image_header.IsPic()) { + if (image_header.GetImageBegin() == target_base) { + return true; + } + *error_msg = StringPrintf("Cannot relocate non-pic image for oat file %s", + (app_oat_file != nullptr) ? app_oat_file->GetLocation().c_str() : ""); + return false; + } + // Set up sections. + uint32_t boot_image_begin = 0; + uint32_t boot_image_end = 0; + uint32_t boot_oat_begin = 0; + uint32_t boot_oat_end = 0; + gc::Heap* const heap = Runtime::Current()->GetHeap(); + heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end); + CHECK_NE(boot_image_begin, boot_image_end) + << "Can not relocate app image without boot image space"; + CHECK_NE(boot_oat_begin, boot_oat_end) << "Can not relocate app image without boot oat file"; + const uint32_t boot_image_size = boot_image_end - boot_image_begin; + const uint32_t boot_oat_size = boot_oat_end - boot_oat_begin; + const uint32_t image_header_boot_image_size = image_header.GetBootImageSize(); + const uint32_t image_header_boot_oat_size = image_header.GetBootOatSize(); + if (boot_image_size != image_header_boot_image_size) { + *error_msg = StringPrintf("Boot image size %" PRIu64 " does not match expected size %" + PRIu64, + static_cast<uint64_t>(boot_image_size), + static_cast<uint64_t>(image_header_boot_image_size)); + return false; + } + if (boot_oat_size != image_header_boot_oat_size) { + *error_msg = StringPrintf("Boot oat size %" PRIu64 " does not match expected size %" + PRIu64, + static_cast<uint64_t>(boot_oat_size), + static_cast<uint64_t>(image_header_boot_oat_size)); + return false; + } + TimingLogger logger(__FUNCTION__, true, false); + RelocationRange boot_image(image_header.GetBootImageBegin(), + boot_image_begin, + boot_image_size); + RelocationRange boot_oat(image_header.GetBootOatBegin(), + boot_oat_begin, + boot_oat_size); + RelocationRange app_image(reinterpret_cast<uintptr_t>(image_header.GetImageBegin()), + reinterpret_cast<uintptr_t>(target_base), + image_header.GetImageSize()); + // Use the oat data section since this is where the OatFile::Begin is. + RelocationRange app_oat(reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin()), + // Not necessarily in low 4GB. + reinterpret_cast<uintptr_t>(app_oat_file->Begin()), + image_header.GetOatDataEnd() - image_header.GetOatDataBegin()); + VLOG(image) << "App image " << app_image; + VLOG(image) << "App oat " << app_oat; + VLOG(image) << "Boot image " << boot_image; + VLOG(image) << "Boot oat " << boot_oat; + // True if we need to fixup any heap pointers, otherwise only code pointers. + const bool fixup_image = boot_image.Delta() != 0 || app_image.Delta() != 0; + const bool fixup_code = boot_oat.Delta() != 0 || app_oat.Delta() != 0; + if (!fixup_image && !fixup_code) { + // Nothing to fix up. + return true; + } + // Need to update the image to be at the target base. + const ImageSection& objects_section = image_header.GetImageSection(ImageHeader::kSectionObjects); + uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset()); + uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End()); + // Two pass approach, fix up all classes first, then fix up non class-objects. + FixupObjectVisitor fixup_object_visitor(boot_image, boot_oat, app_image, app_oat); + if (fixup_image) { + TimingLogger::ScopedTiming timing("Fixup classes", &logger); + // Fixup class only touches app image classes, don't need the mutator lock since the space is + // not yet visible to the GC. + FixupClassVisitor fixup_class_visitor(boot_image, boot_oat, app_image, app_oat); + bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_class_visitor); + // Fixup objects may read fields in the boot image, use the mutator lock here for sanity. Though + // its probably not required. + ScopedObjectAccess soa(Thread::Current()); + timing.NewTiming("Fixup objects"); + bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor); + FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat); + // Fixup image roots. + CHECK(app_image.ContainsSource(reinterpret_cast<uintptr_t>( + image_header.GetImageRoots<kWithoutReadBarrier>()))); + image_header.RelocateImageObjects(app_image.Delta()); + CHECK_EQ(image_header.GetImageBegin(), target_base); + // Fix up dex cache DexFile pointers. + auto* dex_caches = image_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kDexCaches)-> + AsObjectArray<mirror::DexCache>(); + for (int32_t i = 0, count = dex_caches->GetLength(); i < count; ++i) { + mirror::DexCache* dex_cache = dex_caches->Get(i); + // Fix up dex cache pointers. + GcRoot<mirror::String>* strings = dex_cache->GetStrings(); + if (strings != nullptr) { + GcRoot<mirror::String>* new_strings = fixup_adapter.ForwardObject(strings); + if (strings != new_strings) { + dex_cache->SetFieldPtr64<false>(mirror::DexCache::StringsOffset(), new_strings); + } + dex_cache->FixupStrings(new_strings, fixup_adapter); + } + GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes(); + if (types != nullptr) { + GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types); + if (types != new_types) { + dex_cache->SetFieldPtr64<false>(mirror::DexCache::ResolvedTypesOffset(), new_types); + } + dex_cache->FixupResolvedTypes(new_types, fixup_adapter); + } + ArtMethod** methods = dex_cache->GetResolvedMethods(); + if (methods != nullptr) { + ArtMethod** new_methods = fixup_adapter.ForwardObject(methods); + if (methods != new_methods) { + dex_cache->SetFieldPtr64<false>(mirror::DexCache::ResolvedMethodsOffset(), new_methods); + } + for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) { + ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, sizeof(void*)); + ArtMethod* copy = fixup_adapter.ForwardObject(orig); + if (orig != copy) { + mirror::DexCache::SetElementPtrSize(new_methods, j, copy, sizeof(void*)); + } + } + } + ArtField** fields = dex_cache->GetResolvedFields(); + if (fields != nullptr) { + ArtField** new_fields = fixup_adapter.ForwardObject(fields); + if (fields != new_fields) { + dex_cache->SetFieldPtr64<false>(mirror::DexCache::ResolvedFieldsOffset(), new_fields); + } + for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) { + ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, sizeof(void*)); + ArtField* copy = fixup_adapter.ForwardObject(orig); + if (orig != copy) { + mirror::DexCache::SetElementPtrSize(new_fields, j, copy, sizeof(void*)); + } + } + } + } + } + { + // Only touches objects in the app image, no need for mutator lock. + TimingLogger::ScopedTiming timing("Fixup methods", &logger); + FixupArtMethodVisitor method_visitor(fixup_image, boot_image, boot_oat, app_image, app_oat); + image_header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods( + &method_visitor, + target_base, + sizeof(void*)); + } + if (fixup_image) { + { + // Only touches objects in the app image, no need for mutator lock. + TimingLogger::ScopedTiming timing("Fixup fields", &logger); + FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat); + image_header.GetImageSection(ImageHeader::kSectionArtFields).VisitPackedArtFields( + &field_visitor, + target_base); + } + // In the app image case, the image methods are actually in the boot image. + image_header.RelocateImageMethods(boot_image.Delta()); + const auto& class_table_section = image_header.GetImageSection(ImageHeader::kSectionClassTable); + if (class_table_section.Size() > 0u) { + // Note that we require that ReadFromMemory does not make an internal copy of the elements. + // This also relies on visit roots not doing any verification which could fail after we update + // the roots to be the image addresses. + ScopedObjectAccess soa(Thread::Current()); + WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ClassTable temp_table; + temp_table.ReadFromMemory(target_base + class_table_section.Offset()); + FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat); + temp_table.VisitRoots(root_visitor); + } + } + if (VLOG_IS_ON(image)) { + logger.Dump(LOG(INFO)); + } + return true; +} + +ImageSpace* ImageSpace::Init(const char* image_filename, + const char* image_location, + bool validate_oat_file, + const OatFile* oat_file, + std::string* error_msg) { CHECK(image_filename != nullptr); CHECK(image_location != nullptr); - uint64_t start_time = 0; - if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { - start_time = NanoTime(); - LOG(INFO) << "ImageSpace::Init entering image_filename=" << image_filename; - } + TimingLogger logger(__FUNCTION__, true, false); + VLOG(image) << "ImageSpace::Init entering image_filename=" << image_filename; - std::unique_ptr<File> file(OS::OpenFileForReading(image_filename)); - if (file.get() == nullptr) { - *error_msg = StringPrintf("Failed to open '%s'", image_filename); - return nullptr; + std::unique_ptr<File> file; + { + TimingLogger::ScopedTiming timing("OpenImageFile", &logger); + file.reset(OS::OpenFileForReading(image_filename)); + if (file == nullptr) { + *error_msg = StringPrintf("Failed to open '%s'", image_filename); + return nullptr; + } } - ImageHeader image_header; - bool success = file->ReadFully(&image_header, sizeof(image_header)); - if (!success || !image_header.IsValid()) { - *error_msg = StringPrintf("Invalid image header in '%s'", image_filename); - return nullptr; + ImageHeader temp_image_header; + ImageHeader* image_header = &temp_image_header; + { + TimingLogger::ScopedTiming timing("ReadImageHeader", &logger); + bool success = file->ReadFully(image_header, sizeof(*image_header)); + if (!success || !image_header->IsValid()) { + *error_msg = StringPrintf("Invalid image header in '%s'", image_filename); + return nullptr; + } } // Check that the file is larger or equal to the header size + data size. const uint64_t image_file_size = static_cast<uint64_t>(file->GetLength()); - if (image_file_size < sizeof(ImageHeader) + image_header.GetDataSize()) { + if (image_file_size < sizeof(ImageHeader) + image_header->GetDataSize()) { *error_msg = StringPrintf("Image file truncated: %" PRIu64 " vs. %" PRIu64 ".", image_file_size, - image_header.GetDataSize()); + sizeof(ImageHeader) + image_header->GetDataSize()); return nullptr; } @@ -704,17 +1171,17 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat LOG(INFO) << "Dumping image sections"; for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { const auto section_idx = static_cast<ImageHeader::ImageSections>(i); - auto& section = image_header.GetImageSection(section_idx); + auto& section = image_header->GetImageSection(section_idx); LOG(INFO) << section_idx << " start=" - << reinterpret_cast<void*>(image_header.GetImageBegin() + section.Offset()) << " " - << section; + << reinterpret_cast<void*>(image_header->GetImageBegin() + section.Offset()) << " " + << section; } } - const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap); + const auto& bitmap_section = image_header->GetImageSection(ImageHeader::kSectionImageBitmap); // The location we want to map from is the first aligned page after the end of the stored // (possibly compressed) data. - const size_t image_bitmap_offset = RoundUp(sizeof(image_header) + image_header.GetDataSize(), + const size_t image_bitmap_offset = RoundUp(sizeof(ImageHeader) + image_header->GetDataSize(), kPageSize); const size_t end_of_bitmap = image_bitmap_offset + bitmap_section.Size(); if (end_of_bitmap != image_file_size) { @@ -724,67 +1191,84 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat return nullptr; } + // The preferred address to map the image, null specifies any address. If we manage to map the + // image at the image begin, the amount of fixup work required is minimized. + std::vector<uint8_t*> addresses(1, image_header->GetImageBegin()); + if (image_header->IsPic()) { + // Can also map at a random low_4gb address since we can relocate in-place. + addresses.push_back(nullptr); + } + // Note: The image header is part of the image due to mmap page alignment required of offset. std::unique_ptr<MemMap> map; - if (image_header.GetStorageMode() == ImageHeader::kStorageModeUncompressed) { - map.reset(MemMap::MapFileAtAddress(image_header.GetImageBegin(), - image_header.GetImageSize(), - PROT_READ | PROT_WRITE, - MAP_PRIVATE, - file->Fd(), - 0, - /*low_4gb*/false, - /*reuse*/false, - image_filename, - error_msg)); - } else { - // Reserve output and decompress into it. - map.reset(MemMap::MapAnonymous(image_location, - image_header.GetImageBegin(), - image_header.GetImageSize(), - PROT_READ | PROT_WRITE, - /*low_4gb*/false, - /*reuse*/false, - error_msg)); - if (map != nullptr) { - const size_t stored_size = image_header.GetDataSize(); - const size_t write_offset = sizeof(image_header); // Skip the header. - std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size, - PROT_READ, - MAP_PRIVATE, - file->Fd(), - /*offset*/0, - /*low_4gb*/false, - image_filename, - error_msg)); - if (temp_map == nullptr) { - DCHECK(!error_msg->empty()); - return nullptr; - } - memcpy(map->Begin(), &image_header, sizeof(image_header)); - const uint64_t start = NanoTime(); - const size_t decompressed_size = LZ4_decompress_safe( - reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader), - reinterpret_cast<char*>(map->Begin()) + write_offset, - stored_size, - map->Size()); - // TODO: VLOG(image) - VLOG(class_linker) << "Decompressing image took " << PrettyDuration(NanoTime() - start); - if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) { - *error_msg = StringPrintf("Decompressed size does not match expected image size %zu vs %zu", - decompressed_size + sizeof(ImageHeader), - image_header.GetImageSize()); - return nullptr; + std::string temp_error_msg; + for (uint8_t* address : addresses) { + TimingLogger::ScopedTiming timing("MapImageFile", &logger); + // Only care about the error message for the last address in addresses. We want to avoid the + // overhead of printing the process maps if we can relocate. + std::string* out_error_msg = (address == addresses.back()) ? &temp_error_msg : nullptr; + if (image_header->GetStorageMode() == ImageHeader::kStorageModeUncompressed) { + map.reset(MemMap::MapFileAtAddress(address, + image_header->GetImageSize(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + file->Fd(), + 0, + /*low_4gb*/true, + /*reuse*/false, + image_filename, + /*out*/out_error_msg)); + } else { + // Reserve output and decompress into it. + map.reset(MemMap::MapAnonymous(image_location, + address, + image_header->GetImageSize(), + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + /*reuse*/false, + out_error_msg)); + if (map != nullptr) { + const size_t stored_size = image_header->GetDataSize(); + const size_t write_offset = sizeof(ImageHeader); // Skip the header. + std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size, + PROT_READ, + MAP_PRIVATE, + file->Fd(), + /*offset*/0, + /*low_4gb*/false, + image_filename, + out_error_msg)); + if (temp_map == nullptr) { + DCHECK(!out_error_msg->empty()); + return nullptr; + } + memcpy(map->Begin(), image_header, sizeof(ImageHeader)); + const uint64_t start = NanoTime(); + const size_t decompressed_size = LZ4_decompress_safe( + reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader), + reinterpret_cast<char*>(map->Begin()) + write_offset, + stored_size, + map->Size()); + VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start); + if (decompressed_size + sizeof(ImageHeader) != image_header->GetImageSize()) { + *error_msg = StringPrintf("Decompressed size does not match expected image size %zu vs %zu", + decompressed_size + sizeof(ImageHeader), + image_header->GetImageSize()); + return nullptr; + } } } + if (map != nullptr) { + break; + } } if (map == nullptr) { - DCHECK(!error_msg->empty()); + DCHECK(!temp_error_msg.empty()); + *error_msg = temp_error_msg; return nullptr; } - CHECK_EQ(image_header.GetImageBegin(), map->Begin()); - DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader))); + DCHECK_EQ(0, memcmp(image_header, map->Begin(), sizeof(ImageHeader))); std::unique_ptr<MemMap> image_bitmap_map(MemMap::MapFileAtAddress(nullptr, bitmap_section.Size(), @@ -799,25 +1283,42 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str()); return nullptr; } - uint32_t bitmap_index = bitmap_index_.FetchAndAddSequentiallyConsistent(1); - std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_filename, + // Loaded the map, use the image header from the file now in case we patch it with + // RelocateInPlace. + image_header = reinterpret_cast<ImageHeader*>(map->Begin()); + const uint32_t bitmap_index = bitmap_index_.FetchAndAddSequentiallyConsistent(1); + std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", + image_filename, bitmap_index)); // Bitmap only needs to cover until the end of the mirror objects section. - const ImageSection& image_objects = image_header.GetImageSection(ImageHeader::kSectionObjects); - std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap( + const ImageSection& image_objects = image_header->GetImageSection(ImageHeader::kSectionObjects); + // We only want the mirror object, not the ArtFields and ArtMethods. + uint8_t* const image_end = map->Begin() + image_objects.End(); + std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap; + { + TimingLogger::ScopedTiming timing("CreateImageBitmap", &logger); + bitmap.reset( accounting::ContinuousSpaceBitmap::CreateFromMemMap( bitmap_name, image_bitmap_map.release(), reinterpret_cast<uint8_t*>(map->Begin()), image_objects.End())); - if (bitmap == nullptr) { - *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str()); - return nullptr; + if (bitmap == nullptr) { + *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str()); + return nullptr; + } + } + { + TimingLogger::ScopedTiming timing("RelocateImage", &logger); + if (!RelocateInPlace(*image_header, + map->Begin(), + bitmap.get(), + oat_file, + error_msg)) { + return nullptr; + } } - // We only want the mirror object, not the ArtFields and ArtMethods. - uint8_t* const image_end = - map->Begin() + image_header.GetImageSection(ImageHeader::kSectionObjects).End(); std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location, map.release(), @@ -829,38 +1330,61 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat // and ArtField::java_lang_reflect_ArtField_, which are used from // Object::SizeOf() which VerifyImageAllocations() calls, are not // set yet at this point. - - space->oat_file_.reset(space->OpenOatFile(image_filename, error_msg)); - if (space->oat_file_.get() == nullptr) { - DCHECK(!error_msg->empty()); - return nullptr; + if (oat_file == nullptr) { + TimingLogger::ScopedTiming timing("OpenOatFile", &logger); + space->oat_file_.reset(space->OpenOatFile(image_filename, error_msg)); + if (space->oat_file_ == nullptr) { + DCHECK(!error_msg->empty()); + return nullptr; + } + space->oat_file_non_owned_ = space->oat_file_.get(); + } else { + space->oat_file_non_owned_ = oat_file; } - space->oat_file_non_owned_ = space->oat_file_.get(); - if (validate_oat_file && !space->ValidateOatFile(error_msg)) { - DCHECK(!error_msg->empty()); - return nullptr; + if (validate_oat_file) { + TimingLogger::ScopedTiming timing("ValidateOatFile", &logger); + if (!space->ValidateOatFile(error_msg)) { + DCHECK(!error_msg->empty()); + return nullptr; + } } Runtime* runtime = Runtime::Current(); - runtime->SetInstructionSet(space->oat_file_->GetOatHeader().GetInstructionSet()); - if (!runtime->HasResolutionMethod()) { - runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod)); - runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod)); + // If oat_file is null, then it is the boot image space. Use oat_file_non_owned_ from the space + // to set the runtime methods. + CHECK_EQ(oat_file != nullptr, image_header->IsAppImage()); + if (image_header->IsAppImage()) { + CHECK_EQ(runtime->GetResolutionMethod(), + image_header->GetImageMethod(ImageHeader::kResolutionMethod)); + CHECK_EQ(runtime->GetImtConflictMethod(), + image_header->GetImageMethod(ImageHeader::kImtConflictMethod)); + CHECK_EQ(runtime->GetImtUnimplementedMethod(), + image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kSaveAll), + image_header->GetImageMethod(ImageHeader::kCalleeSaveMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kRefsOnly), + image_header->GetImageMethod(ImageHeader::kRefsOnlySaveMethod)); + CHECK_EQ(runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs), + image_header->GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod)); + } else if (!runtime->HasResolutionMethod()) { + runtime->SetInstructionSet(space->oat_file_non_owned_->GetOatHeader().GetInstructionSet()); + runtime->SetResolutionMethod(image_header->GetImageMethod(ImageHeader::kResolutionMethod)); + runtime->SetImtConflictMethod(image_header->GetImageMethod(ImageHeader::kImtConflictMethod)); runtime->SetImtUnimplementedMethod( - image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod)); + image_header->GetImageMethod(ImageHeader::kImtUnimplementedMethod)); runtime->SetCalleeSaveMethod( - image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll); + image_header->GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll); runtime->SetCalleeSaveMethod( - image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly); + image_header->GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly); runtime->SetCalleeSaveMethod( - image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs); + image_header->GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs); } - if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { - LOG(INFO) << "ImageSpace::Init exiting (" << PrettyDuration(NanoTime() - start_time) - << ") " << *space.get(); + VLOG(image) << "ImageSpace::Init exiting " << *space.get(); + if (VLOG_IS_ON(image)) { + logger.Dump(LOG(INFO)); } return space.release(); } @@ -1002,6 +1526,16 @@ void ImageSpace::CreateMultiImageLocations(const std::string& input_image_file_n } } +ImageSpace* ImageSpace::CreateFromAppImage(const char* image, + const OatFile* oat_file, + std::string* error_msg) { + return gc::space::ImageSpace::Init(image, + image, + /*validate_oat_file*/false, + oat_file, + /*out*/error_msg); +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 9c8e8b2014..f2f416377e 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -35,7 +35,7 @@ class ImageSpace : public MemMapSpace { return kSpaceTypeImageSpace; } - // Create a Space from an image file for a specified instruction + // Create a boot image space from an image file for a specified instruction // set. Cannot be used for future allocation or collected. // // Create also opens the OatFile associated with the image file so @@ -43,10 +43,16 @@ class ImageSpace : public MemMapSpace { // creation of the alloc space. The ReleaseOatFile will later be // used to transfer ownership of the OatFile to the ClassLinker when // it is initialized. - static ImageSpace* Create(const char* image, - InstructionSet image_isa, - bool secondary_image, - std::string* error_msg) + static ImageSpace* CreateBootImage(const char* image, + InstructionSet image_isa, + bool secondary_image, + std::string* error_msg) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Try to open an existing app image space. + static ImageSpace* CreateFromAppImage(const char* image, + const OatFile* oat_file, + std::string* error_msg) SHARED_REQUIRES(Locks::mutator_lock_); // Reads the image header from the specified image location for the @@ -144,15 +150,17 @@ class ImageSpace : public MemMapSpace { } protected: - // Tries to initialize an ImageSpace from the given image path, - // returning null on error. + // Tries to initialize an ImageSpace from the given image path, returning null on error. // - // If validate_oat_file is false (for /system), do not verify that - // image's OatFile is up-to-date relative to its DexFile - // inputs. Otherwise (for /data), validate the inputs and generate - // the OatFile in /data/dalvik-cache if necessary. - static ImageSpace* Init(const char* image_filename, const char* image_location, - bool validate_oat_file, std::string* error_msg) + // If validate_oat_file is false (for /system), do not verify that image's OatFile is up-to-date + // relative to its DexFile inputs. Otherwise (for /data), validate the inputs and generate the + // OatFile in /data/dalvik-cache if necessary. If the oat_file is null, it uses the oat file from + // the image. + static ImageSpace* Init(const char* image_filename, + const char* image_location, + bool validate_oat_file, + const OatFile* oat_file, + std::string* error_msg) SHARED_REQUIRES(Locks::mutator_lock_); OatFile* OpenOatFile(const char* image, std::string* error_msg) const diff --git a/runtime/gc/space/large_object_space_test.cc b/runtime/gc/space/large_object_space_test.cc index 05b484af07..ad38724e7d 100644 --- a/runtime/gc/space/large_object_space_test.cc +++ b/runtime/gc/space/large_object_space_test.cc @@ -22,7 +22,7 @@ namespace art { namespace gc { namespace space { -class LargeObjectSpaceTest : public SpaceTest { +class LargeObjectSpaceTest : public SpaceTest<CommonRuntimeTest> { public: void LargeObjectTest(); diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 49126d2c02..fd4d0a1a47 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -247,7 +247,10 @@ size_t RosAllocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p size_t RosAllocSpace::Trim() { VLOG(heap) << "RosAllocSpace::Trim() "; { - MutexLock mu(Thread::Current(), lock_); + Thread* const self = Thread::Current(); + // SOA required for Rosalloc::Trim() -> ArtRosAllocMoreCore() -> Heap::GetRosAllocSpace. + ScopedObjectAccess soa(self); + MutexLock mu(self, lock_); // Trim to release memory at the end of the space. rosalloc_->Trim(); } @@ -373,7 +376,8 @@ void RosAllocSpace::Clear() { namespace allocator { // Callback from rosalloc when it needs to increase the footprint. -void* ArtRosAllocMoreCore(allocator::RosAlloc* rosalloc, intptr_t increment) { +void* ArtRosAllocMoreCore(allocator::RosAlloc* rosalloc, intptr_t increment) + SHARED_REQUIRES(Locks::mutator_lock_) { Heap* heap = Runtime::Current()->GetHeap(); art::gc::space::RosAllocSpace* rosalloc_space = heap->GetRosAllocSpace(rosalloc); DCHECK(rosalloc_space != nullptr); diff --git a/runtime/gc/space/rosalloc_space_base_test.cc b/runtime/gc/space/rosalloc_space_base_test.cc deleted file mode 100644 index 0c5be03180..0000000000 --- a/runtime/gc/space/rosalloc_space_base_test.cc +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2011 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 "space_test.h" - -namespace art { -namespace gc { -namespace space { - -MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, uint8_t* requested_begin) { - return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, - Runtime::Current()->GetHeap()->IsLowMemoryMode(), false); -} - -TEST_SPACE_CREATE_FN_BASE(RosAllocSpace, CreateRosAllocSpace) - - -} // namespace space -} // namespace gc -} // namespace art diff --git a/runtime/gc/space/space_create_test.cc b/runtime/gc/space/space_create_test.cc new file mode 100644 index 0000000000..170f927e9b --- /dev/null +++ b/runtime/gc/space/space_create_test.cc @@ -0,0 +1,364 @@ +/* + * 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 "space_test.h" + +#include "dlmalloc_space.h" +#include "rosalloc_space.h" +#include "scoped_thread_state_change.h" + +namespace art { +namespace gc { +namespace space { + +enum MallocSpaceType { + kMallocSpaceDlMalloc, + kMallocSpaceRosAlloc, +}; + +class SpaceCreateTest : public SpaceTest<CommonRuntimeTestWithParam<MallocSpaceType>> { + public: + MallocSpace* CreateSpace(const std::string& name, + size_t initial_size, + size_t growth_limit, + size_t capacity, + uint8_t* requested_begin) { + const MallocSpaceType type = GetParam(); + if (type == kMallocSpaceDlMalloc) { + return DlMallocSpace::Create(name, + initial_size, + growth_limit, + capacity, + requested_begin, + false); + } + DCHECK_EQ(static_cast<uint32_t>(type), static_cast<uint32_t>(kMallocSpaceRosAlloc)); + return RosAllocSpace::Create(name, + initial_size, + growth_limit, + capacity, + requested_begin, + Runtime::Current()->GetHeap()->IsLowMemoryMode(), + false); + } +}; + +TEST_P(SpaceCreateTest, InitTestBody) { + // This will lead to error messages in the log. + ScopedLogSeverity sls(LogSeverity::FATAL); + + { + // Init < max == growth + std::unique_ptr<Space> space(CreateSpace("test", 16 * MB, 32 * MB, 32 * MB, nullptr)); + EXPECT_TRUE(space != nullptr); + // Init == max == growth + space.reset(CreateSpace("test", 16 * MB, 16 * MB, 16 * MB, nullptr)); + EXPECT_TRUE(space != nullptr); + // Init > max == growth + space.reset(CreateSpace("test", 32 * MB, 16 * MB, 16 * MB, nullptr)); + EXPECT_TRUE(space == nullptr); + // Growth == init < max + space.reset(CreateSpace("test", 16 * MB, 16 * MB, 32 * MB, nullptr)); + EXPECT_TRUE(space != nullptr); + // Growth < init < max + space.reset(CreateSpace("test", 16 * MB, 8 * MB, 32 * MB, nullptr)); + EXPECT_TRUE(space == nullptr); + // Init < growth < max + space.reset(CreateSpace("test", 8 * MB, 16 * MB, 32 * MB, nullptr)); + EXPECT_TRUE(space != nullptr); + // Init < max < growth + space.reset(CreateSpace("test", 8 * MB, 32 * MB, 16 * MB, nullptr)); + EXPECT_TRUE(space == nullptr); + } +} + +// TODO: This test is not very good, we should improve it. +// The test should do more allocations before the creation of the ZygoteSpace, and then do +// allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that +// the GC works with the ZygoteSpace. +TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) { + size_t dummy; + MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB, nullptr)); + ASSERT_TRUE(space != nullptr); + + // Make space findable to the heap, will also delete space when runtime is cleaned up + AddSpace(space); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Succeeds, fits without adjusting the footprint limit. + size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated; + StackHandleScope<3> hs(soa.Self()); + MutableHandle<mirror::Object> ptr1(hs.NewHandle(Alloc(space, + self, + 1 * MB, + &ptr1_bytes_allocated, + &ptr1_usable_size, + &ptr1_bytes_tl_bulk_allocated))); + EXPECT_TRUE(ptr1.Get() != nullptr); + EXPECT_LE(1U * MB, ptr1_bytes_allocated); + EXPECT_LE(1U * MB, ptr1_usable_size); + EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); + EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated); + + // Fails, requires a higher footprint limit. + mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); + EXPECT_TRUE(ptr2 == nullptr); + + // Succeeds, adjusts the footprint. + size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated; + MutableHandle<mirror::Object> ptr3(hs.NewHandle(AllocWithGrowth(space, + self, + 8 * MB, + &ptr3_bytes_allocated, + &ptr3_usable_size, + &ptr3_bytes_tl_bulk_allocated))); + EXPECT_TRUE(ptr3.Get() != nullptr); + EXPECT_LE(8U * MB, ptr3_bytes_allocated); + EXPECT_LE(8U * MB, ptr3_usable_size); + EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); + EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated); + + // Fails, requires a higher footprint limit. + mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy, nullptr, &dummy); + EXPECT_TRUE(ptr4 == nullptr); + + // Also fails, requires a higher allowed footprint. + mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy, nullptr, &dummy); + EXPECT_TRUE(ptr5 == nullptr); + + // Release some memory. + size_t free3 = space->AllocationSize(ptr3.Get(), nullptr); + EXPECT_EQ(free3, ptr3_bytes_allocated); + EXPECT_EQ(free3, space->Free(self, ptr3.Assign(nullptr))); + EXPECT_LE(8U * MB, free3); + + // Succeeds, now that memory has been freed. + size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated; + Handle<mirror::Object> ptr6(hs.NewHandle(AllocWithGrowth(space, + self, + 9 * MB, + &ptr6_bytes_allocated, + &ptr6_usable_size, + &ptr6_bytes_tl_bulk_allocated))); + EXPECT_TRUE(ptr6.Get() != nullptr); + EXPECT_LE(9U * MB, ptr6_bytes_allocated); + EXPECT_LE(9U * MB, ptr6_usable_size); + EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated); + EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated); + + // Final clean up. + size_t free1 = space->AllocationSize(ptr1.Get(), nullptr); + space->Free(self, ptr1.Assign(nullptr)); + EXPECT_LE(1U * MB, free1); + + // Make sure that the zygote space isn't directly at the start of the space. + EXPECT_TRUE(space->Alloc(self, 1U * MB, &dummy, nullptr, &dummy) != nullptr); + + gc::Heap* heap = Runtime::Current()->GetHeap(); + space::Space* old_space = space; + { + ScopedThreadSuspension sts(self, kSuspended); + ScopedSuspendAll ssa("Add image space"); + heap->RemoveSpace(old_space); + } + heap->RevokeAllThreadLocalBuffers(); + space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space", + heap->IsLowMemoryMode(), + &space); + delete old_space; + // Add the zygote space. + AddSpace(zygote_space, false); + + // Make space findable to the heap, will also delete space when runtime is cleaned up + AddSpace(space, false); + + // Succeeds, fits without adjusting the footprint limit. + ptr1.Assign(Alloc(space, + self, + 1 * MB, + &ptr1_bytes_allocated, + &ptr1_usable_size, + &ptr1_bytes_tl_bulk_allocated)); + EXPECT_TRUE(ptr1.Get() != nullptr); + EXPECT_LE(1U * MB, ptr1_bytes_allocated); + EXPECT_LE(1U * MB, ptr1_usable_size); + EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); + EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated); + + // Fails, requires a higher footprint limit. + ptr2 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); + EXPECT_TRUE(ptr2 == nullptr); + + // Succeeds, adjusts the footprint. + ptr3.Assign(AllocWithGrowth(space, + self, + 2 * MB, + &ptr3_bytes_allocated, + &ptr3_usable_size, + &ptr3_bytes_tl_bulk_allocated)); + EXPECT_TRUE(ptr3.Get() != nullptr); + EXPECT_LE(2U * MB, ptr3_bytes_allocated); + EXPECT_LE(2U * MB, ptr3_usable_size); + EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); + EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated); + space->Free(self, ptr3.Assign(nullptr)); + + // Final clean up. + free1 = space->AllocationSize(ptr1.Get(), nullptr); + space->Free(self, ptr1.Assign(nullptr)); + EXPECT_LE(1U * MB, free1); +} + +TEST_P(SpaceCreateTest, AllocAndFreeTestBody) { + size_t dummy = 0; + MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB, nullptr)); + ASSERT_TRUE(space != nullptr); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Make space findable to the heap, will also delete space when runtime is cleaned up + AddSpace(space); + + // Succeeds, fits without adjusting the footprint limit. + size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated; + StackHandleScope<3> hs(soa.Self()); + MutableHandle<mirror::Object> ptr1(hs.NewHandle(Alloc(space, + self, + 1 * MB, + &ptr1_bytes_allocated, + &ptr1_usable_size, + &ptr1_bytes_tl_bulk_allocated))); + EXPECT_TRUE(ptr1.Get() != nullptr); + EXPECT_LE(1U * MB, ptr1_bytes_allocated); + EXPECT_LE(1U * MB, ptr1_usable_size); + EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); + EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated); + + // Fails, requires a higher footprint limit. + mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); + EXPECT_TRUE(ptr2 == nullptr); + + // Succeeds, adjusts the footprint. + size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated; + MutableHandle<mirror::Object> ptr3(hs.NewHandle(AllocWithGrowth(space, + self, + 8 * MB, + &ptr3_bytes_allocated, + &ptr3_usable_size, + &ptr3_bytes_tl_bulk_allocated))); + EXPECT_TRUE(ptr3.Get() != nullptr); + EXPECT_LE(8U * MB, ptr3_bytes_allocated); + EXPECT_LE(8U * MB, ptr3_usable_size); + EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); + EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated); + + // Fails, requires a higher footprint limit. + mirror::Object* ptr4 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); + EXPECT_TRUE(ptr4 == nullptr); + + // Also fails, requires a higher allowed footprint. + mirror::Object* ptr5 = AllocWithGrowth(space, self, 8 * MB, &dummy, nullptr, &dummy); + EXPECT_TRUE(ptr5 == nullptr); + + // Release some memory. + size_t free3 = space->AllocationSize(ptr3.Get(), nullptr); + EXPECT_EQ(free3, ptr3_bytes_allocated); + space->Free(self, ptr3.Assign(nullptr)); + EXPECT_LE(8U * MB, free3); + + // Succeeds, now that memory has been freed. + size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated; + Handle<mirror::Object> ptr6(hs.NewHandle(AllocWithGrowth(space, + self, + 9 * MB, + &ptr6_bytes_allocated, + &ptr6_usable_size, + &ptr6_bytes_tl_bulk_allocated))); + EXPECT_TRUE(ptr6.Get() != nullptr); + EXPECT_LE(9U * MB, ptr6_bytes_allocated); + EXPECT_LE(9U * MB, ptr6_usable_size); + EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated); + EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated); + + // Final clean up. + size_t free1 = space->AllocationSize(ptr1.Get(), nullptr); + space->Free(self, ptr1.Assign(nullptr)); + EXPECT_LE(1U * MB, free1); +} + +TEST_P(SpaceCreateTest, AllocAndFreeListTestBody) { + MallocSpace* space(CreateSpace("test", 4 * MB, 16 * MB, 16 * MB, nullptr)); + ASSERT_TRUE(space != nullptr); + + // Make space findable to the heap, will also delete space when runtime is cleaned up + AddSpace(space); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Succeeds, fits without adjusting the max allowed footprint. + mirror::Object* lots_of_objects[1024]; + for (size_t i = 0; i < arraysize(lots_of_objects); i++) { + size_t allocation_size, usable_size, bytes_tl_bulk_allocated; + size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray(); + lots_of_objects[i] = Alloc(space, + self, + size_of_zero_length_byte_array, + &allocation_size, + &usable_size, + &bytes_tl_bulk_allocated); + EXPECT_TRUE(lots_of_objects[i] != nullptr); + size_t computed_usable_size; + EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size)); + EXPECT_EQ(usable_size, computed_usable_size); + EXPECT_TRUE(bytes_tl_bulk_allocated == 0 || + bytes_tl_bulk_allocated >= allocation_size); + } + + // Release memory. + space->FreeList(self, arraysize(lots_of_objects), lots_of_objects); + + // Succeeds, fits by adjusting the max allowed footprint. + for (size_t i = 0; i < arraysize(lots_of_objects); i++) { + size_t allocation_size, usable_size, bytes_tl_bulk_allocated; + lots_of_objects[i] = AllocWithGrowth(space, + self, + 1024, + &allocation_size, + &usable_size, + &bytes_tl_bulk_allocated); + EXPECT_TRUE(lots_of_objects[i] != nullptr); + size_t computed_usable_size; + EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size)); + EXPECT_EQ(usable_size, computed_usable_size); + EXPECT_TRUE(bytes_tl_bulk_allocated == 0 || + bytes_tl_bulk_allocated >= allocation_size); + } + + // Release memory. + space->FreeList(self, arraysize(lots_of_objects), lots_of_objects); +} + +INSTANTIATE_TEST_CASE_P(CreateRosAllocSpace, + SpaceCreateTest, + testing::Values(kMallocSpaceRosAlloc)); +INSTANTIATE_TEST_CASE_P(CreateDlMallocSpace, + SpaceCreateTest, + testing::Values(kMallocSpaceDlMalloc)); + +} // namespace space +} // namespace gc +} // namespace art diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h index 4d2db11ac2..20ef44a77d 100644 --- a/runtime/gc/space/space_test.h +++ b/runtime/gc/space/space_test.h @@ -27,25 +27,28 @@ #include "mirror/class_loader.h" #include "mirror/object-inl.h" #include "scoped_thread_state_change.h" +#include "thread_list.h" #include "zygote_space.h" namespace art { namespace gc { namespace space { -class SpaceTest : public CommonRuntimeTest { +template <class Super> +class SpaceTest : public Super { public: - jobject byte_array_class_; - - SpaceTest() : byte_array_class_(nullptr) { - } + jobject byte_array_class_ = nullptr; void AddSpace(ContinuousSpace* space, bool revoke = true) { Heap* heap = Runtime::Current()->GetHeap(); if (revoke) { heap->RevokeAllThreadLocalBuffers(); } - heap->AddSpace(space); + { + ScopedThreadStateChange sts(Thread::Current(), kSuspended); + ScopedSuspendAll ssa("Add image space"); + heap->AddSpace(space); + } heap->SetSpaceAsDefault(space); } @@ -62,13 +65,19 @@ class SpaceTest : public CommonRuntimeTest { return reinterpret_cast<mirror::Class*>(self->DecodeJObject(byte_array_class_)); } - mirror::Object* Alloc(space::MallocSpace* alloc_space, Thread* self, size_t bytes, - size_t* bytes_allocated, size_t* usable_size, + mirror::Object* Alloc(space::MallocSpace* alloc_space, + Thread* self, + size_t bytes, + size_t* bytes_allocated, + size_t* usable_size, size_t* bytes_tl_bulk_allocated) SHARED_REQUIRES(Locks::mutator_lock_) { StackHandleScope<1> hs(self); Handle<mirror::Class> byte_array_class(hs.NewHandle(GetByteArrayClass(self))); - mirror::Object* obj = alloc_space->Alloc(self, bytes, bytes_allocated, usable_size, + mirror::Object* obj = alloc_space->Alloc(self, + bytes, + bytes_allocated, + usable_size, bytes_tl_bulk_allocated); if (obj != nullptr) { InstallClass(obj, byte_array_class.Get(), bytes); @@ -76,8 +85,11 @@ class SpaceTest : public CommonRuntimeTest { return obj; } - mirror::Object* AllocWithGrowth(space::MallocSpace* alloc_space, Thread* self, size_t bytes, - size_t* bytes_allocated, size_t* usable_size, + mirror::Object* AllocWithGrowth(space::MallocSpace* alloc_space, + Thread* self, + size_t bytes, + size_t* bytes_allocated, + size_t* usable_size, size_t* bytes_tl_bulk_allocated) SHARED_REQUIRES(Locks::mutator_lock_) { StackHandleScope<1> hs(self); @@ -117,10 +129,6 @@ class SpaceTest : public CommonRuntimeTest { typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, uint8_t* requested_begin); - void InitTestBody(CreateSpaceFn create_space); - void ZygoteSpaceTestBody(CreateSpaceFn create_space); - void AllocAndFreeTestBody(CreateSpaceFn create_space); - void AllocAndFreeListTestBody(CreateSpaceFn create_space); void SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size, int round, size_t growth_limit); @@ -132,278 +140,11 @@ static inline size_t test_rand(size_t* seed) { return *seed; } -void SpaceTest::InitTestBody(CreateSpaceFn create_space) { - // This will lead to error messages in the log. - ScopedLogSeverity sls(LogSeverity::FATAL); - - { - // Init < max == growth - std::unique_ptr<Space> space(create_space("test", 16 * MB, 32 * MB, 32 * MB, nullptr)); - EXPECT_TRUE(space.get() != nullptr); - } - { - // Init == max == growth - std::unique_ptr<Space> space(create_space("test", 16 * MB, 16 * MB, 16 * MB, nullptr)); - EXPECT_TRUE(space.get() != nullptr); - } - { - // Init > max == growth - std::unique_ptr<Space> space(create_space("test", 32 * MB, 16 * MB, 16 * MB, nullptr)); - EXPECT_TRUE(space.get() == nullptr); - } - { - // Growth == init < max - std::unique_ptr<Space> space(create_space("test", 16 * MB, 16 * MB, 32 * MB, nullptr)); - EXPECT_TRUE(space.get() != nullptr); - } - { - // Growth < init < max - std::unique_ptr<Space> space(create_space("test", 16 * MB, 8 * MB, 32 * MB, nullptr)); - EXPECT_TRUE(space.get() == nullptr); - } - { - // Init < growth < max - std::unique_ptr<Space> space(create_space("test", 8 * MB, 16 * MB, 32 * MB, nullptr)); - EXPECT_TRUE(space.get() != nullptr); - } - { - // Init < max < growth - std::unique_ptr<Space> space(create_space("test", 8 * MB, 32 * MB, 16 * MB, nullptr)); - EXPECT_TRUE(space.get() == nullptr); - } -} - -// TODO: This test is not very good, we should improve it. -// The test should do more allocations before the creation of the ZygoteSpace, and then do -// allocations after the ZygoteSpace is created. The test should also do some GCs to ensure that -// the GC works with the ZygoteSpace. -void SpaceTest::ZygoteSpaceTestBody(CreateSpaceFn create_space) { - size_t dummy; - MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr)); - ASSERT_TRUE(space != nullptr); - - // Make space findable to the heap, will also delete space when runtime is cleaned up - AddSpace(space); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - - // Succeeds, fits without adjusting the footprint limit. - size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated; - StackHandleScope<3> hs(soa.Self()); - MutableHandle<mirror::Object> ptr1( - hs.NewHandle(Alloc(space, self, 1 * MB, &ptr1_bytes_allocated, &ptr1_usable_size, - &ptr1_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr1.Get() != nullptr); - EXPECT_LE(1U * MB, ptr1_bytes_allocated); - EXPECT_LE(1U * MB, ptr1_usable_size); - EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); - EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated); - - // Fails, requires a higher footprint limit. - mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); - EXPECT_TRUE(ptr2 == nullptr); - - // Succeeds, adjusts the footprint. - size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated; - MutableHandle<mirror::Object> ptr3( - hs.NewHandle(AllocWithGrowth(space, self, 8 * MB, &ptr3_bytes_allocated, &ptr3_usable_size, - &ptr3_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr3.Get() != nullptr); - EXPECT_LE(8U * MB, ptr3_bytes_allocated); - EXPECT_LE(8U * MB, ptr3_usable_size); - EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); - EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated); - - // Fails, requires a higher footprint limit. - mirror::Object* ptr4 = space->Alloc(self, 8 * MB, &dummy, nullptr, &dummy); - EXPECT_TRUE(ptr4 == nullptr); - - // Also fails, requires a higher allowed footprint. - mirror::Object* ptr5 = space->AllocWithGrowth(self, 8 * MB, &dummy, nullptr, &dummy); - EXPECT_TRUE(ptr5 == nullptr); - - // Release some memory. - size_t free3 = space->AllocationSize(ptr3.Get(), nullptr); - EXPECT_EQ(free3, ptr3_bytes_allocated); - EXPECT_EQ(free3, space->Free(self, ptr3.Assign(nullptr))); - EXPECT_LE(8U * MB, free3); - - // Succeeds, now that memory has been freed. - size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated; - Handle<mirror::Object> ptr6( - hs.NewHandle(AllocWithGrowth(space, self, 9 * MB, &ptr6_bytes_allocated, &ptr6_usable_size, - &ptr6_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr6.Get() != nullptr); - EXPECT_LE(9U * MB, ptr6_bytes_allocated); - EXPECT_LE(9U * MB, ptr6_usable_size); - EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated); - EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated); - - // Final clean up. - size_t free1 = space->AllocationSize(ptr1.Get(), nullptr); - space->Free(self, ptr1.Assign(nullptr)); - EXPECT_LE(1U * MB, free1); - - // Make sure that the zygote space isn't directly at the start of the space. - EXPECT_TRUE(space->Alloc(self, 1U * MB, &dummy, nullptr, &dummy) != nullptr); - - gc::Heap* heap = Runtime::Current()->GetHeap(); - space::Space* old_space = space; - heap->RemoveSpace(old_space); - heap->RevokeAllThreadLocalBuffers(); - space::ZygoteSpace* zygote_space = space->CreateZygoteSpace("alloc space", - heap->IsLowMemoryMode(), - &space); - delete old_space; - // Add the zygote space. - AddSpace(zygote_space, false); - - // Make space findable to the heap, will also delete space when runtime is cleaned up - AddSpace(space, false); - - // Succeeds, fits without adjusting the footprint limit. - ptr1.Assign(Alloc(space, self, 1 * MB, &ptr1_bytes_allocated, &ptr1_usable_size, - &ptr1_bytes_tl_bulk_allocated)); - EXPECT_TRUE(ptr1.Get() != nullptr); - EXPECT_LE(1U * MB, ptr1_bytes_allocated); - EXPECT_LE(1U * MB, ptr1_usable_size); - EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); - EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated); - - // Fails, requires a higher footprint limit. - ptr2 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); - EXPECT_TRUE(ptr2 == nullptr); - - // Succeeds, adjusts the footprint. - ptr3.Assign(AllocWithGrowth(space, self, 2 * MB, &ptr3_bytes_allocated, &ptr3_usable_size, - &ptr3_bytes_tl_bulk_allocated)); - EXPECT_TRUE(ptr3.Get() != nullptr); - EXPECT_LE(2U * MB, ptr3_bytes_allocated); - EXPECT_LE(2U * MB, ptr3_usable_size); - EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); - EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated); - space->Free(self, ptr3.Assign(nullptr)); - - // Final clean up. - free1 = space->AllocationSize(ptr1.Get(), nullptr); - space->Free(self, ptr1.Assign(nullptr)); - EXPECT_LE(1U * MB, free1); -} - -void SpaceTest::AllocAndFreeTestBody(CreateSpaceFn create_space) { - size_t dummy = 0; - MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr)); - ASSERT_TRUE(space != nullptr); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - - // Make space findable to the heap, will also delete space when runtime is cleaned up - AddSpace(space); - - // Succeeds, fits without adjusting the footprint limit. - size_t ptr1_bytes_allocated, ptr1_usable_size, ptr1_bytes_tl_bulk_allocated; - StackHandleScope<3> hs(soa.Self()); - MutableHandle<mirror::Object> ptr1( - hs.NewHandle(Alloc(space, self, 1 * MB, &ptr1_bytes_allocated, &ptr1_usable_size, - &ptr1_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr1.Get() != nullptr); - EXPECT_LE(1U * MB, ptr1_bytes_allocated); - EXPECT_LE(1U * MB, ptr1_usable_size); - EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); - EXPECT_EQ(ptr1_bytes_tl_bulk_allocated, ptr1_bytes_allocated); - - // Fails, requires a higher footprint limit. - mirror::Object* ptr2 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); - EXPECT_TRUE(ptr2 == nullptr); - - // Succeeds, adjusts the footprint. - size_t ptr3_bytes_allocated, ptr3_usable_size, ptr3_bytes_tl_bulk_allocated; - MutableHandle<mirror::Object> ptr3( - hs.NewHandle(AllocWithGrowth(space, self, 8 * MB, &ptr3_bytes_allocated, &ptr3_usable_size, - &ptr3_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr3.Get() != nullptr); - EXPECT_LE(8U * MB, ptr3_bytes_allocated); - EXPECT_LE(8U * MB, ptr3_usable_size); - EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); - EXPECT_EQ(ptr3_bytes_tl_bulk_allocated, ptr3_bytes_allocated); - - // Fails, requires a higher footprint limit. - mirror::Object* ptr4 = Alloc(space, self, 8 * MB, &dummy, nullptr, &dummy); - EXPECT_TRUE(ptr4 == nullptr); - - // Also fails, requires a higher allowed footprint. - mirror::Object* ptr5 = AllocWithGrowth(space, self, 8 * MB, &dummy, nullptr, &dummy); - EXPECT_TRUE(ptr5 == nullptr); - - // Release some memory. - size_t free3 = space->AllocationSize(ptr3.Get(), nullptr); - EXPECT_EQ(free3, ptr3_bytes_allocated); - space->Free(self, ptr3.Assign(nullptr)); - EXPECT_LE(8U * MB, free3); - - // Succeeds, now that memory has been freed. - size_t ptr6_bytes_allocated, ptr6_usable_size, ptr6_bytes_tl_bulk_allocated; - Handle<mirror::Object> ptr6( - hs.NewHandle(AllocWithGrowth(space, self, 9 * MB, &ptr6_bytes_allocated, &ptr6_usable_size, - &ptr6_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr6.Get() != nullptr); - EXPECT_LE(9U * MB, ptr6_bytes_allocated); - EXPECT_LE(9U * MB, ptr6_usable_size); - EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated); - EXPECT_EQ(ptr6_bytes_tl_bulk_allocated, ptr6_bytes_allocated); - - // Final clean up. - size_t free1 = space->AllocationSize(ptr1.Get(), nullptr); - space->Free(self, ptr1.Assign(nullptr)); - EXPECT_LE(1U * MB, free1); -} - -void SpaceTest::AllocAndFreeListTestBody(CreateSpaceFn create_space) { - MallocSpace* space(create_space("test", 4 * MB, 16 * MB, 16 * MB, nullptr)); - ASSERT_TRUE(space != nullptr); - - // Make space findable to the heap, will also delete space when runtime is cleaned up - AddSpace(space); - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - - // Succeeds, fits without adjusting the max allowed footprint. - mirror::Object* lots_of_objects[1024]; - for (size_t i = 0; i < arraysize(lots_of_objects); i++) { - size_t allocation_size, usable_size, bytes_tl_bulk_allocated; - size_t size_of_zero_length_byte_array = SizeOfZeroLengthByteArray(); - lots_of_objects[i] = Alloc(space, self, size_of_zero_length_byte_array, &allocation_size, - &usable_size, &bytes_tl_bulk_allocated); - EXPECT_TRUE(lots_of_objects[i] != nullptr); - size_t computed_usable_size; - EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size)); - EXPECT_EQ(usable_size, computed_usable_size); - EXPECT_TRUE(bytes_tl_bulk_allocated == 0 || - bytes_tl_bulk_allocated >= allocation_size); - } - - // Release memory. - space->FreeList(self, arraysize(lots_of_objects), lots_of_objects); - - // Succeeds, fits by adjusting the max allowed footprint. - for (size_t i = 0; i < arraysize(lots_of_objects); i++) { - size_t allocation_size, usable_size, bytes_tl_bulk_allocated; - lots_of_objects[i] = AllocWithGrowth(space, self, 1024, &allocation_size, &usable_size, - &bytes_tl_bulk_allocated); - EXPECT_TRUE(lots_of_objects[i] != nullptr); - size_t computed_usable_size; - EXPECT_EQ(allocation_size, space->AllocationSize(lots_of_objects[i], &computed_usable_size)); - EXPECT_EQ(usable_size, computed_usable_size); - EXPECT_TRUE(bytes_tl_bulk_allocated == 0 || - bytes_tl_bulk_allocated >= allocation_size); - } - - // Release memory. - space->FreeList(self, arraysize(lots_of_objects), lots_of_objects); -} - -void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t object_size, - int round, size_t growth_limit) { +template <class Super> +void SpaceTest<Super>::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, + intptr_t object_size, + int round, + size_t growth_limit) { if (((object_size > 0 && object_size >= static_cast<intptr_t>(growth_limit))) || ((object_size < 0 && -object_size >= static_cast<intptr_t>(growth_limit)))) { // No allocation can succeed @@ -576,7 +317,9 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, intptr_t EXPECT_LE(space->Size(), growth_limit); } -void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, CreateSpaceFn create_space) { +template <class Super> +void SpaceTest<Super>::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, + CreateSpaceFn create_space) { if (object_size < SizeOfZeroLengthByteArray()) { // Too small for the object layout/model. return; @@ -614,25 +357,8 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, Create SizeFootPrintGrowthLimitAndTrimDriver(-size, spaceFn); \ } -#define TEST_SPACE_CREATE_FN_BASE(spaceName, spaceFn) \ - class spaceName##BaseTest : public SpaceTest { \ - }; \ - \ - TEST_F(spaceName##BaseTest, Init) { \ - InitTestBody(spaceFn); \ - } \ - TEST_F(spaceName##BaseTest, ZygoteSpace) { \ - ZygoteSpaceTestBody(spaceFn); \ - } \ - TEST_F(spaceName##BaseTest, AllocAndFree) { \ - AllocAndFreeTestBody(spaceFn); \ - } \ - TEST_F(spaceName##BaseTest, AllocAndFreeList) { \ - AllocAndFreeListTestBody(spaceFn); \ - } - #define TEST_SPACE_CREATE_FN_STATIC(spaceName, spaceFn) \ - class spaceName##StaticTest : public SpaceTest { \ + class spaceName##StaticTest : public SpaceTest<CommonRuntimeTest> { \ }; \ \ TEST_SizeFootPrintGrowthLimitAndTrimStatic(12B, spaceName, spaceFn, 12) \ @@ -648,7 +374,7 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size, Create TEST_SizeFootPrintGrowthLimitAndTrimStatic(8MB, spaceName, spaceFn, 8 * MB) #define TEST_SPACE_CREATE_FN_RANDOM(spaceName, spaceFn) \ - class spaceName##RandomTest : public SpaceTest { \ + class spaceName##RandomTest : public SpaceTest<CommonRuntimeTest> { \ }; \ \ TEST_SizeFootPrintGrowthLimitAndTrimRandom(16B, spaceName, spaceFn, 16) \ diff --git a/runtime/handle.h b/runtime/handle.h index 5b3bb60dfa..a415373c69 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -70,13 +70,11 @@ class Handle : public ValueObject { return reinterpret_cast<jobject>(reference_); } - ALWAYS_INLINE StackReference<mirror::Object>* GetReference() - SHARED_REQUIRES(Locks::mutator_lock_) { + ALWAYS_INLINE StackReference<mirror::Object>* GetReference() { return reference_; } - ALWAYS_INLINE const StackReference<mirror::Object>* GetReference() const - SHARED_REQUIRES(Locks::mutator_lock_) { + ALWAYS_INLINE const StackReference<mirror::Object>* GetReference() const { return reference_; } diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index e617348ce8..d53a0e4f96 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -62,8 +62,7 @@ class PACKED(4) HandleScope { ALWAYS_INLINE mirror::Object* GetReference(size_t i) const SHARED_REQUIRES(Locks::mutator_lock_); - ALWAYS_INLINE Handle<mirror::Object> GetHandle(size_t i) - SHARED_REQUIRES(Locks::mutator_lock_); + ALWAYS_INLINE Handle<mirror::Object> GetHandle(size_t i); ALWAYS_INLINE MutableHandle<mirror::Object> GetMutableHandle(size_t i) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index dfc1f5fe71..bb35ec73d9 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -419,18 +419,13 @@ class Hprof : public SingleRootVisitor { Hprof(const char* output_filename, int fd, bool direct_to_ddms) : filename_(output_filename), fd_(fd), - direct_to_ddms_(direct_to_ddms), - start_ns_(NanoTime()), - output_(nullptr), - current_heap_(HPROF_HEAP_DEFAULT), - objects_in_segment_(0), - next_string_id_(0x400000), - next_class_serial_number_(1) { + direct_to_ddms_(direct_to_ddms) { LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting..."; } void Dump() - REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !Locks::alloc_tracker_lock_) { + REQUIRES(Locks::mutator_lock_) + REQUIRES(!Locks::heap_bitmap_lock_, !Locks::alloc_tracker_lock_) { { MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_); if (Runtime::Current()->GetHeap()->IsAllocTrackingEnabled()) { @@ -462,10 +457,11 @@ class Hprof : public SingleRootVisitor { } if (okay) { - uint64_t duration = NanoTime() - start_ns_; - LOG(INFO) << "hprof: heap dump completed (" - << PrettySize(RoundUp(overall_size, 1024)) - << ") in " << PrettyDuration(duration); + const uint64_t duration = NanoTime() - start_ns_; + LOG(INFO) << "hprof: heap dump completed (" << PrettySize(RoundUp(overall_size, KB)) + << ") in " << PrettyDuration(duration) + << " objects " << total_objects_ + << " objects with stack traces " << total_objects_with_stack_trace_; } } @@ -855,7 +851,7 @@ class Hprof : public SingleRootVisitor { } CHECK_EQ(traces_.size(), next_trace_sn - kHprofNullStackTrace - 1); CHECK_EQ(frames_.size(), next_frame_id); - VLOG(heap) << "hprof: found " << count << " objects with allocation stack traces"; + total_objects_with_stack_trace_ = count; } // If direct_to_ddms_ is set, "filename_" and "fd" will be ignored. @@ -865,16 +861,19 @@ class Hprof : public SingleRootVisitor { int fd_; bool direct_to_ddms_; - uint64_t start_ns_; + uint64_t start_ns_ = NanoTime(); - EndianOutput* output_; + EndianOutput* output_ = nullptr; - HprofHeapId current_heap_; // Which heap we're currently dumping. - size_t objects_in_segment_; + HprofHeapId current_heap_ = HPROF_HEAP_DEFAULT; // Which heap we're currently dumping. + size_t objects_in_segment_ = 0; - HprofStringId next_string_id_; + size_t total_objects_ = 0u; + size_t total_objects_with_stack_trace_ = 0u; + + HprofStringId next_string_id_ = 0x400000; SafeMap<std::string, HprofStringId> strings_; - HprofClassSerialNumber next_class_serial_number_; + HprofClassSerialNumber next_class_serial_number_ = 1; SafeMap<mirror::Class*, HprofClassSerialNumber> classes_; std::unordered_map<const gc::AllocRecordStackTrace*, HprofStackTraceSerialNumber, @@ -1064,6 +1063,8 @@ void Hprof::DumpHeapObject(mirror::Object* obj) { return; } + ++total_objects_; + GcRootVisitor visitor(this); obj->VisitReferences(visitor, VoidFunctor()); diff --git a/runtime/image-inl.h b/runtime/image-inl.h new file mode 100644 index 0000000000..e3307d87b6 --- /dev/null +++ b/runtime/image-inl.h @@ -0,0 +1,47 @@ +/* + * 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_RUNTIME_IMAGE_INL_H_ +#define ART_RUNTIME_IMAGE_INL_H_ + +#include "image.h" + +namespace art { + +template <ReadBarrierOption kReadBarrierOption> +inline mirror::Object* ImageHeader::GetImageRoot(ImageRoot image_root) const { + mirror::ObjectArray<mirror::Object>* image_roots = GetImageRoots<kReadBarrierOption>(); + return image_roots->Get<kVerifyNone, kReadBarrierOption>(static_cast<int32_t>(image_root)); +} + +template <ReadBarrierOption kReadBarrierOption> +inline mirror::ObjectArray<mirror::Object>* ImageHeader::GetImageRoots() const { + // Need a read barrier as it's not visited during root scan. + // Pass in the address of the local variable to the read barrier + // rather than image_roots_ because it won't move (asserted below) + // and it's a const member. + mirror::ObjectArray<mirror::Object>* image_roots = + reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(image_roots_); + mirror::ObjectArray<mirror::Object>* result = + ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Object>, kReadBarrierOption>( + &image_roots); + DCHECK_EQ(image_roots, result); + return image_roots; +} + +} // namespace art + +#endif // ART_RUNTIME_IMAGE_INL_H_ diff --git a/runtime/image.cc b/runtime/image.cc index 3cb66428fa..de00343451 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -24,7 +24,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '5', '\0' }; +const uint8_t ImageHeader::kImageVersion[] = { '0', '2', '6', '\0' }; ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, @@ -35,8 +35,13 @@ ImageHeader::ImageHeader(uint32_t image_begin, uint32_t oat_data_begin, uint32_t oat_data_end, uint32_t oat_file_end, + uint32_t boot_image_begin, + uint32_t boot_image_size, + uint32_t boot_oat_begin, + uint32_t boot_oat_size, uint32_t pointer_size, bool compile_pic, + bool is_pic, StorageMode storage_mode, size_t data_size) : image_begin_(image_begin), @@ -46,10 +51,15 @@ ImageHeader::ImageHeader(uint32_t image_begin, oat_data_begin_(oat_data_begin), oat_data_end_(oat_data_end), oat_file_end_(oat_file_end), + boot_image_begin_(boot_image_begin), + boot_image_size_(boot_image_size), + boot_oat_begin_(boot_oat_begin), + boot_oat_size_(boot_oat_size), patch_delta_(0), image_roots_(image_roots), pointer_size_(pointer_size), compile_pic_(compile_pic), + is_pic_(is_pic), storage_mode_(storage_mode), data_size_(data_size) { CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize)); @@ -67,13 +77,21 @@ ImageHeader::ImageHeader(uint32_t image_begin, void ImageHeader::RelocateImage(off_t delta) { CHECK_ALIGNED(delta, kPageSize) << " patch delta must be page aligned"; - image_begin_ += delta; oat_file_begin_ += delta; oat_data_begin_ += delta; oat_data_end_ += delta; oat_file_end_ += delta; - image_roots_ += delta; patch_delta_ += delta; + RelocateImageObjects(delta); + RelocateImageMethods(delta); +} + +void ImageHeader::RelocateImageObjects(off_t delta) { + image_begin_ += delta; + image_roots_ += delta; +} + +void ImageHeader::RelocateImageMethods(off_t delta) { for (size_t i = 0; i < kImageMethodsCount; ++i) { image_methods_[i] += delta; } @@ -110,24 +128,6 @@ const char* ImageHeader::GetMagic() const { return reinterpret_cast<const char*>(magic_); } -mirror::Object* ImageHeader::GetImageRoot(ImageRoot image_root) const { - return GetImageRoots()->Get(image_root); -} - -mirror::ObjectArray<mirror::Object>* ImageHeader::GetImageRoots() const { - // Need a read barrier as it's not visited during root scan. - // Pass in the address of the local variable to the read barrier - // rather than image_roots_ because it won't move (asserted below) - // and it's a const member. - mirror::ObjectArray<mirror::Object>* image_roots = - reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(image_roots_); - mirror::ObjectArray<mirror::Object>* result = - ReadBarrier::BarrierForRoot<mirror::ObjectArray<mirror::Object>, kWithReadBarrier>( - &image_roots); - DCHECK_EQ(image_roots, result); - return result; -} - ArtMethod* ImageHeader::GetImageMethod(ImageMethod index) const { CHECK_LT(static_cast<size_t>(index), kImageMethodsCount); return reinterpret_cast<ArtMethod*>(image_methods_[index]); diff --git a/runtime/image.h b/runtime/image.h index 7418f660aa..c449e43243 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -93,10 +93,15 @@ class PACKED(4) ImageHeader { oat_data_begin_(0U), oat_data_end_(0U), oat_file_end_(0U), + boot_image_begin_(0U), + boot_image_size_(0U), + boot_oat_begin_(0U), + boot_oat_size_(0U), patch_delta_(0), image_roots_(0U), pointer_size_(0U), compile_pic_(0), + is_pic_(0), storage_mode_(kDefaultStorageMode), data_size_(0) {} @@ -109,8 +114,13 @@ class PACKED(4) ImageHeader { uint32_t oat_data_begin, uint32_t oat_data_end, uint32_t oat_file_end, + uint32_t boot_image_begin, + uint32_t boot_image_size, + uint32_t boot_oat_begin, + uint32_t boot_oat_size, uint32_t pointer_size, bool compile_pic, + bool is_pic, StorageMode storage_mode, size_t data_size); @@ -202,17 +212,42 @@ class PACKED(4) ImageHeader { return GetImageSection(kSectionArtMethods); } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> mirror::Object* GetImageRoot(ImageRoot image_root) const SHARED_REQUIRES(Locks::mutator_lock_); + + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> mirror::ObjectArray<mirror::Object>* GetImageRoots() const SHARED_REQUIRES(Locks::mutator_lock_); void RelocateImage(off_t delta); + void RelocateImageMethods(off_t delta); + void RelocateImageObjects(off_t delta); bool CompilePic() const { return compile_pic_ != 0; } + bool IsPic() const { + return is_pic_ != 0; + } + + uint32_t GetBootImageBegin() const { + return boot_image_begin_; + } + + uint32_t GetBootImageSize() const { + return boot_image_size_; + } + + uint32_t GetBootOatBegin() const { + return boot_oat_begin_; + } + + uint32_t GetBootOatSize() const { + return boot_oat_size_; + } + StorageMode GetStorageMode() const { return storage_mode_; } @@ -221,6 +256,12 @@ class PACKED(4) ImageHeader { return data_size_; } + bool IsAppImage() const { + // App images currently require a boot image, if the size is non zero then it is an app image + // header. + return boot_image_size_ != 0u; + } + private: static const uint8_t kImageMagic[4]; static const uint8_t kImageVersion[4]; @@ -250,6 +291,16 @@ class PACKED(4) ImageHeader { // .so files. Used for positioning a following alloc spaces. uint32_t oat_file_end_; + // Boot image begin and end (app image headers only). + uint32_t boot_image_begin_; + uint32_t boot_image_size_; + + // Boot oat begin and end (app image headers only). + uint32_t boot_oat_begin_; + uint32_t boot_oat_size_; + + // TODO: We should probably insert a boot image checksum for app images. + // The total delta that this image has been patched. int32_t patch_delta_; @@ -262,10 +313,15 @@ class PACKED(4) ImageHeader { // Boolean (0 or 1) to denote if the image was compiled with --compile-pic option const uint32_t compile_pic_; + // Boolean (0 or 1) to denote if the image can be mapped at a random address, this only refers to + // the .art file. Currently, app oat files do not depend on their app image. There are no pointers + // from the app oat code to the app image. + const uint32_t is_pic_; + // Image section sizes/offsets correspond to the uncompressed form. ImageSection sections_[kSectionCount]; - // Image methods. + // Image methods, may be inside of the boot image for app images. uint64_t image_methods_[kImageMethodsCount]; // Storage method for the image, the image may be compressed. diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 7d55e8c20a..c57b1bbf2d 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -78,7 +78,7 @@ Instrumentation::Instrumentation() have_field_read_listeners_(false), have_field_write_listeners_(false), have_exception_caught_listeners_(false), - have_backward_branch_listeners_(false), + have_branch_listeners_(false), have_invoke_virtual_or_interface_listeners_(false), deoptimized_methods_lock_("deoptimized methods lock"), deoptimization_enabled_(false), @@ -431,11 +431,11 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev method_unwind_listeners_, listener, &have_method_unwind_listeners_); - PotentiallyAddListenerTo(kBackwardBranch, + PotentiallyAddListenerTo(kBranch, events, - backward_branch_listeners_, + branch_listeners_, listener, - &have_backward_branch_listeners_); + &have_branch_listeners_); PotentiallyAddListenerTo(kInvokeVirtualOrInterface, events, invoke_virtual_or_interface_listeners_, @@ -508,11 +508,11 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t method_unwind_listeners_, listener, &have_method_unwind_listeners_); - PotentiallyRemoveListenerFrom(kBackwardBranch, + PotentiallyRemoveListenerFrom(kBranch, events, - backward_branch_listeners_, + branch_listeners_, listener, - &have_backward_branch_listeners_); + &have_branch_listeners_); PotentiallyRemoveListenerFrom(kInvokeVirtualOrInterface, events, invoke_virtual_or_interface_listeners_, @@ -917,11 +917,13 @@ void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_o } } -void Instrumentation::BackwardBranchImpl(Thread* thread, ArtMethod* method, - int32_t offset) const { - for (InstrumentationListener* listener : backward_branch_listeners_) { +void Instrumentation::BranchImpl(Thread* thread, + ArtMethod* method, + uint32_t dex_pc, + int32_t offset) const { + for (InstrumentationListener* listener : branch_listeners_) { if (listener != nullptr) { - listener->BackwardBranch(thread, method, offset); + listener->Branch(thread, method, dex_pc, offset); } } } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index b29245f052..56aeefc2f5 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -94,8 +94,11 @@ struct InstrumentationListener { virtual void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object) SHARED_REQUIRES(Locks::mutator_lock_) = 0; - // Call-back for when we get a backward branch. - virtual void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset) + // Call-back for when we execute a branch. + virtual void Branch(Thread* thread, + ArtMethod* method, + uint32_t dex_pc, + int32_t dex_pc_offset) SHARED_REQUIRES(Locks::mutator_lock_) = 0; // Call-back for when we get an invokevirtual or an invokeinterface. @@ -122,7 +125,7 @@ class Instrumentation { kFieldRead = 0x10, kFieldWritten = 0x20, kExceptionCaught = 0x40, - kBackwardBranch = 0x80, + kBranch = 0x80, kInvokeVirtualOrInterface = 0x100, }; @@ -276,8 +279,8 @@ class Instrumentation { return have_exception_caught_listeners_; } - bool HasBackwardBranchListeners() const SHARED_REQUIRES(Locks::mutator_lock_) { - return have_backward_branch_listeners_; + bool HasBranchListeners() const SHARED_REQUIRES(Locks::mutator_lock_) { + return have_branch_listeners_; } bool HasInvokeVirtualOrInterfaceListeners() const SHARED_REQUIRES(Locks::mutator_lock_) { @@ -324,11 +327,11 @@ class Instrumentation { } } - // Inform listeners that a backward branch has been taken (only supported by the interpreter). - void BackwardBranch(Thread* thread, ArtMethod* method, int32_t offset) const + // Inform listeners that a branch has been taken (only supported by the interpreter). + void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_) { - if (UNLIKELY(HasBackwardBranchListeners())) { - BackwardBranchImpl(thread, method, offset); + if (UNLIKELY(HasBranchListeners())) { + BranchImpl(thread, method, dex_pc, offset); } } @@ -442,7 +445,7 @@ class Instrumentation { void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object, ArtMethod* method, uint32_t dex_pc) const SHARED_REQUIRES(Locks::mutator_lock_); - void BackwardBranchImpl(Thread* thread, ArtMethod* method, int32_t offset) const + void BranchImpl(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_); void InvokeVirtualOrInterfaceImpl(Thread* thread, mirror::Object* this_object, @@ -513,8 +516,8 @@ class Instrumentation { // Do we have any exception caught listeners? Short-cut to avoid taking the instrumentation_lock_. bool have_exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); - // Do we have any backward branch listeners? Short-cut to avoid taking the instrumentation_lock_. - bool have_backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_); + // Do we have any branch listeners? Short-cut to avoid taking the instrumentation_lock_. + bool have_branch_listeners_ GUARDED_BY(Locks::mutator_lock_); // Do we have any invoke listeners? Short-cut to avoid taking the instrumentation_lock_. bool have_invoke_virtual_or_interface_listeners_ GUARDED_BY(Locks::mutator_lock_); @@ -537,7 +540,7 @@ class Instrumentation { std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_); - std::list<InstrumentationListener*> backward_branch_listeners_ GUARDED_BY(Locks::mutator_lock_); + std::list<InstrumentationListener*> branch_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> invoke_virtual_or_interface_listeners_ GUARDED_BY(Locks::mutator_lock_); std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc index e4688a21dd..56e3bc5745 100644 --- a/runtime/instrumentation_test.cc +++ b/runtime/instrumentation_test.cc @@ -37,7 +37,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio : received_method_enter_event(false), received_method_exit_event(false), received_method_unwind_event(false), received_dex_pc_moved_event(false), received_field_read_event(false), received_field_written_event(false), - received_exception_caught_event(false), received_backward_branch_event(false), + received_exception_caught_event(false), received_branch_event(false), received_invoke_virtual_or_interface_event(false) {} virtual ~TestInstrumentationListener() {} @@ -100,11 +100,12 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio received_exception_caught_event = true; } - void BackwardBranch(Thread* thread ATTRIBUTE_UNUSED, - ArtMethod* method ATTRIBUTE_UNUSED, - int32_t dex_pc_offset ATTRIBUTE_UNUSED) + void Branch(Thread* thread ATTRIBUTE_UNUSED, + ArtMethod* method ATTRIBUTE_UNUSED, + uint32_t dex_pc ATTRIBUTE_UNUSED, + int32_t dex_pc_offset ATTRIBUTE_UNUSED) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { - received_backward_branch_event = true; + received_branch_event = true; } void InvokeVirtualOrInterface(Thread* thread ATTRIBUTE_UNUSED, @@ -124,7 +125,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio received_field_read_event = false; received_field_written_event = false; received_exception_caught_event = false; - received_backward_branch_event = false; + received_branch_event = false; received_invoke_virtual_or_interface_event = false; } @@ -135,7 +136,7 @@ class TestInstrumentationListener FINAL : public instrumentation::Instrumentatio bool received_field_read_event; bool received_field_written_event; bool received_exception_caught_event; - bool received_backward_branch_event; + bool received_branch_event; bool received_invoke_virtual_or_interface_event; private: @@ -305,8 +306,8 @@ class InstrumentationTest : public CommonRuntimeTest { return instr->HasFieldWriteListeners(); case instrumentation::Instrumentation::kExceptionCaught: return instr->HasExceptionCaughtListeners(); - case instrumentation::Instrumentation::kBackwardBranch: - return instr->HasBackwardBranchListeners(); + case instrumentation::Instrumentation::kBranch: + return instr->HasBranchListeners(); case instrumentation::Instrumentation::kInvokeVirtualOrInterface: return instr->HasInvokeVirtualOrInterfaceListeners(); default: @@ -349,8 +350,8 @@ class InstrumentationTest : public CommonRuntimeTest { self->ClearException(); break; } - case instrumentation::Instrumentation::kBackwardBranch: - instr->BackwardBranch(self, method, dex_pc); + case instrumentation::Instrumentation::kBranch: + instr->Branch(self, method, dex_pc, -1); break; case instrumentation::Instrumentation::kInvokeVirtualOrInterface: instr->InvokeVirtualOrInterface(self, obj, method, dex_pc, method); @@ -378,8 +379,8 @@ class InstrumentationTest : public CommonRuntimeTest { return listener.received_field_written_event; case instrumentation::Instrumentation::kExceptionCaught: return listener.received_exception_caught_event; - case instrumentation::Instrumentation::kBackwardBranch: - return listener.received_backward_branch_event; + case instrumentation::Instrumentation::kBranch: + return listener.received_branch_event; case instrumentation::Instrumentation::kInvokeVirtualOrInterface: return listener.received_invoke_virtual_or_interface_event; default: @@ -441,8 +442,8 @@ TEST_F(InstrumentationTest, ExceptionCaughtEvent) { TestEvent(instrumentation::Instrumentation::kExceptionCaught); } -TEST_F(InstrumentationTest, BackwardBranchEvent) { - TestEvent(instrumentation::Instrumentation::kBackwardBranch); +TEST_F(InstrumentationTest, BranchEvent) { + TestEvent(instrumentation::Instrumentation::kBranch); } TEST_F(InstrumentationTest, InvokeVirtualOrInterfaceEvent) { diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 015bf98e38..74a2532a5d 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -22,6 +22,7 @@ #include "gc/collector/garbage_collector.h" #include "gc/space/image_space.h" #include "gc/weak_root_state.h" +#include "image-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" @@ -86,14 +87,24 @@ void InternTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) { // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. } -mirror::String* InternTable::LookupStrong(mirror::String* s) { - return strong_interns_.Find(s); +mirror::String* InternTable::LookupWeak(Thread* self, mirror::String* s) { + MutexLock mu(self, *Locks::intern_table_lock_); + return LookupWeakLocked(s); +} + +mirror::String* InternTable::LookupStrong(Thread* self, mirror::String* s) { + MutexLock mu(self, *Locks::intern_table_lock_); + return LookupStrongLocked(s); } -mirror::String* InternTable::LookupWeak(mirror::String* s) { +mirror::String* InternTable::LookupWeakLocked(mirror::String* s) { return weak_interns_.Find(s); } +mirror::String* InternTable::LookupStrongLocked(mirror::String* s) { + return strong_interns_.Find(s); +} + void InternTable::AddNewTable() { MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); weak_interns_.AddNewTable(); @@ -169,7 +180,7 @@ void InternTable::AddImagesStringsToTable(const std::vector<gc::space::ImageSpac for (size_t j = 0; j < num_strings; ++j) { mirror::String* image_string = dex_cache->GetResolvedString(j); if (image_string != nullptr) { - mirror::String* found = LookupStrong(image_string); + mirror::String* found = LookupStrongLocked(image_string); if (found == nullptr) { InsertStrong(image_string); } else { @@ -250,7 +261,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong, bool hold } } // Check the strong table for a match. - mirror::String* strong = LookupStrong(s); + mirror::String* strong = LookupStrongLocked(s); if (strong != nullptr) { return strong; } @@ -272,7 +283,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong, bool hold CHECK(self->GetWeakRefAccessEnabled()); } // There is no match in the strong table, check the weak table. - mirror::String* weak = LookupWeak(s); + mirror::String* weak = LookupWeakLocked(s); if (weak != nullptr) { if (is_strong) { // A match was found in the weak table. Promote to the strong table. @@ -317,8 +328,7 @@ mirror::String* InternTable::InternWeak(mirror::String* s) { } bool InternTable::ContainsWeak(mirror::String* s) { - MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); - return LookupWeak(s) == s; + return LookupWeak(Thread::Current(), s) == s; } void InternTable::SweepInternTableWeaks(IsMarkedVisitor* visitor) { diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 2b2176efe1..274f5ade5e 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -84,10 +84,22 @@ class InternTable { bool ContainsWeak(mirror::String* s) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::intern_table_lock_); + // Lookup a strong intern, returns null if not found. + mirror::String* LookupStrong(Thread* self, mirror::String* s) + REQUIRES(!Locks::intern_table_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Lookup a weak intern, returns null if not found. + mirror::String* LookupWeak(Thread* self, mirror::String* s) + REQUIRES(!Locks::intern_table_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Total number of interned strings. size_t Size() const REQUIRES(!Locks::intern_table_lock_); + // Total number of weakly live interned strings. size_t StrongSize() const REQUIRES(!Locks::intern_table_lock_); + // Total number of strongly live interned strings. size_t WeakSize() const REQUIRES(!Locks::intern_table_lock_); @@ -186,9 +198,9 @@ class InternTable { mirror::String* Insert(mirror::String* s, bool is_strong, bool holding_locks) REQUIRES(!Locks::intern_table_lock_) SHARED_REQUIRES(Locks::mutator_lock_); - mirror::String* LookupStrong(mirror::String* s) + mirror::String* LookupStrongLocked(mirror::String* s) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_); - mirror::String* LookupWeak(mirror::String* s) + mirror::String* LookupWeakLocked(mirror::String* s) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_); mirror::String* InsertStrong(mirror::String* s) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_); diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 6b5218dff0..ec63fdf90e 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -239,7 +239,7 @@ static std::ostream& operator<<(std::ostream& os, const InterpreterImplKind& rhs } #if !defined(__clang__) -#if defined(__arm__) +#if (defined(__arm__) || defined(__i386__)) // TODO: remove when all targets implemented. static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else @@ -247,7 +247,7 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKin #endif #else // Clang 3.4 fails to build the goto interpreter implementation. -#if defined(__arm__) +#if (defined(__arm__) || defined(__i386__)) static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; #else static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind; diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index ecd4de9bd0..6c9cc7017b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -518,10 +518,10 @@ static inline bool NeedsInterpreter(Thread* self, ShadowFrame* new_shadow_frame) Dbg::IsForcedInterpreterNeededForCalling(self, target); } -static void ArtInterpreterToCompiledCodeBridge(Thread* self, - const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, - JValue* result) +void ArtInterpreterToCompiledCodeBridge(Thread* self, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, + JValue* result) SHARED_REQUIRES(Locks::mutator_lock_) { ArtMethod* method = shadow_frame->GetMethod(); // Ensure static methods are initialized. diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 932d255b0e..949112de5c 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -980,6 +980,9 @@ static inline bool IsBackwardBranch(int32_t branch_offset) { return branch_offset <= 0; } +void ArtInterpreterToCompiledCodeBridge(Thread* self, const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + // Explicitly instantiate all DoInvoke functions. #define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \ template SHARED_REQUIRES(Locks::mutator_lock_) \ diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index 9766299ae0..d50cfe64ae 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -21,6 +21,7 @@ #include "base/stl_util.h" // MakeUnique #include "experimental_flags.h" #include "interpreter_common.h" +#include "jit/jit.h" #include "safe_math.h" #include <memory> // std::unique_ptr @@ -63,10 +64,10 @@ namespace interpreter { currentHandlersTable = handlersTable[ \ Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()] -#define BACKWARD_BRANCH_INSTRUMENTATION(offset) \ +#define BRANCH_INSTRUMENTATION(offset) \ do { \ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \ - instrumentation->BackwardBranch(self, shadow_frame.GetMethod(), offset); \ + instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset); \ } while (false) #define UNREACHABLE_CODE_CHECK() \ @@ -183,9 +184,25 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF self->AssertNoPendingException(); } instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + ArtMethod *method = shadow_frame.GetMethod(); + if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), 0); + method, 0); + } + + if (UNLIKELY(Runtime::Current()->GetJit() != nullptr && + Runtime::Current()->GetJit()->JitAtFirstUse() && + method->HasAnyCompiledCode())) { + JValue result; + + // Pop the shadow frame before calling into compiled code. + self->PopShadowFrame(); + ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result); + // Push the shadow frame back as the caller will expect it. + self->PushShadowFrame(&shadow_frame); + + return result; } } @@ -633,8 +650,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(GOTO) { int8_t offset = inst->VRegA_10t(inst_data); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -646,8 +663,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(GOTO_16) { int16_t offset = inst->VRegA_20t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -659,8 +676,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(GOTO_32) { int32_t offset = inst->VRegA_30t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -672,8 +689,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(PACKED_SWITCH) { int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -685,8 +702,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(SPARSE_SWITCH) { int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -788,8 +805,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(IF_EQ) { if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -806,8 +823,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -824,8 +841,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -842,8 +859,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -860,8 +877,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -878,8 +895,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -895,8 +912,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(IF_EQZ) { if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -912,8 +929,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(IF_NEZ) { if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -929,8 +946,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(IF_LTZ) { if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -946,8 +963,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(IF_GEZ) { if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -963,8 +980,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(IF_GTZ) { if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); @@ -980,8 +997,8 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF HANDLE_INSTRUCTION_START(IF_LEZ) { if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); if (UNLIKELY(self->TestAllFlags())) { self->CheckSuspend(); UPDATE_HANDLER_TABLE(); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index bab0d40000..56a213cd1f 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -17,6 +17,7 @@ #include "base/stl_util.h" // MakeUnique #include "experimental_flags.h" #include "interpreter_common.h" +#include "jit/jit.h" #include "safe_math.h" #include <memory> // std::unique_ptr @@ -69,9 +70,9 @@ namespace interpreter { } \ } while (false) -#define BACKWARD_BRANCH_INSTRUMENTATION(offset) \ +#define BRANCH_INSTRUMENTATION(offset) \ do { \ - instrumentation->BackwardBranch(self, shadow_frame.GetMethod(), offset); \ + instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset); \ } while (false) static bool IsExperimentalInstructionEnabled(const Instruction *inst) { @@ -96,9 +97,26 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (kIsDebugBuild) { self->AssertNoPendingException(); } + + ArtMethod* method = shadow_frame.GetMethod(); + if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - shadow_frame.GetMethod(), 0); + method, 0); + } + + if (UNLIKELY(Runtime::Current()->GetJit() != nullptr && + Runtime::Current()->GetJit()->JitAtFirstUse() && + method->HasAnyCompiledCode())) { + JValue result; + + // Pop the shadow frame before calling into compiled code. + self->PopShadowFrame(); + ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result); + // Push the shadow frame back as the caller will expect it. + self->PushShadowFrame(&shadow_frame); + + return result; } } const uint16_t* const insns = code_item->insns_; @@ -565,8 +583,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::GOTO: { PREAMBLE(); int8_t offset = inst->VRegA_10t(inst_data); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -575,8 +593,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::GOTO_16: { PREAMBLE(); int16_t offset = inst->VRegA_20t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -585,8 +603,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::GOTO_32: { PREAMBLE(); int32_t offset = inst->VRegA_30t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -595,8 +613,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::PACKED_SWITCH: { PREAMBLE(); int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -605,8 +623,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::SPARSE_SWITCH: { PREAMBLE(); int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -709,8 +727,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -724,8 +742,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -739,8 +757,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -754,8 +772,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -769,8 +787,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -784,8 +802,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) { int16_t offset = inst->VRegC_22t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -798,8 +816,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -812,8 +830,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -826,8 +844,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -840,8 +858,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -854,8 +872,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); @@ -868,8 +886,8 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, PREAMBLE(); if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) { int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); if (IsBackwardBranch(offset)) { - BACKWARD_BRANCH_INSTRUMENTATION(offset); self->AllowThreadSuspension(); } inst = inst->RelativeAt(offset); diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S index 617f572c0f..1dba856ecb 100644 --- a/runtime/interpreter/mterp/arm/footer.S +++ b/runtime/interpreter/mterp/arm/footer.S @@ -150,12 +150,8 @@ MterpExceptionReturn: b MterpDone MterpReturn: ldr r2, [rFP, #OFF_FP_RESULT_REGISTER] - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] str r0, [r2] str r1, [r2, #4] - mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) - blne MterpSuspendCheck @ (self) mov r0, #1 @ signal return to caller. MterpDone: add sp, sp, #4 @ un-align 64 diff --git a/runtime/interpreter/mterp/arm/op_check_cast.S b/runtime/interpreter/mterp/arm/op_check_cast.S index 3e3ac707c4..24eba45a69 100644 --- a/runtime/interpreter/mterp/arm/op_check_cast.S +++ b/runtime/interpreter/mterp/arm/op_check_cast.S @@ -5,10 +5,10 @@ EXPORT_PC FETCH r0, 1 @ r0<- BBBB mov r1, rINST, lsr #8 @ r1<- AA - GET_VREG r1, r1 @ r1<- object + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &object ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- method mov r3, rSELF @ r3<- self - bl MterpCheckCast @ (index, obj, method, self) + bl MterpCheckCast @ (index, &obj, method, self) PREFETCH_INST 2 cmp r0, #0 bne MterpPossibleException diff --git a/runtime/interpreter/mterp/arm/op_instance_of.S b/runtime/interpreter/mterp/arm/op_instance_of.S index e94108ca06..d76f0b09fe 100644 --- a/runtime/interpreter/mterp/arm/op_instance_of.S +++ b/runtime/interpreter/mterp/arm/op_instance_of.S @@ -8,12 +8,12 @@ EXPORT_PC FETCH r0, 1 @ r0<- CCCC mov r1, rINST, lsr #12 @ r1<- B - GET_VREG r1, r1 @ r1<- vB (object) + 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) + bl MterpInstanceOf @ (index, &obj, method, self) ldr r1, [rSELF, #THREAD_EXCEPTION_OFFSET] PREFETCH_INST 2 cmp r1, #0 @ exception pending? diff --git a/runtime/interpreter/mterp/arm/op_return.S b/runtime/interpreter/mterp/arm/op_return.S index a4ffd04ea6..1888373256 100644 --- a/runtime/interpreter/mterp/arm/op_return.S +++ b/runtime/interpreter/mterp/arm/op_return.S @@ -6,6 +6,10 @@ /* op vAA */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA GET_VREG r0, r2 @ r0<- vAA mov r1, #0 diff --git a/runtime/interpreter/mterp/arm/op_return_void.S b/runtime/interpreter/mterp/arm/op_return_void.S index f6dfd99309..cbea2bf6a0 100644 --- a/runtime/interpreter/mterp/arm/op_return_void.S +++ b/runtime/interpreter/mterp/arm/op_return_void.S @@ -1,5 +1,9 @@ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 b MterpReturn diff --git a/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S b/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S index 7322940a74..2dde7aeefc 100644 --- a/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S +++ b/runtime/interpreter/mterp/arm/op_return_void_no_barrier.S @@ -1,3 +1,7 @@ + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 b MterpReturn diff --git a/runtime/interpreter/mterp/arm/op_return_wide.S b/runtime/interpreter/mterp/arm/op_return_wide.S index 2881c87e58..cfab5301e5 100644 --- a/runtime/interpreter/mterp/arm/op_return_wide.S +++ b/runtime/interpreter/mterp/arm/op_return_wide.S @@ -4,6 +4,10 @@ /* return-wide vAA */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + 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] ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1 diff --git a/runtime/interpreter/mterp/config_x86 b/runtime/interpreter/mterp/config_x86 index 277817d92e..5fab379f8d 100644 --- a/runtime/interpreter/mterp/config_x86 +++ b/runtime/interpreter/mterp/config_x86 @@ -36,262 +36,262 @@ op-start x86 # (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/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 9975458b85..0afd2765db 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -274,13 +274,15 @@ extern "C" bool MterpConstClass(uint32_t index, uint32_t tgt_vreg, ShadowFrame* return false; } -extern "C" bool MterpCheckCast(uint32_t index, Object* obj, art::ArtMethod* method, - Thread* self) +extern "C" bool MterpCheckCast(uint32_t index, StackReference<mirror::Object>* vreg_addr, + art::ArtMethod* method, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { Class* c = ResolveVerifyAndClinit(index, method, self, false, false); if (UNLIKELY(c == nullptr)) { return true; } + // Must load obj from vreg following ResolveVerifyAndClinit due to moving gc. + Object* obj = vreg_addr->AsMirrorPtr(); if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) { ThrowClassCastException(c, obj->GetClass()); return true; @@ -288,13 +290,15 @@ extern "C" bool MterpCheckCast(uint32_t index, Object* obj, art::ArtMethod* meth return false; } -extern "C" bool MterpInstanceOf(uint32_t index, Object* obj, art::ArtMethod* method, - Thread* self) +extern "C" bool MterpInstanceOf(uint32_t index, StackReference<mirror::Object>* vreg_addr, + art::ArtMethod* method, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { Class* c = ResolveVerifyAndClinit(index, method, self, false, false); if (UNLIKELY(c == nullptr)) { return false; // Caller will check for pending exception. Return value unimportant. } + // Must load obj from vreg following ResolveVerifyAndClinit due to moving gc. + Object* obj = vreg_addr->AsMirrorPtr(); return (obj != nullptr) && obj->InstanceOf(c); } @@ -505,8 +509,7 @@ extern "C" int artSet64IndirectStaticFromMterp(uint32_t field_idx, ArtMethod* re uint64_t* new_value, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); - ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, - sizeof(int64_t)); + ArtField* field = FindFieldFast(field_idx, referrer, StaticPrimitiveWrite, sizeof(int64_t)); if (LIKELY(field != nullptr)) { // Compiled code can't use transactional mode. field->Set64<false>(field->GetDeclaringClass(), *new_value); @@ -524,8 +527,7 @@ extern "C" int artSet64IndirectStaticFromMterp(uint32_t field_idx, ArtMethod* re extern "C" int artSet8InstanceFromMterp(uint32_t field_idx, mirror::Object* obj, uint8_t new_value, ArtMethod* referrer) SHARED_REQUIRES(Locks::mutator_lock_) { - ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, - sizeof(int8_t)); + ArtField* field = FindFieldFast(field_idx, referrer, InstancePrimitiveWrite, sizeof(int8_t)); if (LIKELY(field != nullptr && obj != nullptr)) { Primitive::Type type = field->GetTypeAsPrimitiveType(); if (type == Primitive::kPrimBoolean) { @@ -582,7 +584,7 @@ extern "C" int artSet64InstanceFromMterp(uint32_t field_idx, mirror::Object* obj } extern "C" int artSetObjInstanceFromMterp(uint32_t field_idx, mirror::Object* obj, - mirror::Object* new_value, ArtMethod* referrer) + mirror::Object* new_value, ArtMethod* referrer) SHARED_REQUIRES(Locks::mutator_lock_) { ArtField* field = FindFieldFast(field_idx, referrer, InstanceObjectWrite, sizeof(mirror::HeapReference<mirror::Object>)); diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 9ae98a2bbe..78c784b773 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -599,6 +599,10 @@ artMterpAsmInstructionStart = .L_op_nop /* File: arm/op_return_void.S */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 b MterpReturn @@ -615,6 +619,10 @@ artMterpAsmInstructionStart = .L_op_nop /* op vAA */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA GET_VREG r0, r2 @ r0<- vAA mov r1, #0 @@ -630,6 +638,10 @@ artMterpAsmInstructionStart = .L_op_nop /* return-wide vAA */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + 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] ldmia r2, {r0-r1} @ r0/r1 <- vAA/vAA+1 @@ -648,6 +660,10 @@ artMterpAsmInstructionStart = .L_op_nop /* op vAA */ .extern MterpThreadFenceForConstructor bl MterpThreadFenceForConstructor + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + blne MterpSuspendCheck @ (self) mov r2, rINST, lsr #8 @ r2<- AA GET_VREG r0, r2 @ r0<- vAA mov r1, #0 @@ -878,10 +894,10 @@ artMterpAsmInstructionStart = .L_op_nop EXPORT_PC FETCH r0, 1 @ r0<- BBBB mov r1, rINST, lsr #8 @ r1<- AA - GET_VREG r1, r1 @ r1<- object + VREG_INDEX_TO_ADDR r1, r1 @ r1<- &object ldr r2, [rFP, #OFF_FP_METHOD] @ r2<- method mov r3, rSELF @ r3<- self - bl MterpCheckCast @ (index, obj, method, self) + bl MterpCheckCast @ (index, &obj, method, self) PREFETCH_INST 2 cmp r0, #0 bne MterpPossibleException @@ -903,12 +919,12 @@ artMterpAsmInstructionStart = .L_op_nop EXPORT_PC FETCH r0, 1 @ r0<- CCCC mov r1, rINST, lsr #12 @ r1<- B - GET_VREG r1, r1 @ r1<- vB (object) + 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) + bl MterpInstanceOf @ (index, &obj, method, self) ldr r1, [rSELF, #THREAD_EXCEPTION_OFFSET] PREFETCH_INST 2 cmp r1, #0 @ exception pending? @@ -3385,6 +3401,10 @@ artMterpAsmInstructionStart = .L_op_nop .balign 128 .L_op_return_void_no_barrier: /* 0x73 */ /* File: arm/op_return_void_no_barrier.S */ + ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] + mov r0, rSELF + ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) + blne MterpSuspendCheck @ (self) mov r0, #0 mov r1, #0 b MterpReturn @@ -12183,12 +12203,8 @@ MterpExceptionReturn: b MterpDone MterpReturn: ldr r2, [rFP, #OFF_FP_RESULT_REGISTER] - ldr lr, [rSELF, #THREAD_FLAGS_OFFSET] str r0, [r2] str r1, [r2, #4] - mov r0, rSELF - ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST) - blne MterpSuspendCheck @ (self) mov r0, #1 @ signal return to caller. MterpDone: add sp, sp, #4 @ un-align 64 diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S new file mode 100644 index 0000000000..e2918dcfb2 --- /dev/null +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -0,0 +1,12969 @@ +/* + * This file was generated automatically by gen-mterp.py for 'x86'. + * + * --> DO NOT EDIT <-- + */ + +/* File: x86/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 ABI general notes: + +Caller save set: + eax, edx, ecx, st(0)-st(7) +Callee save set: + ebx, esi, edi, ebp +Return regs: + 32-bit in eax + 64-bit in edx:eax (low-order 32 in eax) + fp on top of fp stack st(0) + +Parameters passed on stack, pushed right-to-left. On entry to target, first +parm is at 4(%esp). Traditional entry code is: + +functEntry: + push %ebp # save old frame pointer + mov %ebp,%esp # establish new frame pointer + sub FrameSize,%esp # Allocate storage for spill, locals & outs + +Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp) + +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 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rPC esi interpreted program counter, used for fetching instructions + rFP edi 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 edx base of instruction handler table + rREFS ebp 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" + +/* Frame size must be 16-byte aligned. + * Remember about 4 bytes for return address + */ +#define FRAME_SIZE 44 + +/* 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) +/* 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) +/* Out Arg offsets, relative to %esp */ +#define OUT_ARG3 ( 12) +#define OUT_ARG2 ( 8) +#define OUT_ARG1 ( 4) +#define OUT_ARG0 ( 0) /* <- ExecuteMterpImpl esp + 0 */ + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF IN_ARG0(%esp) +#define rPC %esi +#define rFP %edi +#define rINST %ebx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %edx +#define rREFS %ebp + +/* + * 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) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * 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 + movl 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. + * + * TODO: Consider spilling the IBase instead of restoring it from Thread structure. + */ +.macro REFRESH_IBASE + movl rSELF, rIBASE + movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE +.endm + +/* + * If rSELF is already loaded then we can use it from known reg. + */ +.macro REFRESH_IBASE_FROM_SELF _reg + movl THREAD_CURRENT_IBASE_OFFSET(\_reg), 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 + movzwl (rPC), rINST +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll $7, %eax + addl rIBASE, %eax + jmp *%eax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leal 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_HIGH_ADDRESS(_vreg) 4(rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) +#define VREG_REF_HIGH_ADDRESS(_vreg) 4(rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value to xmm. */ +.macro GET_WIDE_FP_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl $0, (rREFS,\_vreg,4) +.endm + +/* Write wide value from xmm. xmm is clobbered. */ +.macro SET_WIDE_FP_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + pxor \_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 $0, 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl $0, (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl $0, (rREFS,\_vreg,4) + movl $0, 4(rREFS,\_vreg,4) +.endm + +/* File: x86/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 ExecuteMterpImpl + .type ExecuteMterpImpl, %function + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +ExecuteMterpImpl: + .cfi_startproc + /* 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 + + /* Remember the return register */ + movl IN_ARG3(%esp), %eax + movl %eax, SHADOWFRAME_RESULT_REGISTER_OFFSET(%edx) + + /* Remember the code_item */ + movl IN_ARG1(%esp), %ecx + movl %ecx, SHADOWFRAME_CODE_ITEM_OFFSET(%edx) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(%edx), %eax + leal SHADOWFRAME_VREGS_OFFSET(%edx), rFP + leal (rFP, %eax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax + lea CODEITEM_INSNS_OFFSET(%ecx), rPC + lea (rPC, %eax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ + + + .global artMterpAsmInstructionStart + .type artMterpAsmInstructionStart, %function +artMterpAsmInstructionStart = .L_op_nop + .text + +/* ------------------------------ */ + .balign 128 +.L_op_nop: /* 0x00 */ +/* File: x86/op_nop.S */ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move: /* 0x01 */ +/* File: x86/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movzbl rINSTbl, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG rINST rINST + .if 0 + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_from16: /* 0x02 */ +/* File: x86/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzx rINSTbl, %eax # eax <- AA + movw 2(rPC), rINSTw # rINSTw <- BBBB + GET_VREG rINST rINST # rINST <- fp[BBBB] + .if 0 + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_16: /* 0x03 */ +/* File: x86/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwl 4(rPC), %ecx # ecx <- BBBB + movzwl 2(rPC), %eax # eax <- AAAA + GET_VREG rINST %ecx + .if 0 + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide: /* 0x04 */ +/* File: x86/op_move_wide.S */ + /* move-wide vA, vB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, rINST # rINST <- B + andb $0xf, %cl # ecx <- A + GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- v[B] + SET_WIDE_FP_VREG %xmm0 %ecx # v[A] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_from16: /* 0x05 */ +/* File: x86/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 + movzbl rINSTbl, %eax # eax <- AAAA + GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B] + SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_move_wide_16: /* 0x06 */ +/* File: x86/op_move_wide_16.S */ + /* move-wide/16 vAAAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 4(rPC), %ecx # ecx<- BBBB + movzwl 2(rPC), %eax # eax<- AAAA + GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B] + SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_move_object: /* 0x07 */ +/* File: x86/op_move_object.S */ +/* File: x86/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movzbl rINSTbl, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG rINST rINST + .if 1 + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_from16: /* 0x08 */ +/* File: x86/op_move_object_from16.S */ +/* File: x86/op_move_from16.S */ + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzx rINSTbl, %eax # eax <- AA + movw 2(rPC), rINSTw # rINSTw <- BBBB + GET_VREG rINST rINST # rINST <- fp[BBBB] + .if 1 + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_object_16: /* 0x09 */ +/* File: x86/op_move_object_16.S */ +/* File: x86/op_move_16.S */ + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwl 4(rPC), %ecx # ecx <- BBBB + movzwl 2(rPC), %eax # eax <- AAAA + GET_VREG rINST %ecx + .if 1 + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_result: /* 0x0a */ +/* File: x86/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType. + movl (%eax), %eax # r0 <- result.i. + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <- fp[B] + .else + SET_VREG %eax rINST # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_wide: /* 0x0b */ +/* File: x86/op_move_result_wide.S */ + /* move-result-wide vAA */ + movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType. + movl 4(%eax), %ecx # Get high + movl (%eax), %eax # Get low + SET_VREG %eax rINST # v[AA+0] <- eax + SET_VREG_HIGH %ecx rINST # v[AA+1] <- ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_move_result_object: /* 0x0c */ +/* File: x86/op_move_result_object.S */ +/* File: x86/op_move_result.S */ + /* for: move-result, move-result-object */ + /* op vAA */ + movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType. + movl (%eax), %eax # r0 <- result.i. + .if 1 + SET_VREG_OBJECT %eax rINST # fp[A] <- fp[B] + .else + SET_VREG %eax rINST # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_move_exception: /* 0x0d */ +/* File: x86/op_move_exception.S */ + /* move-exception vAA */ + movl rSELF, %ecx + movl THREAD_EXCEPTION_OFFSET(%ecx), %eax + SET_VREG_OBJECT %eax rINST # fp[AA] <- exception object + movl $0, THREAD_EXCEPTION_OFFSET(%ecx) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_return_void: /* 0x0e */ +/* File: x86/op_return_void.S */ + .extern MterpThreadFenceForConstructor + call MterpThreadFenceForConstructor + movl rSELF, %eax + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + xorl %eax, %eax + xorl %ecx, %ecx + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return: /* 0x0f */ +/* File: x86/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call MterpThreadFenceForConstructor + movl rSELF, %eax + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + GET_VREG %eax rINST # eax <- vAA + xorl %ecx, %ecx + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_wide: /* 0x10 */ +/* File: x86/op_return_wide.S */ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call MterpThreadFenceForConstructor + movl rSELF, %eax + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + GET_VREG %eax rINST # eax <- v[AA+0] + GET_VREG_HIGH %ecx rINST # ecx <- v[AA+1] + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_return_object: /* 0x11 */ +/* File: x86/op_return_object.S */ +/* File: x86/op_return.S */ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call MterpThreadFenceForConstructor + movl rSELF, %eax + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + GET_VREG %eax rINST # eax <- vAA + xorl %ecx, %ecx + jmp MterpReturn + + +/* ------------------------------ */ + .balign 128 +.L_op_const_4: /* 0x12 */ +/* File: x86/op_const_4.S */ + /* const/4 vA, #+B */ + movsx rINSTbl, %eax # eax <-ssssssBx + movl $0xf, rINST + andl %eax, rINST # rINST <- A + sarl $4, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_const_16: /* 0x13 */ +/* File: x86/op_const_16.S */ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx rINST # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const: /* 0x14 */ +/* File: x86/op_const.S */ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax rINST # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_high16: /* 0x15 */ +/* File: x86/op_const_high16.S */ + /* const/high16 vAA, #+BBBB0000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $16, %eax # eax <- BBBB0000 + SET_VREG %eax rINST # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_16: /* 0x16 */ +/* File: x86/op_const_wide_16.S */ + /* const-wide/16 vAA, #+BBBB */ + movswl 2(rPC), %eax # eax <- ssssBBBB + movl rIBASE, %ecx # preserve rIBASE (cltd trashes it) + cltd # rIBASE:eax <- ssssssssssssBBBB + SET_VREG_HIGH rIBASE rINST # store msw + SET_VREG %eax rINST # store lsw + movl %ecx, rIBASE # restore rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_32: /* 0x17 */ +/* File: x86/op_const_wide_32.S */ + /* const-wide/32 vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # eax <- BBBBbbbb + movl rIBASE, %ecx # preserve rIBASE (cltd trashes it) + cltd # rIBASE:eax <- ssssssssssssBBBB + SET_VREG_HIGH rIBASE rINST # store msw + SET_VREG %eax rINST # store lsw + movl %ecx, rIBASE # restore rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide: /* 0x18 */ +/* File: x86/op_const_wide.S */ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movl 2(rPC), %eax # eax <- lsw + movzbl rINSTbl, %ecx # ecx <- AA + movl 6(rPC), rINST # rINST <- msw + SET_VREG %eax %ecx + SET_VREG_HIGH rINST %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 + +/* ------------------------------ */ + .balign 128 +.L_op_const_wide_high16: /* 0x19 */ +/* File: x86/op_const_wide_high16.S */ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $16, %eax # eax <- BBBB0000 + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + xorl %eax, %eax + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string: /* 0x1a */ +/* File: x86/op_const_string.S */ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, OUT_ARG1(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpConstString # (index, tgt_reg, shadow_frame, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_const_string_jumbo: /* 0x1b */ +/* File: x86/op_const_string_jumbo.S */ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, OUT_ARG1(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpConstString # (index, tgt_reg, shadow_frame, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_const_class: /* 0x1c */ +/* File: x86/op_const_class.S */ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwl 2(rPC), %eax # eax<- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, OUT_ARG1(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpConstClass # (index, tgt_reg, shadow_frame, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_enter: /* 0x1d */ +/* File: x86/op_monitor_enter.S */ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG %ecx rINST + movl %ecx, OUT_ARG0(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG1(%esp) + call artLockObjectFromCode # (object, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_monitor_exit: /* 0x1e */ +/* File: x86/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 %ecx rINST + movl %ecx, OUT_ARG0(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG1(%esp) + call artUnlockObjectFromCode # (object, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_check_cast: /* 0x1f */ +/* File: x86/op_check_cast.S */ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + leal VREG_ADDRESS(rINST), %ecx + movl %ecx, OUT_ARG1(%esp) + movl OFF_FP_METHOD(rFP),%eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpCheckCast # (index, &obj, method, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_instance_of: /* 0x20 */ +/* File: x86/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), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, %eax # eax <- BA + sarl $4, %eax # eax <- B + leal VREG_ADDRESS(%eax), %ecx # Get object address + movl %ecx, OUT_ARG1(%esp) + movl OFF_FP_METHOD(rFP),%eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpInstanceOf # (index, &obj, method, self) + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + andb $0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_array_length: /* 0x21 */ +/* File: x86/op_array_length.S */ +/* + * Return the length of an array. + */ + mov rINST, %eax # eax <- BA + sarl $4, rINST # rINST <- B + GET_VREG %ecx rINST # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%ecx), rINST + SET_VREG rINST %eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_new_instance: /* 0x22 */ +/* File: x86/op_new_instance.S */ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG1(%esp) + REFRESH_INST 34 + movl rINST, OUT_ARG2(%esp) + call MterpNewInstance + REFRESH_IBASE + testl %eax, %eax # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_new_array: /* 0x23 */ +/* File: x86/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 + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST 35 + movl rINST, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpNewArray + REFRESH_IBASE + testl %eax, %eax # 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/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 + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) + call MterpFilledNewArray + REFRESH_IBASE + testl %eax, %eax # 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/op_filled_new_array_range.S */ +/* File: x86/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 + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) + call MterpFilledNewArrayRange + REFRESH_IBASE + testl %eax, %eax # 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/op_fill_array_data.S */ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2 + GET_VREG %eax rINST # eax <- vAA (array object) + movl %eax, OUT_ARG0(%esp) + movl %ecx, OUT_ARG1(%esp) + call MterpFillArrayData # (obj, payload) + REFRESH_IBASE + testl %eax, %eax # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* ------------------------------ */ + .balign 128 +.L_op_throw: /* 0x27 */ +/* File: x86/op_throw.S */ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax rINST # eax<- vAA (exception object) + testl %eax, %eax + jz common_errNullObject + movl rSELF,%ecx + movl %eax, THREAD_EXCEPTION_OFFSET(%ecx) + jmp MterpException + +/* ------------------------------ */ + .balign 128 +.L_op_goto: /* 0x28 */ +/* File: x86/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 */ + movsbl rINSTbl, %eax # eax <- ssssssAA + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_16: /* 0x29 */ +/* File: x86/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 */ + movswl 2(rPC), %eax # eax <- ssssAAAA + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_goto_32: /* 0x2a */ +/* File: x86/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". Because + * we need the V bit set, we'll use an adds to convert from Dalvik + * offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movl 2(rPC), %eax # eax <- AAAAAAAA + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_packed_switch: /* 0x2b */ +/* File: x86/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 */ + movl 2(rPC), %ecx # ecx <- BBBBbbbb + GET_VREG %eax rINST # eax <- vAA + leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2 + movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA + movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData + call MterpDoPackedSwitch + addl %eax, %eax + leal (rPC, %eax), rPC + FETCH_INST + REFRESH_IBASE + jg 1f +#if MTERP_SUSPEND + # REFRESH_IBASE - we did it above. +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + +/* ------------------------------ */ + .balign 128 +.L_op_sparse_switch: /* 0x2c */ +/* File: x86/op_sparse_switch.S */ +/* File: x86/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 */ + movl 2(rPC), %ecx # ecx <- BBBBbbbb + GET_VREG %eax rINST # eax <- vAA + leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2 + movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA + movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData + call MterpDoSparseSwitch + addl %eax, %eax + leal (rPC, %eax), rPC + FETCH_INST + REFRESH_IBASE + jg 1f +#if MTERP_SUSPEND + # REFRESH_IBASE - we did it above. +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_float: /* 0x2d */ +/* File: x86/op_cmpl_float.S */ +/* File: x86/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 */ + movzbl 3(rPC), %ecx # ecx<- CC + movzbl 2(rPC), %eax # eax<- BB + movss VREG_ADDRESS(%eax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%ecx), %xmm0 + jp .Lop_cmpl_float_nan_is_neg + je .Lop_cmpl_float_finish + jb .Lop_cmpl_float_less +.Lop_cmpl_float_nan_is_pos: + incl %eax + jmp .Lop_cmpl_float_finish +.Lop_cmpl_float_nan_is_neg: +.Lop_cmpl_float_less: + decl %eax +.Lop_cmpl_float_finish: + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_float: /* 0x2e */ +/* File: x86/op_cmpg_float.S */ +/* File: x86/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 */ + movzbl 3(rPC), %ecx # ecx<- CC + movzbl 2(rPC), %eax # eax<- BB + movss VREG_ADDRESS(%eax), %xmm0 + xor %eax, %eax + ucomiss VREG_ADDRESS(%ecx), %xmm0 + jp .Lop_cmpg_float_nan_is_pos + je .Lop_cmpg_float_finish + jb .Lop_cmpg_float_less +.Lop_cmpg_float_nan_is_pos: + incl %eax + jmp .Lop_cmpg_float_finish +.Lop_cmpg_float_nan_is_neg: +.Lop_cmpg_float_less: + decl %eax +.Lop_cmpg_float_finish: + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpl_double: /* 0x2f */ +/* File: x86/op_cmpl_double.S */ +/* File: x86/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 */ + movzbl 3(rPC), %ecx # ecx<- CC + movzbl 2(rPC), %eax # eax<- BB + movsd VREG_ADDRESS(%eax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%ecx), %xmm0 + jp .Lop_cmpl_double_nan_is_neg + je .Lop_cmpl_double_finish + jb .Lop_cmpl_double_less +.Lop_cmpl_double_nan_is_pos: + incl %eax + jmp .Lop_cmpl_double_finish +.Lop_cmpl_double_nan_is_neg: +.Lop_cmpl_double_less: + decl %eax +.Lop_cmpl_double_finish: + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmpg_double: /* 0x30 */ +/* File: x86/op_cmpg_double.S */ +/* File: x86/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 */ + movzbl 3(rPC), %ecx # ecx<- CC + movzbl 2(rPC), %eax # eax<- BB + movsd VREG_ADDRESS(%eax), %xmm0 + xor %eax, %eax + ucomisd VREG_ADDRESS(%ecx), %xmm0 + jp .Lop_cmpg_double_nan_is_pos + je .Lop_cmpg_double_finish + jb .Lop_cmpg_double_less +.Lop_cmpg_double_nan_is_pos: + incl %eax + jmp .Lop_cmpg_double_finish +.Lop_cmpg_double_nan_is_neg: +.Lop_cmpg_double_less: + decl %eax +.Lop_cmpg_double_finish: + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_cmp_long: /* 0x31 */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG_HIGH %eax %eax # eax <- v[BB+1], BB is clobbered + cmpl VREG_HIGH_ADDRESS(%ecx), %eax + jl .Lop_cmp_long_smaller + jg .Lop_cmp_long_bigger + movzbl 2(rPC), %eax # eax <- BB, restore BB + GET_VREG %eax %eax # eax <- v[BB] + sub VREG_ADDRESS(%ecx), %eax + ja .Lop_cmp_long_bigger + jb .Lop_cmp_long_smaller +.Lop_cmp_long_finish: + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.Lop_cmp_long_bigger: + movl $1, %eax + jmp .Lop_cmp_long_finish + +.Lop_cmp_long_smaller: + movl $-1, %eax + jmp .Lop_cmp_long_finish + +/* ------------------------------ */ + .balign 128 +.L_op_if_eq: /* 0x32 */ +/* File: x86/op_if_eq.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + andb $0xf, %cl # ecx <- A + 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 + jne 1f + movswl 2(rPC),%eax # Get signed branch offset +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ne: /* 0x33 */ +/* File: x86/op_if_ne.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + andb $0xf, %cl # ecx <- A + 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 + je 1f + movswl 2(rPC),%eax # Get signed branch offset +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lt: /* 0x34 */ +/* File: x86/op_if_lt.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + andb $0xf, %cl # ecx <- A + 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 + jge 1f + movswl 2(rPC),%eax # Get signed branch offset +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ge: /* 0x35 */ +/* File: x86/op_if_ge.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + andb $0xf, %cl # ecx <- A + 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 + jl 1f + movswl 2(rPC),%eax # Get signed branch offset +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gt: /* 0x36 */ +/* File: x86/op_if_gt.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + andb $0xf, %cl # ecx <- A + 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 + jle 1f + movswl 2(rPC),%eax # Get signed branch offset +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_le: /* 0x37 */ +/* File: x86/op_if_le.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + andb $0xf, %cl # ecx <- A + 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 + jg 1f + movswl 2(rPC),%eax # Get signed branch offset +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_eqz: /* 0x38 */ +/* File: x86/op_if_eqz.S */ +/* File: x86/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(rINST) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jne 1f + movswl 2(rPC),%eax # fetch signed displacement +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_nez: /* 0x39 */ +/* File: x86/op_if_nez.S */ +/* File: x86/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(rINST) # compare (vA, 0) + movl $2, %eax # assume branch not taken + je 1f + movswl 2(rPC),%eax # fetch signed displacement +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_ltz: /* 0x3a */ +/* File: x86/op_if_ltz.S */ +/* File: x86/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(rINST) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jge 1f + movswl 2(rPC),%eax # fetch signed displacement +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gez: /* 0x3b */ +/* File: x86/op_if_gez.S */ +/* File: x86/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(rINST) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jl 1f + movswl 2(rPC),%eax # fetch signed displacement +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_gtz: /* 0x3c */ +/* File: x86/op_if_gtz.S */ +/* File: x86/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(rINST) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jle 1f + movswl 2(rPC),%eax # fetch signed displacement +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_if_lez: /* 0x3d */ +/* File: x86/op_if_lez.S */ +/* File: x86/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(rINST) # compare (vA, 0) + movl $2, %eax # assume branch not taken + jg 1f + movswl 2(rPC),%eax # fetch signed displacement +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3e: /* 0x3e */ +/* File: x86/op_unused_3e.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_3f: /* 0x3f */ +/* File: x86/op_unused_3f.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_40: /* 0x40 */ +/* File: x86/op_unused_40.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_41: /* 0x41 */ +/* File: x86/op_unused_41.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_42: /* 0x42 */ +/* File: x86/op_unused_42.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_43: /* 0x43 */ +/* File: x86/op_unused_43.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_aget: /* 0x44 */ +/* File: x86/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + movl MIRROR_INT_ARRAY_DATA_OFFSET(%eax,%ecx,4), %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_wide: /* 0x45 */ +/* File: x86/op_aget_wide.S */ +/* + * Array get, 64 bits. vAA <- vBB[vCC]. + */ + /* aget-wide vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax + movq (%eax), %xmm0 # xmm0 <- vBB[vCC] + SET_WIDE_FP_VREG %xmm0 rINST # vAA <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_object: /* 0x46 */ +/* File: x86/op_aget_object.S */ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # ecs <- vCC (requested index) + EXPORT_PC + movl %eax, OUT_ARG0(%esp) + movl %ecx, OUT_ARG1(%esp) + call artAGetObjectFromMterp # (array, index) + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + SET_VREG_OBJECT %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aget_boolean: /* 0x47 */ +/* File: x86/op_aget_boolean.S */ +/* File: x86/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + movzbl MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_byte: /* 0x48 */ +/* File: x86/op_aget_byte.S */ +/* File: x86/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + movsbl MIRROR_BYTE_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_char: /* 0x49 */ +/* File: x86/op_aget_char.S */ +/* File: x86/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + movzwl MIRROR_CHAR_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aget_short: /* 0x4a */ +/* File: x86/op_aget_short.S */ +/* File: x86/op_aget.S */ +/* + * Array get, 32 bits or less. vAA <- vBB[vCC]. + * + * for: aget, aget-boolean, aget-byte, aget-char, aget-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + movswl MIRROR_SHORT_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput: /* 0x4b */ +/* File: x86/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_INT_ARRAY_DATA_OFFSET(%eax,%ecx,4), %eax + GET_VREG rINST rINST + movl rINST, (%eax) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_wide: /* 0x4c */ +/* File: x86/op_aput_wide.S */ +/* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + */ + /* aput-wide vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax + GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- vAA + movq %xmm0, (%eax) # vBB[vCC] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_object: /* 0x4d */ +/* File: x86/op_aput_object.S */ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST 77 + movl rINST, OUT_ARG2(%esp) + call MterpAputObject # (array, index) + REFRESH_IBASE + testl %eax, %eax + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_aput_boolean: /* 0x4e */ +/* File: x86/op_aput_boolean.S */ +/* File: x86/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_BOOLEAN_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax + GET_VREG rINST rINST + movb rINSTbl, (%eax) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_byte: /* 0x4f */ +/* File: x86/op_aput_byte.S */ +/* File: x86/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_BYTE_ARRAY_DATA_OFFSET(%eax,%ecx,1), %eax + GET_VREG rINST rINST + movb rINSTbl, (%eax) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_char: /* 0x50 */ +/* File: x86/op_aput_char.S */ +/* File: x86/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_CHAR_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax + GET_VREG rINST rINST + movw rINSTw, (%eax) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_aput_short: /* 0x51 */ +/* File: x86/op_aput_short.S */ +/* File: x86/op_aput.S */ +/* + * Array put, 32 bits or less. vBB[vCC] <- vAA. + * + * for: aput, aput-boolean, aput-byte, aput-char, aput-short + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_SHORT_ARRAY_DATA_OFFSET(%eax,%ecx,2), %eax + GET_VREG rINST rINST + movw rINSTw, (%eax) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget: /* 0x52 */ +/* File: x86/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGet32InstanceFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <-value + .else + SET_VREG %eax rINST # fp[A] <-value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide: /* 0x53 */ +/* File: x86/op_iget_wide.S */ +/* + * 64-bit instance field get. + * + * for: iget-wide + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGet64InstanceFromCode + mov rSELF, %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + SET_VREG %eax rINST + SET_VREG_HIGH %edx rINST + REFRESH_IBASE_FROM_SELF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object: /* 0x54 */ +/* File: x86/op_iget_object.S */ +/* File: x86/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGetObjInstanceFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 1 + SET_VREG_OBJECT %eax rINST # fp[A] <-value + .else + SET_VREG %eax rINST # fp[A] <-value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean: /* 0x55 */ +/* File: x86/op_iget_boolean.S */ +/* File: x86/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGetBooleanInstanceFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <-value + .else + SET_VREG %eax rINST # fp[A] <-value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte: /* 0x56 */ +/* File: x86/op_iget_byte.S */ +/* File: x86/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGetByteInstanceFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <-value + .else + SET_VREG %eax rINST # fp[A] <-value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char: /* 0x57 */ +/* File: x86/op_iget_char.S */ +/* File: x86/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGetCharInstanceFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <-value + .else + SET_VREG %eax rINST # fp[A] <-value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short: /* 0x58 */ +/* File: x86/op_iget_short.S */ +/* File: x86/op_iget.S */ +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGetShortInstanceFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf, rINSTbl # rINST <- A + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <-value + .else + SET_VREG %eax rINST # fp[A] <-value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput: /* 0x59 */ +/* File: x86/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), %eax # eax<- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG %eax, rINST + movl %eax, OUT_ARG2(%esp) # fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call artSet32InstanceFromMterp + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide: /* 0x5a */ +/* File: x86/op_iput_wide.S */ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl,%ecx # ecx <- BA + sarl $4,%ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $0xf,rINSTbl # rINST <- A + leal VREG_ADDRESS(rINST), %eax + movl %eax, OUT_ARG2(%esp) # &fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call artSet64InstanceFromMterp + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object: /* 0x5b */ +/* File: x86/op_iput_object.S */ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST 91 + movl rINST, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpIputObject + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean: /* 0x5c */ +/* File: x86/op_iput_boolean.S */ +/* File: x86/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), %eax # eax<- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG %eax, rINST + movl %eax, OUT_ARG2(%esp) # fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call artSet8InstanceFromMterp + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte: /* 0x5d */ +/* File: x86/op_iput_byte.S */ +/* File: x86/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), %eax # eax<- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG %eax, rINST + movl %eax, OUT_ARG2(%esp) # fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call artSet8InstanceFromMterp + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char: /* 0x5e */ +/* File: x86/op_iput_char.S */ +/* File: x86/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), %eax # eax<- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG %eax, rINST + movl %eax, OUT_ARG2(%esp) # fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call artSet16InstanceFromMterp + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short: /* 0x5f */ +/* File: x86/op_iput_short.S */ +/* File: x86/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), %eax # eax<- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $0xf, rINSTbl # rINST<- A + GET_VREG %eax, rINST + movl %eax, OUT_ARG2(%esp) # fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call artSet16InstanceFromMterp + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget: /* 0x60 */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGet32StaticFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <- value + .else + SET_VREG %eax rINST # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sget_wide: /* 0x61 */ +/* File: x86/op_sget_wide.S */ +/* + * SGET_WIDE handler wrapper. + * + */ + /* sget-wide vAA, field@BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGet64StaticFromCode + movl rSELF, %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + SET_VREG %eax rINST # fp[A]<- low part + SET_VREG_HIGH %edx rINST # fp[A+1]<- high part + REFRESH_IBASE_FROM_SELF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sget_object: /* 0x62 */ +/* File: x86/op_sget_object.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGetObjStaticFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + .if 1 + SET_VREG_OBJECT %eax rINST # fp[A] <- value + .else + SET_VREG %eax rINST # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_boolean: /* 0x63 */ +/* File: x86/op_sget_boolean.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGetBooleanStaticFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <- value + .else + SET_VREG %eax rINST # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_byte: /* 0x64 */ +/* File: x86/op_sget_byte.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGetByteStaticFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <- value + .else + SET_VREG %eax rINST # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_char: /* 0x65 */ +/* File: x86/op_sget_char.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGetCharStaticFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <- value + .else + SET_VREG %eax rINST # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sget_short: /* 0x66 */ +/* File: x86/op_sget_short.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGetShortStaticFromCode + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + .if 0 + SET_VREG_OBJECT %eax rINST # fp[A] <- value + .else + SET_VREG %eax rINST # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput: /* 0x67 */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + GET_VREG rINST rINST + movl rINST, OUT_ARG1(%esp) # fp[AA] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artSet32StaticFromCode + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_wide: /* 0x68 */ +/* File: x86/op_sput_wide.S */ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + leal VREG_ADDRESS(rINST), %eax + movl %eax, OUT_ARG2(%esp) # &fp[AA] + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artSet64IndirectStaticFromMterp + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_object: /* 0x69 */ +/* File: x86/op_sput_object.S */ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST 105 + movl rINST, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpSputObject + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_sput_boolean: /* 0x6a */ +/* File: x86/op_sput_boolean.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + GET_VREG rINST rINST + movl rINST, OUT_ARG1(%esp) # fp[AA] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artSet8StaticFromCode + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_byte: /* 0x6b */ +/* File: x86/op_sput_byte.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + GET_VREG rINST rINST + movl rINST, OUT_ARG1(%esp) # fp[AA] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artSet8StaticFromCode + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_char: /* 0x6c */ +/* File: x86/op_sput_char.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + GET_VREG rINST rINST + movl rINST, OUT_ARG1(%esp) # fp[AA] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artSet16StaticFromCode + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sput_short: /* 0x6d */ +/* File: x86/op_sput_short.S */ +/* File: x86/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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + GET_VREG rINST rINST + movl rINST, OUT_ARG1(%esp) # fp[AA] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artSet16StaticFromCode + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual: /* 0x6e */ +/* File: x86/op_invoke_virtual.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 110 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeVirtual + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * 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/op_invoke_super.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 111 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeSuper + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * 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/op_invoke_direct.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 112 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeDirect + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static: /* 0x71 */ +/* File: x86/op_invoke_static.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 113 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeStatic + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface: /* 0x72 */ +/* File: x86/op_invoke_interface.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 114 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeInterface + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + +/* + * 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/op_return_void_no_barrier.S */ + movl rSELF, %eax + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + xorl %eax, %eax + xorl %ecx, %ecx + jmp MterpReturn + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range: /* 0x74 */ +/* File: x86/op_invoke_virtual_range.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 116 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeVirtualRange + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_super_range: /* 0x75 */ +/* File: x86/op_invoke_super_range.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 117 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeSuperRange + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_direct_range: /* 0x76 */ +/* File: x86/op_invoke_direct_range.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 118 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeDirectRange + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_static_range: /* 0x77 */ +/* File: x86/op_invoke_static_range.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 119 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeStaticRange + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_interface_range: /* 0x78 */ +/* File: x86/op_invoke_interface_range.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 120 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeInterfaceRange + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_79: /* 0x79 */ +/* File: x86/op_unused_79.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_7a: /* 0x7a */ +/* File: x86/op_unused_7a.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_int: /* 0x7b */ +/* File: x86/op_neg_int.S */ +/* File: x86/unop.S */ +/* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movzbl rINSTbl,%ecx # ecx <- A+ + sarl $4,rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf,%cl # ecx <- A + negl %eax + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_int: /* 0x7c */ +/* File: x86/op_not_int.S */ +/* File: x86/unop.S */ +/* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movzbl rINSTbl,%ecx # ecx <- A+ + sarl $4,rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf,%cl # ecx <- A + notl %eax + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_long: /* 0x7d */ +/* File: x86/op_neg_long.S */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax %ecx # eax <- v[B+0] + GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1] + negl %eax + adcl $0, %ecx + negl %ecx + SET_VREG %eax rINST # v[A+0] <- eax + SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_not_long: /* 0x7e */ +/* File: x86/op_not_long.S */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax %ecx # eax <- v[B+0] + GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1] + notl %eax + notl %ecx + SET_VREG %eax rINST # v[A+0] <- eax + SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_neg_float: /* 0x7f */ +/* File: x86/op_neg_float.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + flds VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + fchs + fstps VREG_ADDRESS(%ecx) # vA <- %st0 + .if 0 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_neg_double: /* 0x80 */ +/* File: x86/op_neg_double.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + fldl VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + fchs + fstpl VREG_ADDRESS(%ecx) # vA <- %st0 + .if 1 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_long: /* 0x81 */ +/* File: x86/op_int_to_long.S */ + /* int to long vA, vB */ + movzbl rINSTbl, %eax # eax <- +A + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + andb $0xf, rINSTbl # rINST <- A + movl rIBASE, %ecx # cltd trashes rIBASE/edx + cltd # rINST:eax<- sssssssBBBBBBBB + SET_VREG_HIGH rIBASE rINST # v[A+1] <- rIBASE + SET_VREG %eax rINST # v[A+0] <- %eax + movl %ecx, rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_float: /* 0x82 */ +/* File: x86/op_int_to_float.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + fildl VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + + fstps VREG_ADDRESS(%ecx) # vA <- %st0 + .if 0 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_double: /* 0x83 */ +/* File: x86/op_int_to_double.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + fildl VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + + fstpl VREG_ADDRESS(%ecx) # vA <- %st0 + .if 1 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_int: /* 0x84 */ +/* File: x86/op_long_to_int.S */ +/* we ignore the high word, making this equivalent to a 32-bit reg move */ +/* File: x86/op_move.S */ + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movzbl rINSTbl, %eax # eax <- BA + andb $0xf, %al # eax <- A + shrl $4, rINST # rINST <- B + GET_VREG rINST rINST + .if 0 + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_float: /* 0x85 */ +/* File: x86/op_long_to_float.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + fildll VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + + fstps VREG_ADDRESS(%ecx) # vA <- %st0 + .if 0 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_long_to_double: /* 0x86 */ +/* File: x86/op_long_to_double.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + fildll VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + + fstpl VREG_ADDRESS(%ecx) # vA <- %st0 + .if 1 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_int: /* 0x87 */ +/* File: x86/op_float_to_int.S */ +/* File: x86/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. This model + * differs from what is delivered normally via the x86 fpu, so we have + * to play some games. + */ + /* float/double to int/long vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + .if 0 + fldl VREG_ADDRESS(rINST) # %st0 <- vB + .else + flds VREG_ADDRESS(rINST) # %st0 <- vB + .endif + ftst + fnstcw LOCAL0(%esp) # remember original rounding mode + movzwl LOCAL0(%esp), %eax + movb $0xc, %ah + movw %ax, LOCAL0+2(%esp) + fldcw LOCAL0+2(%esp) # set "to zero" rounding mode + andb $0xf, %cl # ecx <- A + .if 0 + fistpll VREG_ADDRESS(%ecx) # convert and store + .else + fistpl VREG_ADDRESS(%ecx) # convert and store + .endif + fldcw LOCAL0(%esp) # restore previous rounding mode + .if 0 + movl $0x80000000, %eax + xorl VREG_HIGH_ADDRESS(%ecx), %eax + orl VREG_ADDRESS(%ecx), %eax + .else + cmpl $0x80000000, VREG_ADDRESS(%ecx) + .endif + je .Lop_float_to_int_special_case # fix up result + +.Lop_float_to_int_finish: + xor %eax, %eax + mov %eax, VREG_REF_ADDRESS(%ecx) + .if 0 + mov %eax, VREG_REF_HIGH_ADDRESS(%ecx) + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.Lop_float_to_int_special_case: + fnstsw %ax + sahf + jp .Lop_float_to_int_isNaN + adcl $-1, VREG_ADDRESS(%ecx) + .if 0 + adcl $-1, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_float_to_int_finish +.Lop_float_to_int_isNaN: + movl $0, VREG_ADDRESS(%ecx) + .if 0 + movl $0, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_float_to_int_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_long: /* 0x88 */ +/* File: x86/op_float_to_long.S */ +/* File: x86/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. This model + * differs from what is delivered normally via the x86 fpu, so we have + * to play some games. + */ + /* float/double to int/long vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + .if 0 + fldl VREG_ADDRESS(rINST) # %st0 <- vB + .else + flds VREG_ADDRESS(rINST) # %st0 <- vB + .endif + ftst + fnstcw LOCAL0(%esp) # remember original rounding mode + movzwl LOCAL0(%esp), %eax + movb $0xc, %ah + movw %ax, LOCAL0+2(%esp) + fldcw LOCAL0+2(%esp) # set "to zero" rounding mode + andb $0xf, %cl # ecx <- A + .if 1 + fistpll VREG_ADDRESS(%ecx) # convert and store + .else + fistpl VREG_ADDRESS(%ecx) # convert and store + .endif + fldcw LOCAL0(%esp) # restore previous rounding mode + .if 1 + movl $0x80000000, %eax + xorl VREG_HIGH_ADDRESS(%ecx), %eax + orl VREG_ADDRESS(%ecx), %eax + .else + cmpl $0x80000000, VREG_ADDRESS(%ecx) + .endif + je .Lop_float_to_long_special_case # fix up result + +.Lop_float_to_long_finish: + xor %eax, %eax + mov %eax, VREG_REF_ADDRESS(%ecx) + .if 1 + mov %eax, VREG_REF_HIGH_ADDRESS(%ecx) + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.Lop_float_to_long_special_case: + fnstsw %ax + sahf + jp .Lop_float_to_long_isNaN + adcl $-1, VREG_ADDRESS(%ecx) + .if 1 + adcl $-1, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_float_to_long_finish +.Lop_float_to_long_isNaN: + movl $0, VREG_ADDRESS(%ecx) + .if 1 + movl $0, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_float_to_long_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_float_to_double: /* 0x89 */ +/* File: x86/op_float_to_double.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + flds VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + + fstpl VREG_ADDRESS(%ecx) # vA <- %st0 + .if 1 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_int: /* 0x8a */ +/* File: x86/op_double_to_int.S */ +/* File: x86/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. This model + * differs from what is delivered normally via the x86 fpu, so we have + * to play some games. + */ + /* float/double to int/long vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + .if 1 + fldl VREG_ADDRESS(rINST) # %st0 <- vB + .else + flds VREG_ADDRESS(rINST) # %st0 <- vB + .endif + ftst + fnstcw LOCAL0(%esp) # remember original rounding mode + movzwl LOCAL0(%esp), %eax + movb $0xc, %ah + movw %ax, LOCAL0+2(%esp) + fldcw LOCAL0+2(%esp) # set "to zero" rounding mode + andb $0xf, %cl # ecx <- A + .if 0 + fistpll VREG_ADDRESS(%ecx) # convert and store + .else + fistpl VREG_ADDRESS(%ecx) # convert and store + .endif + fldcw LOCAL0(%esp) # restore previous rounding mode + .if 0 + movl $0x80000000, %eax + xorl VREG_HIGH_ADDRESS(%ecx), %eax + orl VREG_ADDRESS(%ecx), %eax + .else + cmpl $0x80000000, VREG_ADDRESS(%ecx) + .endif + je .Lop_double_to_int_special_case # fix up result + +.Lop_double_to_int_finish: + xor %eax, %eax + mov %eax, VREG_REF_ADDRESS(%ecx) + .if 0 + mov %eax, VREG_REF_HIGH_ADDRESS(%ecx) + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.Lop_double_to_int_special_case: + fnstsw %ax + sahf + jp .Lop_double_to_int_isNaN + adcl $-1, VREG_ADDRESS(%ecx) + .if 0 + adcl $-1, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_double_to_int_finish +.Lop_double_to_int_isNaN: + movl $0, VREG_ADDRESS(%ecx) + .if 0 + movl $0, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_double_to_int_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_long: /* 0x8b */ +/* File: x86/op_double_to_long.S */ +/* File: x86/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. This model + * differs from what is delivered normally via the x86 fpu, so we have + * to play some games. + */ + /* float/double to int/long vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + .if 1 + fldl VREG_ADDRESS(rINST) # %st0 <- vB + .else + flds VREG_ADDRESS(rINST) # %st0 <- vB + .endif + ftst + fnstcw LOCAL0(%esp) # remember original rounding mode + movzwl LOCAL0(%esp), %eax + movb $0xc, %ah + movw %ax, LOCAL0+2(%esp) + fldcw LOCAL0+2(%esp) # set "to zero" rounding mode + andb $0xf, %cl # ecx <- A + .if 1 + fistpll VREG_ADDRESS(%ecx) # convert and store + .else + fistpl VREG_ADDRESS(%ecx) # convert and store + .endif + fldcw LOCAL0(%esp) # restore previous rounding mode + .if 1 + movl $0x80000000, %eax + xorl VREG_HIGH_ADDRESS(%ecx), %eax + orl VREG_ADDRESS(%ecx), %eax + .else + cmpl $0x80000000, VREG_ADDRESS(%ecx) + .endif + je .Lop_double_to_long_special_case # fix up result + +.Lop_double_to_long_finish: + xor %eax, %eax + mov %eax, VREG_REF_ADDRESS(%ecx) + .if 1 + mov %eax, VREG_REF_HIGH_ADDRESS(%ecx) + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.Lop_double_to_long_special_case: + fnstsw %ax + sahf + jp .Lop_double_to_long_isNaN + adcl $-1, VREG_ADDRESS(%ecx) + .if 1 + adcl $-1, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_double_to_long_finish +.Lop_double_to_long_isNaN: + movl $0, VREG_ADDRESS(%ecx) + .if 1 + movl $0, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .Lop_double_to_long_finish + + +/* ------------------------------ */ + .balign 128 +.L_op_double_to_float: /* 0x8c */ +/* File: x86/op_double_to_float.S */ +/* File: x86/fpcvt.S */ +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + fldl VREG_ADDRESS(rINST) # %st0 <- vB + andb $0xf, %cl # ecx <- A + + fstps VREG_ADDRESS(%ecx) # vA <- %st0 + .if 0 + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_byte: /* 0x8d */ +/* File: x86/op_int_to_byte.S */ +/* File: x86/unop.S */ +/* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movzbl rINSTbl,%ecx # ecx <- A+ + sarl $4,rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf,%cl # ecx <- A + movsbl %al, %eax + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_char: /* 0x8e */ +/* File: x86/op_int_to_char.S */ +/* File: x86/unop.S */ +/* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movzbl rINSTbl,%ecx # ecx <- A+ + sarl $4,rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf,%cl # ecx <- A + movzwl %ax,%eax + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_int_to_short: /* 0x8f */ +/* File: x86/op_int_to_short.S */ +/* File: x86/unop.S */ +/* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movzbl rINSTbl,%ecx # ecx <- A+ + sarl $4,rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf,%cl # ecx <- A + movswl %ax, %eax + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int: /* 0x90 */ +/* File: x86/op_add_int.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + addl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int: /* 0x91 */ +/* File: x86/op_sub_int.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + subl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int: /* 0x92 */ +/* File: x86/op_mul_int.S */ + /* + * 32-bit binary multiplication. + */ + /* mul vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + mov rIBASE, LOCAL0(%esp) + imull (rFP,%ecx,4), %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_div_int: /* 0x93 */ +/* File: x86/op_div_int.S */ +/* File: x86/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + GET_VREG %ecx %ecx # ecx <- vCC + mov rIBASE, LOCAL0(%esp) + testl %ecx, %ecx + je common_errDivideByZero + movl %eax, %edx + orl %ecx, %edx + test $0xFFFFFF00, %edx # If both arguments are less + # than 8-bit and +ve + jz .Lop_div_int_8 # Do 8-bit divide + test $0xFFFF0000, %edx # If both arguments are less + # than 16-bit and +ve + jz .Lop_div_int_16 # Do 16-bit divide + cmpl $-1, %ecx + jne .Lop_div_int_32 + cmpl $0x80000000, %eax + jne .Lop_div_int_32 + movl $0x80000000, %eax + jmp .Lop_div_int_finish +.Lop_div_int_32: + cltd + idivl %ecx + jmp .Lop_div_int_finish +.Lop_div_int_8: + div %cl # 8-bit divide otherwise. + # Remainder in %ah, quotient in %al + .if 0 + movl %eax, %edx + shr $8, %edx + .else + andl $0x000000FF, %eax + .endif + jmp .Lop_div_int_finish +.Lop_div_int_16: + xorl %edx, %edx # Clear %edx before divide + div %cx +.Lop_div_int_finish: + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int: /* 0x94 */ +/* File: x86/op_rem_int.S */ +/* File: x86/bindiv.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + GET_VREG %ecx %ecx # ecx <- vCC + mov rIBASE, LOCAL0(%esp) + testl %ecx, %ecx + je common_errDivideByZero + movl %eax, %edx + orl %ecx, %edx + test $0xFFFFFF00, %edx # If both arguments are less + # than 8-bit and +ve + jz .Lop_rem_int_8 # Do 8-bit divide + test $0xFFFF0000, %edx # If both arguments are less + # than 16-bit and +ve + jz .Lop_rem_int_16 # Do 16-bit divide + cmpl $-1, %ecx + jne .Lop_rem_int_32 + cmpl $0x80000000, %eax + jne .Lop_rem_int_32 + movl $0, rIBASE + jmp .Lop_rem_int_finish +.Lop_rem_int_32: + cltd + idivl %ecx + jmp .Lop_rem_int_finish +.Lop_rem_int_8: + div %cl # 8-bit divide otherwise. + # Remainder in %ah, quotient in %al + .if 1 + movl %eax, %edx + shr $8, %edx + .else + andl $0x000000FF, %eax + .endif + jmp .Lop_rem_int_finish +.Lop_rem_int_16: + xorl %edx, %edx # Clear %edx before divide + div %cx +.Lop_rem_int_finish: + SET_VREG rIBASE rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int: /* 0x95 */ +/* File: x86/op_and_int.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + andl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int: /* 0x96 */ +/* File: x86/op_or_int.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + orl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int: /* 0x97 */ +/* File: x86/op_xor_int.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + xorl (rFP,%ecx,4), %eax # ex: addl (rFP,%ecx,4),%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int: /* 0x98 */ +/* File: x86/op_shl_int.S */ +/* File: x86/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + GET_VREG %ecx %ecx # eax <- vBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int: /* 0x99 */ +/* File: x86/op_shr_int.S */ +/* File: x86/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + GET_VREG %ecx %ecx # eax <- vBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int: /* 0x9a */ +/* File: x86/op_ushr_int.S */ +/* File: x86/binop1.S */ +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + GET_VREG %ecx %ecx # eax <- vBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long: /* 0x9b */ +/* File: x86/op_add_long.S */ +/* File: x86/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + movl rIBASE,LOCAL0(%esp) # save rIBASE + GET_VREG rIBASE %eax # rIBASE <- v[BB+0] + GET_VREG_HIGH %eax %eax # eax <- v[BB+1] + addl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE + adcl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax + SET_VREG rIBASE rINST # v[AA+0] <- rIBASE + movl LOCAL0(%esp),rIBASE # restore rIBASE + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long: /* 0x9c */ +/* File: x86/op_sub_long.S */ +/* File: x86/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + movl rIBASE,LOCAL0(%esp) # save rIBASE + GET_VREG rIBASE %eax # rIBASE <- v[BB+0] + GET_VREG_HIGH %eax %eax # eax <- v[BB+1] + subl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE + sbbl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax + SET_VREG rIBASE rINST # v[AA+0] <- rIBASE + movl LOCAL0(%esp),rIBASE # restore rIBASE + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long: /* 0x9d */ +/* File: x86/op_mul_long.S */ +/* + * Signed 64-bit integer multiply. + * + * We could definately use more free registers for + * this code. We spill rINSTw (ebx), + * giving us eax, ebc, ecx and edx as computational + * temps. On top of that, we'll spill edi (rFP) + * for use as the vB pointer and esi (rPC) for use + * as the vC pointer. Yuck. + * + */ + /* mul-long vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- B + movzbl 3(rPC), %ecx # ecx <- C + mov rPC, LOCAL0(%esp) # save Interpreter PC + mov rFP, LOCAL1(%esp) # save FP + mov rIBASE, LOCAL2(%esp) # save rIBASE + leal (rFP,%eax,4), %esi # esi <- &v[B] + leal (rFP,%ecx,4), rFP # rFP <- &v[C] + movl 4(%esi), %ecx # ecx <- Bmsw + imull (rFP), %ecx # ecx <- (Bmsw*Clsw) + movl 4(rFP), %eax # eax <- Cmsw + imull (%esi), %eax # eax <- (Cmsw*Blsw) + addl %eax, %ecx # ecx <- (Bmsw*Clsw)+(Cmsw*Blsw) + movl (rFP), %eax # eax <- Clsw + mull (%esi) # eax <- (Clsw*Alsw) + mov LOCAL0(%esp), rPC # restore Interpreter PC + mov LOCAL1(%esp), rFP # restore FP + leal (%ecx,rIBASE), rIBASE # full result now in rIBASE:%eax + SET_VREG_HIGH rIBASE rINST # v[B+1] <- rIBASE + mov LOCAL2(%esp), rIBASE # restore IBASE + SET_VREG %eax rINST # v[B] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_div_long: /* 0x9e */ +/* File: x86/op_div_long.S */ +/* art_quick_* methods has quick abi, + * so use eax, ecx, edx, ebx for args + */ + /* div vAA, vBB, vCC */ + .extern art_quick_ldiv + mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx + mov rINST, LOCAL1(%esp) # save rINST/%ebx + movzbl 3(rPC), %eax # eax <- CC + GET_VREG %ecx %eax + GET_VREG_HIGH %ebx %eax + movl %ecx, %edx + orl %ebx, %ecx + jz common_errDivideByZero + movzbl 2(rPC), %eax # eax <- BB + GET_VREG_HIGH %ecx %eax + GET_VREG %eax %eax + call art_quick_ldiv + mov LOCAL1(%esp), rINST # restore rINST/%ebx + SET_VREG_HIGH rIBASE rINST + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long: /* 0x9f */ +/* File: x86/op_rem_long.S */ +/* File: x86/op_div_long.S */ +/* art_quick_* methods has quick abi, + * so use eax, ecx, edx, ebx for args + */ + /* div vAA, vBB, vCC */ + .extern art_quick_lmod + mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx + mov rINST, LOCAL1(%esp) # save rINST/%ebx + movzbl 3(rPC), %eax # eax <- CC + GET_VREG %ecx %eax + GET_VREG_HIGH %ebx %eax + movl %ecx, %edx + orl %ebx, %ecx + jz common_errDivideByZero + movzbl 2(rPC), %eax # eax <- BB + GET_VREG_HIGH %ecx %eax + GET_VREG %eax %eax + call art_quick_lmod + mov LOCAL1(%esp), rINST # restore rINST/%ebx + SET_VREG_HIGH rIBASE rINST + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long: /* 0xa0 */ +/* File: x86/op_and_long.S */ +/* File: x86/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + movl rIBASE,LOCAL0(%esp) # save rIBASE + GET_VREG rIBASE %eax # rIBASE <- v[BB+0] + GET_VREG_HIGH %eax %eax # eax <- v[BB+1] + andl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE + andl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax + SET_VREG rIBASE rINST # v[AA+0] <- rIBASE + movl LOCAL0(%esp),rIBASE # restore rIBASE + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long: /* 0xa1 */ +/* File: x86/op_or_long.S */ +/* File: x86/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + movl rIBASE,LOCAL0(%esp) # save rIBASE + GET_VREG rIBASE %eax # rIBASE <- v[BB+0] + GET_VREG_HIGH %eax %eax # eax <- v[BB+1] + orl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE + orl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax + SET_VREG rIBASE rINST # v[AA+0] <- rIBASE + movl LOCAL0(%esp),rIBASE # restore rIBASE + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long: /* 0xa2 */ +/* File: x86/op_xor_long.S */ +/* File: x86/binopWide.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + movl rIBASE,LOCAL0(%esp) # save rIBASE + GET_VREG rIBASE %eax # rIBASE <- v[BB+0] + GET_VREG_HIGH %eax %eax # eax <- v[BB+1] + xorl (rFP,%ecx,4), rIBASE # ex: addl (rFP,%ecx,4),rIBASE + xorl 4(rFP,%ecx,4), %eax # ex: adcl 4(rFP,%ecx,4),%eax + SET_VREG rIBASE rINST # v[AA+0] <- rIBASE + movl LOCAL0(%esp),rIBASE # restore rIBASE + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long: /* 0xa3 */ +/* File: x86/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. x86 shifts automatically mask off + * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31 + * case specially. + */ + /* shl-long vAA, vBB, vCC */ + /* ecx gets shift count */ + /* Need to spill rINST */ + /* rINSTw gets AA */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE %eax # ecx <- v[BB+1] + GET_VREG %ecx %ecx # ecx <- vCC + GET_VREG %eax %eax # eax <- v[BB+0] + shldl %eax,rIBASE + sall %cl, %eax + testb $32, %cl + je 2f + movl %eax, rIBASE + xorl %eax, %eax +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- %eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long: /* 0xa4 */ +/* File: x86/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. x86 shifts automatically mask off + * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31 + * case specially. + */ + /* shr-long vAA, vBB, vCC */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE %eax # rIBASE<- v[BB+1] + GET_VREG %ecx %ecx # ecx <- vCC + GET_VREG %eax %eax # eax <- v[BB+0] + shrdl rIBASE, %eax + sarl %cl, rIBASE + testb $32, %cl + je 2f + movl rIBASE, %eax + sarl $31, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long: /* 0xa5 */ +/* File: x86/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. x86 shifts automatically mask off + * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31 + * case specially. + */ + /* shr-long vAA, vBB, vCC */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE %eax # rIBASE <- v[BB+1] + GET_VREG %ecx %ecx # ecx <- vCC + GET_VREG %eax %eax # eax <- v[BB+0] + shrdl rIBASE, %eax + shrl %cl, rIBASE + testb $32, %cl + je 2f + movl rIBASE, %eax + xorl rIBASE, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[BB+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_float: /* 0xa6 */ +/* File: x86/op_add_float.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + addss VREG_ADDRESS(%eax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float: /* 0xa7 */ +/* File: x86/op_sub_float.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + subss VREG_ADDRESS(%eax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float: /* 0xa8 */ +/* File: x86/op_mul_float.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + mulss VREG_ADDRESS(%eax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float: /* 0xa9 */ +/* File: x86/op_div_float.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + divss VREG_ADDRESS(%eax), %xmm0 + movss %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float: /* 0xaa */ +/* File: x86/op_rem_float.S */ + /* rem_float vAA, vBB, vCC */ + movzbl 3(rPC), %ecx # ecx <- BB + movzbl 2(rPC), %eax # eax <- CC + flds VREG_ADDRESS(%ecx) # vBB to fp stack + flds VREG_ADDRESS(%eax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINST) # %st to vAA + CLEAR_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double: /* 0xab */ +/* File: x86/op_add_double.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + addsd VREG_ADDRESS(%eax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double: /* 0xac */ +/* File: x86/op_sub_double.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + subsd VREG_ADDRESS(%eax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double: /* 0xad */ +/* File: x86/op_mul_double.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + mulsd VREG_ADDRESS(%eax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double: /* 0xae */ +/* File: x86/op_div_double.S */ +/* File: x86/sseBinop.S */ + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + divsd VREG_ADDRESS(%eax), %xmm0 + movsd %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double: /* 0xaf */ +/* File: x86/op_rem_double.S */ + /* rem_double vAA, vBB, vCC */ + movzbl 3(rPC), %ecx # ecx <- BB + movzbl 2(rPC), %eax # eax <- CC + fldl VREG_ADDRESS(%ecx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%eax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINST) # fp[vAA] <- %st + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_2addr: /* 0xb0 */ +/* File: x86/op_add_int_2addr.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf, %cl # ecx <- A + addl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_int_2addr: /* 0xb1 */ +/* File: x86/op_sub_int_2addr.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf, %cl # ecx <- A + subl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_2addr: /* 0xb2 */ +/* File: x86/op_mul_int_2addr.S */ + /* mul vA, vB */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf, %cl # ecx <- A + mov rIBASE, LOCAL0(%esp) + imull (rFP,%ecx,4), %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_2addr: /* 0xb3 */ +/* File: x86/op_div_int_2addr.S */ +/* File: x86/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem/2addr vA, vB */ + movzx rINSTbl, %ecx # eax <- BA + mov rIBASE, LOCAL0(%esp) + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # eax <- vBB + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- vBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + jne .Lop_div_int_2addr_continue_div2addr + cmpl $0x80000000, %eax + jne .Lop_div_int_2addr_continue_div2addr + movl $0x80000000, %eax + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.Lop_div_int_2addr_continue_div2addr: + cltd + idivl %ecx + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_2addr: /* 0xb4 */ +/* File: x86/op_rem_int_2addr.S */ +/* File: x86/bindiv2addr.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem/2addr vA, vB */ + movzx rINSTbl, %ecx # eax <- BA + mov rIBASE, LOCAL0(%esp) + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # eax <- vBB + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- vBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + jne .Lop_rem_int_2addr_continue_div2addr + cmpl $0x80000000, %eax + jne .Lop_rem_int_2addr_continue_div2addr + movl $0, rIBASE + SET_VREG rIBASE rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.Lop_rem_int_2addr_continue_div2addr: + cltd + idivl %ecx + SET_VREG rIBASE rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_2addr: /* 0xb5 */ +/* File: x86/op_and_int_2addr.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf, %cl # ecx <- A + andl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_2addr: /* 0xb6 */ +/* File: x86/op_or_int_2addr.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf, %cl # ecx <- A + orl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_2addr: /* 0xb7 */ +/* File: x86/op_xor_int_2addr.S */ +/* File: x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $0xf, %cl # ecx <- A + xorl %eax, (rFP,%ecx,4) # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_2addr: /* 0xb8 */ +/* File: x86/op_shl_int_2addr.S */ +/* File: x86/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movzx rINSTbl, %ecx # eax <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # eax <- vBB + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- vAA + sall %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_2addr: /* 0xb9 */ +/* File: x86/op_shr_int_2addr.S */ +/* File: x86/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movzx rINSTbl, %ecx # eax <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # eax <- vBB + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- vAA + sarl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_2addr: /* 0xba */ +/* File: x86/op_ushr_int_2addr.S */ +/* File: x86/shop2addr.S */ +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movzx rINSTbl, %ecx # eax <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # eax <- vBB + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- vAA + shrl %cl, %eax # ex: sarl %cl, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_long_2addr: /* 0xbb */ +/* File: x86/op_add_long_2addr.S */ +/* File: x86/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movzbl rINSTbl,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + GET_VREG %eax %ecx # eax<- v[B+0] + GET_VREG_HIGH %ecx %ecx # eax<- v[B+1] + andb $0xF,rINSTbl # rINST<- A + addl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4) + adcl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4) + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_long_2addr: /* 0xbc */ +/* File: x86/op_sub_long_2addr.S */ +/* File: x86/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movzbl rINSTbl,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + GET_VREG %eax %ecx # eax<- v[B+0] + GET_VREG_HIGH %ecx %ecx # eax<- v[B+1] + andb $0xF,rINSTbl # rINST<- A + subl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4) + sbbl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4) + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_long_2addr: /* 0xbd */ +/* File: x86/op_mul_long_2addr.S */ +/* + * Signed 64-bit integer multiply, 2-addr version + * + * We could definately use more free registers for + * this code. We must spill %edx (rIBASE) because it + * is used by imul. We'll also spill rINST (ebx), + * giving us eax, ebc, ecx and rIBASE as computational + * temps. On top of that, we'll spill %esi (edi) + * for use as the vA pointer and rFP (esi) for use + * as the vB pointer. Yuck. + */ + /* mul-long/2addr vA, vB */ + movzbl rINSTbl, %eax # eax <- BA + andb $0xf, %al # eax <- A + CLEAR_WIDE_REF %eax # clear refs in advance + sarl $4, rINST # rINST <- B + mov rPC, LOCAL0(%esp) # save Interpreter PC + mov rFP, LOCAL1(%esp) # save FP + mov rIBASE, LOCAL2(%esp) # save rIBASE + leal (rFP,%eax,4), %esi # esi <- &v[A] + leal (rFP,rINST,4), rFP # rFP <- &v[B] + movl 4(%esi), %ecx # ecx <- Amsw + imull (rFP), %ecx # ecx <- (Amsw*Blsw) + movl 4(rFP), %eax # eax <- Bmsw + imull (%esi), %eax # eax <- (Bmsw*Alsw) + addl %eax, %ecx # ecx <- (Amsw*Blsw)+(Bmsw*Alsw) + movl (rFP), %eax # eax <- Blsw + mull (%esi) # eax <- (Blsw*Alsw) + leal (%ecx,rIBASE), rIBASE # full result now in %edx:%eax + movl rIBASE, 4(%esi) # v[A+1] <- rIBASE + movl %eax, (%esi) # v[A] <- %eax + mov LOCAL0(%esp), rPC # restore Interpreter PC + mov LOCAL2(%esp), rIBASE # restore IBASE + mov LOCAL1(%esp), rFP # restore FP + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_div_long_2addr: /* 0xbe */ +/* File: x86/op_div_long_2addr.S */ +/* art_quick_* methods has quick abi, + * so use eax, ecx, edx, ebx for args + */ + /* div/2addr vA, vB */ + .extern art_quick_ldiv + mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx + movzbl rINSTbl, %eax + shrl $4, %eax # eax <- B + andb $0xf, rINSTbl # rINST <- A + mov rINST, LOCAL1(%esp) # save rINST/%ebx + movl %ebx, %ecx + GET_VREG %edx %eax + GET_VREG_HIGH %ebx %eax + movl %edx, %eax + orl %ebx, %eax + jz common_errDivideByZero + GET_VREG %eax %ecx + GET_VREG_HIGH %ecx %ecx + call art_quick_ldiv + mov LOCAL1(%esp), rINST # restore rINST/%ebx + SET_VREG_HIGH rIBASE rINST + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_rem_long_2addr: /* 0xbf */ +/* File: x86/op_rem_long_2addr.S */ +/* File: x86/op_div_long_2addr.S */ +/* art_quick_* methods has quick abi, + * so use eax, ecx, edx, ebx for args + */ + /* div/2addr vA, vB */ + .extern art_quick_lmod + mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx + movzbl rINSTbl, %eax + shrl $4, %eax # eax <- B + andb $0xf, rINSTbl # rINST <- A + mov rINST, LOCAL1(%esp) # save rINST/%ebx + movl %ebx, %ecx + GET_VREG %edx %eax + GET_VREG_HIGH %ebx %eax + movl %edx, %eax + orl %ebx, %eax + jz common_errDivideByZero + GET_VREG %eax %ecx + GET_VREG_HIGH %ecx %ecx + call art_quick_lmod + mov LOCAL1(%esp), rINST # restore rINST/%ebx + SET_VREG_HIGH rIBASE rINST + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_and_long_2addr: /* 0xc0 */ +/* File: x86/op_and_long_2addr.S */ +/* File: x86/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movzbl rINSTbl,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + GET_VREG %eax %ecx # eax<- v[B+0] + GET_VREG_HIGH %ecx %ecx # eax<- v[B+1] + andb $0xF,rINSTbl # rINST<- A + andl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4) + andl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4) + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_long_2addr: /* 0xc1 */ +/* File: x86/op_or_long_2addr.S */ +/* File: x86/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movzbl rINSTbl,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + GET_VREG %eax %ecx # eax<- v[B+0] + GET_VREG_HIGH %ecx %ecx # eax<- v[B+1] + andb $0xF,rINSTbl # rINST<- A + orl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4) + orl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4) + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_long_2addr: /* 0xc2 */ +/* File: x86/op_xor_long_2addr.S */ +/* File: x86/binopWide2addr.S */ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movzbl rINSTbl,%ecx # ecx<- BA + sarl $4,%ecx # ecx<- B + GET_VREG %eax %ecx # eax<- v[B+0] + GET_VREG_HIGH %ecx %ecx # eax<- v[B+1] + andb $0xF,rINSTbl # rINST<- A + xorl %eax, (rFP,rINST,4) # ex: addl %eax,(rFP,rINST,4) + xorl %ecx, 4(rFP,rINST,4) # ex: adcl %ecx,4(rFP,rINST,4) + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_long_2addr: /* 0xc3 */ +/* File: x86/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 */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl rINSTbl, %ecx # ecx <- BA + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- v[AA+0] + sarl $4, %ecx # ecx <- B + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1] + GET_VREG %ecx %ecx # ecx <- vBB + shldl %eax, rIBASE + sall %cl, %eax + testb $32, %cl + je 2f + movl %eax, rIBASE + xorl %eax, %eax +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_shr_long_2addr: /* 0xc4 */ +/* File: x86/op_shr_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 */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl rINSTbl, %ecx # ecx <- BA + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- v[AA+0] + sarl $4, %ecx # ecx <- B + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1] + GET_VREG %ecx %ecx # ecx <- vBB + shrdl rIBASE, %eax + sarl %cl, rIBASE + testb $32, %cl + je 2f + movl rIBASE, %eax + sarl $31, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86/op_ushr_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 */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl rINSTbl, %ecx # ecx <- BA + andb $0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- v[AA+0] + sarl $4, %ecx # ecx <- B + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1] + GET_VREG %ecx %ecx # ecx <- vBB + shrdl rIBASE, %eax + shrl %cl, rIBASE + testb $32, %cl + je 2f + movl rIBASE, %eax + xorl rIBASE, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_float_2addr: /* 0xc6 */ +/* File: x86/op_add_float_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addss VREG_ADDRESS(rINST), %xmm0 + movss %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_float_2addr: /* 0xc7 */ +/* File: x86/op_sub_float_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subss VREG_ADDRESS(rINST), %xmm0 + movss %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_float_2addr: /* 0xc8 */ +/* File: x86/op_mul_float_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulss VREG_ADDRESS(rINST), %xmm0 + movss %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_float_2addr: /* 0xc9 */ +/* File: x86/op_div_float_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movss VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divss VREG_ADDRESS(rINST), %xmm0 + movss %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movss %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_float_2addr: /* 0xca */ +/* File: x86/op_rem_float_2addr.S */ + /* rem_float/2addr vA, vB */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + flds VREG_ADDRESS(rINST) # vB to fp stack + andb $0xf, %cl # ecx <- A + flds VREG_ADDRESS(%ecx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%ecx) # %st to vA + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_double_2addr: /* 0xcb */ +/* File: x86/op_add_double_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + addsd VREG_ADDRESS(rINST), %xmm0 + movsd %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_sub_double_2addr: /* 0xcc */ +/* File: x86/op_sub_double_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + subsd VREG_ADDRESS(rINST), %xmm0 + movsd %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_double_2addr: /* 0xcd */ +/* File: x86/op_mul_double_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + mulsd VREG_ADDRESS(rINST), %xmm0 + movsd %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_div_double_2addr: /* 0xce */ +/* File: x86/op_div_double_2addr.S */ +/* File: x86/sseBinop2Addr.S */ + movzx rINSTbl, %ecx # ecx <- A+ + andl $0xf, %ecx # ecx <- A + movsd VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $4, rINST # rINST<- B + divsd VREG_ADDRESS(rINST), %xmm0 + movsd %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movsd %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_double_2addr: /* 0xcf */ +/* File: x86/op_rem_double_2addr.S */ + /* rem_double/2addr vA, vB */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $4, rINST # rINST <- B + fldl VREG_ADDRESS(rINST) # vB to fp stack + andb $0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%ecx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%ecx) # %st to vA + CLEAR_WIDE_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit16: /* 0xd0 */ +/* File: x86/op_add_int_lit16.S */ +/* File: x86/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 */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + addl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int: /* 0xd1 */ +/* File: x86/op_rsub_int.S */ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +/* File: x86/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 */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + subl %eax, %ecx # for example: addl %ecx, %eax + SET_VREG %ecx rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit16: /* 0xd2 */ +/* File: x86/op_mul_int_lit16.S */ + /* mul/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + mov rIBASE, LOCAL0(%esp) + imull %ecx, %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit16: /* 0xd3 */ +/* File: x86/op_div_int_lit16.S */ +/* File: x86/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + jne .Lop_div_int_lit16_continue_div + cmpl $0x80000000, %eax + jne .Lop_div_int_lit16_continue_div + movl $0x80000000, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.Lop_div_int_lit16_continue_div: + mov rIBASE, LOCAL0(%esp) + cltd + idivl %ecx + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit16: /* 0xd4 */ +/* File: x86/op_rem_int_lit16.S */ +/* File: x86/bindivLit16.S */ +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + je common_errDivideByZero + cmpl $-1, %ecx + jne .Lop_rem_int_lit16_continue_div + cmpl $0x80000000, %eax + jne .Lop_rem_int_lit16_continue_div + movl $0, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.Lop_rem_int_lit16_continue_div: + mov rIBASE, LOCAL0(%esp) + cltd + idivl %ecx + SET_VREG rIBASE rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit16: /* 0xd5 */ +/* File: x86/op_and_int_lit16.S */ +/* File: x86/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 */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + andl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit16: /* 0xd6 */ +/* File: x86/op_or_int_lit16.S */ +/* File: x86/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 */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + orl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit16: /* 0xd7 */ +/* File: x86/op_xor_int_lit16.S */ +/* File: x86/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 */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $0xf, rINSTbl # rINST <- A + xorl %ecx, %eax # for example: addl %ecx, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_add_int_lit8: /* 0xd8 */ +/* File: x86/op_add_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + addl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86/op_rsub_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + subl %eax, %ecx # ex: addl %ecx,%eax + SET_VREG %ecx rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_mul_int_lit8: /* 0xda */ +/* File: x86/op_mul_int_lit8.S */ + /* mul/lit8 vAA, vBB, #+CC */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + mov rIBASE, LOCAL0(%esp) + imull %ecx, %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_div_int_lit8: /* 0xdb */ +/* File: x86/op_div_int_lit8.S */ +/* File: x86/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $0x80000000, %eax + jne .Lop_div_int_lit8_continue_div + cmpl $-1, %ecx + jne .Lop_div_int_lit8_continue_div + movl $0x80000000, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.Lop_div_int_lit8_continue_div: + mov rIBASE, LOCAL0(%esp) + cltd + idivl %ecx + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_rem_int_lit8: /* 0xdc */ +/* File: x86/op_rem_int_lit8.S */ +/* File: x86/bindivLit8.S */ +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $0x80000000, %eax + jne .Lop_rem_int_lit8_continue_div + cmpl $-1, %ecx + jne .Lop_rem_int_lit8_continue_div + movl $0, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.Lop_rem_int_lit8_continue_div: + mov rIBASE, LOCAL0(%esp) + cltd + idivl %ecx + SET_VREG rIBASE rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_and_int_lit8: /* 0xdd */ +/* File: x86/op_and_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + andl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_or_int_lit8: /* 0xde */ +/* File: x86/op_or_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + orl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_xor_int_lit8: /* 0xdf */ +/* File: x86/op_xor_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + xorl %ecx, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shl_int_lit8: /* 0xe0 */ +/* File: x86/op_shl_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + sall %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_shr_int_lit8: /* 0xe1 */ +/* File: x86/op_shr_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + sarl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86/op_ushr_int_lit8.S */ +/* File: x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + shrl %cl, %eax # ex: addl %ecx,%eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_quick: /* 0xe3 */ +/* File: x86/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + movl (%ecx,%eax,1), %eax + andb $0xf,rINSTbl # rINST <- A + SET_VREG %eax rINST # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_wide_quick: /* 0xe4 */ +/* File: x86/op_iget_wide_quick.S */ + /* iget-wide-quick vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + movq (%ecx,%eax,1), %xmm0 + andb $0xf, rINSTbl # rINST <- A + SET_WIDE_FP_VREG %xmm0 rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iget_object_quick: /* 0xe5 */ +/* File: x86/op_iget_object_quick.S */ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + movl %ecx, OUT_ARG0(%esp) + movl %eax, OUT_ARG1(%esp) + EXPORT_PC + call artIGetObjectFromMterp # (obj, offset) + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $0xf,rINSTbl # rINST <- A + SET_VREG_OBJECT %eax rINST # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_quick: /* 0xe6 */ +/* File: x86/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST rINST # rINST <- v[A] + movzwl 2(rPC), %eax # eax <- field byte offset + movl rINST, (%ecx,%eax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_wide_quick: /* 0xe7 */ +/* File: x86/op_iput_wide_quick.S */ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx<- BA + sarl $4, %ecx # ecx<- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwl 2(rPC), %eax # eax<- field byte offset + leal (%ecx,%eax,1), %ecx # ecx<- Address of 64-bit target + andb $0xf, rINSTbl # rINST<- A + GET_WIDE_FP_VREG %xmm0 rINST # xmm0<- fp[A]/fp[A+1] + movq %xmm0, (%ecx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_iput_object_quick: /* 0xe8 */ +/* File: x86/op_iput_object_quick.S */ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST 232 + movl rINST, OUT_ARG2(%esp) + call MterpIputObjectQuick + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86/op_invoke_virtual_quick.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 233 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeVirtualQuick + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86/op_invoke_virtual_range_quick.S */ +/* File: x86/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 + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST 234 + movl rINST, OUT_ARG3(%esp) + call MterpInvokeVirtualQuickRange + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_boolean_quick: /* 0xeb */ +/* File: x86/op_iput_boolean_quick.S */ +/* File: x86/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST rINST # rINST <- v[A] + movzwl 2(rPC), %eax # eax <- field byte offset + movb rINSTbl, (%ecx,%eax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_byte_quick: /* 0xec */ +/* File: x86/op_iput_byte_quick.S */ +/* File: x86/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST rINST # rINST <- v[A] + movzwl 2(rPC), %eax # eax <- field byte offset + movb rINSTbl, (%ecx,%eax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_char_quick: /* 0xed */ +/* File: x86/op_iput_char_quick.S */ +/* File: x86/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST rINST # rINST <- v[A] + movzwl 2(rPC), %eax # eax <- field byte offset + movw rINSTw, (%ecx,%eax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iput_short_quick: /* 0xee */ +/* File: x86/op_iput_short_quick.S */ +/* File: x86/op_iput_quick.S */ + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $0xf, rINSTbl # rINST <- A + GET_VREG rINST rINST # rINST <- v[A] + movzwl 2(rPC), %eax # eax <- field byte offset + movw rINSTw, (%ecx,%eax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_boolean_quick: /* 0xef */ +/* File: x86/op_iget_boolean_quick.S */ +/* File: x86/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + movsbl (%ecx,%eax,1), %eax + andb $0xf,rINSTbl # rINST <- A + SET_VREG %eax rINST # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_byte_quick: /* 0xf0 */ +/* File: x86/op_iget_byte_quick.S */ +/* File: x86/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + movsbl (%ecx,%eax,1), %eax + andb $0xf,rINSTbl # rINST <- A + SET_VREG %eax rINST # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_char_quick: /* 0xf1 */ +/* File: x86/op_iget_char_quick.S */ +/* File: x86/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwl (%ecx,%eax,1), %eax + andb $0xf,rINSTbl # rINST <- A + SET_VREG %eax rINST # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + + +/* ------------------------------ */ + .balign 128 +.L_op_iget_short_quick: /* 0xf2 */ +/* File: x86/op_iget_short_quick.S */ +/* File: x86/op_iget_quick.S */ + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + movswl (%ecx,%eax,1), %eax + andb $0xf,rINSTbl # rINST <- A + SET_VREG %eax rINST # fp[A] <- value + 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/op_unused_f4.S */ +/* File: x86/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/op_unused_fa.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fb: /* 0xfb */ +/* File: x86/op_unused_fb.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fc: /* 0xfc */ +/* File: x86/op_unused_fc.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fd: /* 0xfd */ +/* File: x86/op_unused_fd.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_fe: /* 0xfe */ +/* File: x86/op_unused_fe.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + +/* ------------------------------ */ + .balign 128 +.L_op_unused_ff: /* 0xff */ +/* File: x86/op_unused_ff.S */ +/* File: x86/unused.S */ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback + + + .balign 128 + .size artMterpAsmInstructionStart, .-artMterpAsmInstructionStart + .global artMterpAsmInstructionEnd +artMterpAsmInstructionEnd: + +/* + * =========================================================================== + * Sister implementations + * =========================================================================== + */ + .global artMterpAsmSisterStart + .type artMterpAsmSisterStart, %function + .text + .balign 4 +artMterpAsmSisterStart: + + .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: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(0*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move: /* 0x01 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(1*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_from16: /* 0x02 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(2*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_16: /* 0x03 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(3*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide: /* 0x04 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(4*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_from16: /* 0x05 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(5*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_wide_16: /* 0x06 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(6*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object: /* 0x07 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(7*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_from16: /* 0x08 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(8*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_object_16: /* 0x09 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(9*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result: /* 0x0a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(10*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_wide: /* 0x0b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(11*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_result_object: /* 0x0c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(12*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_move_exception: /* 0x0d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(13*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void: /* 0x0e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(14*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return: /* 0x0f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(15*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_wide: /* 0x10 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(16*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_object: /* 0x11 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(17*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_4: /* 0x12 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(18*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_16: /* 0x13 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(19*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const: /* 0x14 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(20*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_high16: /* 0x15 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(21*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_16: /* 0x16 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(22*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_32: /* 0x17 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(23*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide: /* 0x18 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(24*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_wide_high16: /* 0x19 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(25*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string: /* 0x1a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(26*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_string_jumbo: /* 0x1b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(27*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_const_class: /* 0x1c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(28*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_enter: /* 0x1d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(29*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_monitor_exit: /* 0x1e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(30*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_check_cast: /* 0x1f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(31*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_instance_of: /* 0x20 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(32*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_array_length: /* 0x21 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(33*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_instance: /* 0x22 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(34*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_new_array: /* 0x23 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(35*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array: /* 0x24 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(36*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_filled_new_array_range: /* 0x25 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(37*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_fill_array_data: /* 0x26 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(38*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_throw: /* 0x27 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(39*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto: /* 0x28 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(40*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_16: /* 0x29 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(41*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_goto_32: /* 0x2a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(42*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_packed_switch: /* 0x2b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(43*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sparse_switch: /* 0x2c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(44*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_float: /* 0x2d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(45*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_float: /* 0x2e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(46*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpl_double: /* 0x2f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(47*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmpg_double: /* 0x30 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(48*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_cmp_long: /* 0x31 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(49*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eq: /* 0x32 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(50*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ne: /* 0x33 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(51*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lt: /* 0x34 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(52*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ge: /* 0x35 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(53*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gt: /* 0x36 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(54*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_le: /* 0x37 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(55*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_eqz: /* 0x38 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(56*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_nez: /* 0x39 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(57*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_ltz: /* 0x3a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(58*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gez: /* 0x3b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(59*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_gtz: /* 0x3c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(60*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_if_lez: /* 0x3d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(61*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3e: /* 0x3e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(62*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_3f: /* 0x3f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(63*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_40: /* 0x40 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(64*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_41: /* 0x41 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(65*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_42: /* 0x42 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(66*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_43: /* 0x43 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(67*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget: /* 0x44 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(68*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_wide: /* 0x45 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(69*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_object: /* 0x46 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(70*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_boolean: /* 0x47 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(71*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_byte: /* 0x48 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(72*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_char: /* 0x49 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(73*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aget_short: /* 0x4a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(74*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput: /* 0x4b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(75*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_wide: /* 0x4c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(76*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_object: /* 0x4d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(77*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_boolean: /* 0x4e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(78*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_byte: /* 0x4f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(79*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_char: /* 0x50 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(80*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_aput_short: /* 0x51 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(81*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget: /* 0x52 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(82*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide: /* 0x53 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(83*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object: /* 0x54 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(84*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean: /* 0x55 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(85*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte: /* 0x56 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(86*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char: /* 0x57 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(87*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short: /* 0x58 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(88*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput: /* 0x59 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(89*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide: /* 0x5a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(90*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object: /* 0x5b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(91*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean: /* 0x5c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(92*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte: /* 0x5d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(93*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char: /* 0x5e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(94*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short: /* 0x5f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(95*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget: /* 0x60 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(96*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_wide: /* 0x61 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(97*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_object: /* 0x62 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(98*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_boolean: /* 0x63 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(99*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_byte: /* 0x64 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(100*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_char: /* 0x65 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(101*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sget_short: /* 0x66 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(102*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput: /* 0x67 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(103*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_wide: /* 0x68 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(104*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_object: /* 0x69 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(105*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_boolean: /* 0x6a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(106*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_byte: /* 0x6b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(107*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_char: /* 0x6c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(108*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sput_short: /* 0x6d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(109*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual: /* 0x6e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(110*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super: /* 0x6f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(111*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct: /* 0x70 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(112*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static: /* 0x71 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(113*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface: /* 0x72 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(114*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_return_void_no_barrier: /* 0x73 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(115*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range: /* 0x74 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(116*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_super_range: /* 0x75 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(117*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_direct_range: /* 0x76 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(118*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_static_range: /* 0x77 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(119*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_interface_range: /* 0x78 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(120*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_79: /* 0x79 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(121*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_7a: /* 0x7a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(122*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_int: /* 0x7b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(123*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_int: /* 0x7c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(124*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_long: /* 0x7d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(125*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_not_long: /* 0x7e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(126*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_float: /* 0x7f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(127*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_neg_double: /* 0x80 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(128*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_long: /* 0x81 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(129*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_float: /* 0x82 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(130*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_double: /* 0x83 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(131*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_int: /* 0x84 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(132*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_float: /* 0x85 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(133*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_long_to_double: /* 0x86 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(134*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_int: /* 0x87 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(135*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_long: /* 0x88 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(136*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_float_to_double: /* 0x89 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(137*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_int: /* 0x8a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(138*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_long: /* 0x8b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(139*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_double_to_float: /* 0x8c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(140*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_byte: /* 0x8d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(141*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_char: /* 0x8e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(142*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_int_to_short: /* 0x8f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(143*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int: /* 0x90 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(144*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int: /* 0x91 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(145*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int: /* 0x92 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(146*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int: /* 0x93 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(147*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int: /* 0x94 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(148*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int: /* 0x95 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(149*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int: /* 0x96 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(150*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int: /* 0x97 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(151*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int: /* 0x98 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(152*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int: /* 0x99 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(153*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int: /* 0x9a */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(154*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long: /* 0x9b */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(155*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long: /* 0x9c */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(156*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long: /* 0x9d */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(157*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long: /* 0x9e */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(158*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long: /* 0x9f */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(159*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long: /* 0xa0 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(160*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long: /* 0xa1 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(161*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long: /* 0xa2 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(162*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long: /* 0xa3 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(163*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long: /* 0xa4 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(164*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long: /* 0xa5 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(165*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float: /* 0xa6 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(166*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float: /* 0xa7 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(167*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float: /* 0xa8 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(168*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float: /* 0xa9 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(169*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float: /* 0xaa */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(170*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double: /* 0xab */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(171*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double: /* 0xac */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(172*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double: /* 0xad */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(173*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double: /* 0xae */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(174*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double: /* 0xaf */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(175*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_2addr: /* 0xb0 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(176*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_int_2addr: /* 0xb1 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(177*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_2addr: /* 0xb2 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(178*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_2addr: /* 0xb3 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(179*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_2addr: /* 0xb4 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(180*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_2addr: /* 0xb5 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(181*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_2addr: /* 0xb6 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(182*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_2addr: /* 0xb7 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(183*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_2addr: /* 0xb8 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(184*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_2addr: /* 0xb9 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(185*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_2addr: /* 0xba */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(186*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_long_2addr: /* 0xbb */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(187*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_long_2addr: /* 0xbc */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(188*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_long_2addr: /* 0xbd */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(189*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_long_2addr: /* 0xbe */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(190*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_long_2addr: /* 0xbf */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(191*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_long_2addr: /* 0xc0 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(192*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_long_2addr: /* 0xc1 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(193*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_long_2addr: /* 0xc2 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(194*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_long_2addr: /* 0xc3 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(195*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_long_2addr: /* 0xc4 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(196*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_long_2addr: /* 0xc5 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(197*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_float_2addr: /* 0xc6 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(198*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_float_2addr: /* 0xc7 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(199*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_float_2addr: /* 0xc8 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(200*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_float_2addr: /* 0xc9 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(201*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_float_2addr: /* 0xca */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(202*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_double_2addr: /* 0xcb */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(203*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_sub_double_2addr: /* 0xcc */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(204*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_double_2addr: /* 0xcd */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(205*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_double_2addr: /* 0xce */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(206*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_double_2addr: /* 0xcf */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(207*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit16: /* 0xd0 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(208*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int: /* 0xd1 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(209*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit16: /* 0xd2 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(210*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit16: /* 0xd3 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(211*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit16: /* 0xd4 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(212*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit16: /* 0xd5 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(213*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit16: /* 0xd6 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(214*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit16: /* 0xd7 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(215*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_add_int_lit8: /* 0xd8 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(216*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rsub_int_lit8: /* 0xd9 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(217*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_mul_int_lit8: /* 0xda */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(218*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_div_int_lit8: /* 0xdb */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(219*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_rem_int_lit8: /* 0xdc */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(220*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_and_int_lit8: /* 0xdd */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(221*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_or_int_lit8: /* 0xde */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(222*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_xor_int_lit8: /* 0xdf */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(223*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shl_int_lit8: /* 0xe0 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(224*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_shr_int_lit8: /* 0xe1 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(225*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_ushr_int_lit8: /* 0xe2 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(226*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_quick: /* 0xe3 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(227*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_wide_quick: /* 0xe4 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(228*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_object_quick: /* 0xe5 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(229*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_quick: /* 0xe6 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(230*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_wide_quick: /* 0xe7 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(231*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_object_quick: /* 0xe8 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(232*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_quick: /* 0xe9 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(233*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_virtual_range_quick: /* 0xea */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(234*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_boolean_quick: /* 0xeb */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(235*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_byte_quick: /* 0xec */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(236*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_char_quick: /* 0xed */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(237*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iput_short_quick: /* 0xee */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(238*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_boolean_quick: /* 0xef */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(239*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_byte_quick: /* 0xf0 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(240*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_char_quick: /* 0xf1 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(241*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_iget_short_quick: /* 0xf2 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(242*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_invoke_lambda: /* 0xf3 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(243*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_f4: /* 0xf4 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(244*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_capture_variable: /* 0xf5 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(245*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_create_lambda: /* 0xf6 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(246*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_liberate_variable: /* 0xf7 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(247*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_box_lambda: /* 0xf8 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(248*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unbox_lambda: /* 0xf9 */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(249*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fa: /* 0xfa */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(250*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fb: /* 0xfb */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(251*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fc: /* 0xfc */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(252*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fd: /* 0xfd */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(253*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_fe: /* 0xfe */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(254*128) + +/* ------------------------------ */ + .balign 128 +.L_ALT_op_unused_ff: /* 0xff */ +/* File: x86/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 + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(255*128) + + .balign 128 + .size artMterpAsmAltInstructionStart, .-artMterpAsmAltInstructionStart + .global artMterpAsmAltInstructionEnd +artMterpAsmAltInstructionEnd: +/* File: x86/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. + */ +#define MTERP_LOGGING 0 +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogDivideByZeroException +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogArrayIndexException +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogNegativeArraySizeException +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogNoSuchMethodException +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogNullObjectException +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG0(%esp) + call MterpLogExceptionThrownException +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG0(%esp) + movl THREAD_FLAGS_OFFSET(%eax), %eax + movl %eax, OUT_ARG2(%esp) + call 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: + movl rSELF, %eax + testl $-1, THREAD_EXCEPTION_OFFSET(%eax) + 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: + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpHandleException + testl %eax, %eax + 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) + /* resume execution at catch block */ + 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: + movl rSELF, %eax + EXPORT_PC + testl $(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck + REFRESH_IBASE +1: + GOTO_NEXT + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogFallback +#endif +MterpCommonFallback: + xor %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: + movl OFF_FP_RESULT_REGISTER(rFP), %edx + movl %eax, (%edx) + 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 + + .cfi_endproc + .size ExecuteMterpImpl, .-ExecuteMterpImpl + diff --git a/runtime/interpreter/mterp/rebuild.sh b/runtime/interpreter/mterp/rebuild.sh index a325fff93e..8b26976328 100755 --- a/runtime/interpreter/mterp/rebuild.sh +++ b/runtime/interpreter/mterp/rebuild.sh @@ -21,4 +21,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; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done +for arch in arm x86; do TARGET_ARCH_EXT=$arch make -f Makefile_mterp; done diff --git a/runtime/interpreter/mterp/x86/alt_stub.S b/runtime/interpreter/mterp/x86/alt_stub.S new file mode 100644 index 0000000000..6462fc5919 --- /dev/null +++ b/runtime/interpreter/mterp/x86/alt_stub.S @@ -0,0 +1,20 @@ +/* + * Inter-instruction transfer stub. Call out to MterpCheckBefore to handle + * any interesting requests and then jump to the real instruction + * handler. Unlike the Arm handler, we can't do this as a tail call + * because rIBASE is caller save and we need to reload it. + * + * Note that unlike in the Arm implementation, we should never arrive + * here with a zero breakFlag because we always refresh rIBASE on + * return. + */ + .extern MterpCheckBefore + EXPORT_PC + + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + call MterpCheckBefore # (self, shadow_frame) + REFRESH_IBASE + jmp .L_op_nop+(${opnum}*${handler_size_bytes}) diff --git a/runtime/interpreter/mterp/x86/bincmp.S b/runtime/interpreter/mterp/x86/bincmp.S new file mode 100644 index 0000000000..a9a8c3ae7f --- /dev/null +++ b/runtime/interpreter/mterp/x86/bincmp.S @@ -0,0 +1,28 @@ +/* + * 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 */ + movzx rINSTbl, %ecx # ecx <- A+ + andb $$0xf, %cl # ecx <- A + 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 + j${revcmp} 1f + movswl 2(rPC),%eax # Get signed branch offset +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/bindiv.S b/runtime/interpreter/mterp/x86/bindiv.S new file mode 100644 index 0000000000..742f758bc4 --- /dev/null +++ b/runtime/interpreter/mterp/x86/bindiv.S @@ -0,0 +1,48 @@ +%default {"result":"","special":"","rem":""} +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + GET_VREG %ecx %ecx # ecx <- vCC + mov rIBASE, LOCAL0(%esp) + testl %ecx, %ecx + je common_errDivideByZero + movl %eax, %edx + orl %ecx, %edx + test $$0xFFFFFF00, %edx # If both arguments are less + # than 8-bit and +ve + jz .L${opcode}_8 # Do 8-bit divide + test $$0xFFFF0000, %edx # If both arguments are less + # than 16-bit and +ve + jz .L${opcode}_16 # Do 16-bit divide + cmpl $$-1, %ecx + jne .L${opcode}_32 + cmpl $$0x80000000, %eax + jne .L${opcode}_32 + movl $special, $result + jmp .L${opcode}_finish +.L${opcode}_32: + cltd + idivl %ecx + jmp .L${opcode}_finish +.L${opcode}_8: + div %cl # 8-bit divide otherwise. + # Remainder in %ah, quotient in %al + .if $rem + movl %eax, %edx + shr $$8, %edx + .else + andl $$0x000000FF, %eax + .endif + jmp .L${opcode}_finish +.L${opcode}_16: + xorl %edx, %edx # Clear %edx before divide + div %cx +.L${opcode}_finish: + SET_VREG $result rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/bindiv2addr.S b/runtime/interpreter/mterp/x86/bindiv2addr.S new file mode 100644 index 0000000000..ee7c523b0a --- /dev/null +++ b/runtime/interpreter/mterp/x86/bindiv2addr.S @@ -0,0 +1,29 @@ +%default {"result":"","special":""} +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem/2addr vA, vB */ + movzx rINSTbl, %ecx # eax <- BA + mov rIBASE, LOCAL0(%esp) + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx %ecx # eax <- vBB + andb $$0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- vBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $$-1, %ecx + jne .L${opcode}_continue_div2addr + cmpl $$0x80000000, %eax + jne .L${opcode}_continue_div2addr + movl $special, $result + SET_VREG $result rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.L${opcode}_continue_div2addr: + cltd + idivl %ecx + SET_VREG $result rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/bindivLit16.S b/runtime/interpreter/mterp/x86/bindivLit16.S new file mode 100644 index 0000000000..a2c4334cf0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/bindivLit16.S @@ -0,0 +1,29 @@ +%default {"result":"","special":""} +/* + * 32-bit binary div/rem operation. Handles special case of op0=minint and + * op1=-1. + */ + /* div/rem/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $$0xf, rINSTbl # rINST <- A + testl %ecx, %ecx + je common_errDivideByZero + cmpl $$-1, %ecx + jne .L${opcode}_continue_div + cmpl $$0x80000000, %eax + jne .L${opcode}_continue_div + movl $special, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.L${opcode}_continue_div: + mov rIBASE, LOCAL0(%esp) + cltd + idivl %ecx + SET_VREG $result rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/bindivLit8.S b/runtime/interpreter/mterp/x86/bindivLit8.S new file mode 100644 index 0000000000..61bee0621c --- /dev/null +++ b/runtime/interpreter/mterp/x86/bindivLit8.S @@ -0,0 +1,26 @@ +%default {"result":"","special":""} +/* + * 32-bit div/rem "lit8" binary operation. Handles special case of + * op0=minint & op1=-1 + */ + /* div/rem/lit8 vAA, vBB, #+CC */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + testl %ecx, %ecx + je common_errDivideByZero + cmpl $$0x80000000, %eax + jne .L${opcode}_continue_div + cmpl $$-1, %ecx + jne .L${opcode}_continue_div + movl $special, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.L${opcode}_continue_div: + mov rIBASE, LOCAL0(%esp) + cltd + idivl %ecx + SET_VREG $result rINST + mov LOCAL0(%esp), rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/binop.S b/runtime/interpreter/mterp/x86/binop.S new file mode 100644 index 0000000000..5383f25fa1 --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + $instr # ex: addl (rFP,%ecx,4),%eax + SET_VREG $result rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/binop1.S b/runtime/interpreter/mterp/x86/binop1.S new file mode 100644 index 0000000000..cd51d0c27f --- /dev/null +++ b/runtime/interpreter/mterp/x86/binop1.S @@ -0,0 +1,13 @@ +%default {"result":"%eax","tmp":"%ecx"} +/* + * Generic 32-bit binary operation in which both operands loaded to + * registers (op0 in eax, op1 in ecx). + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + GET_VREG %ecx %ecx # eax <- vBB + $instr # ex: addl %ecx,%eax + SET_VREG $result rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/binop2addr.S b/runtime/interpreter/mterp/x86/binop2addr.S new file mode 100644 index 0000000000..abee4dbd46 --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $$4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $$0xf, %cl # ecx <- A + $instr # for ex: addl %eax,(rFP,%ecx,4) + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/binopLit16.S b/runtime/interpreter/mterp/x86/binopLit16.S new file mode 100644 index 0000000000..6c7fe61e10 --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $$0xf, rINSTbl # rINST <- A + $instr # for example: addl %ecx, %eax + SET_VREG $result rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/binopLit8.S b/runtime/interpreter/mterp/x86/binopLit8.S new file mode 100644 index 0000000000..924685df0e --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + $instr # ex: addl %ecx,%eax + SET_VREG $result rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/binopWide.S b/runtime/interpreter/mterp/x86/binopWide.S new file mode 100644 index 0000000000..9f7106e620 --- /dev/null +++ b/runtime/interpreter/mterp/x86/binopWide.S @@ -0,0 +1,15 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop vAA, vBB, vCC */ + movzbl 2(rPC),%eax # eax <- BB + movzbl 3(rPC),%ecx # ecx <- CC + movl rIBASE,LOCAL0(%esp) # save rIBASE + GET_VREG rIBASE %eax # rIBASE <- v[BB+0] + GET_VREG_HIGH %eax %eax # eax <- v[BB+1] + $instr1 # ex: addl (rFP,%ecx,4),rIBASE + $instr2 # ex: adcl 4(rFP,%ecx,4),%eax + SET_VREG rIBASE rINST # v[AA+0] <- rIBASE + movl LOCAL0(%esp),rIBASE # restore rIBASE + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/binopWide2addr.S b/runtime/interpreter/mterp/x86/binopWide2addr.S new file mode 100644 index 0000000000..7560af44fe --- /dev/null +++ b/runtime/interpreter/mterp/x86/binopWide2addr.S @@ -0,0 +1,13 @@ +/* + * Generic 64-bit binary operation. + */ + /* binop/2addr vA, vB */ + movzbl rINSTbl,%ecx # ecx<- BA + sarl $$4,%ecx # ecx<- B + GET_VREG %eax %ecx # eax<- v[B+0] + GET_VREG_HIGH %ecx %ecx # eax<- v[B+1] + andb $$0xF,rINSTbl # rINST<- A + $instr1 # ex: addl %eax,(rFP,rINST,4) + $instr2 # ex: adcl %ecx,4(rFP,rINST,4) + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/cvtfp_int.S b/runtime/interpreter/mterp/x86/cvtfp_int.S new file mode 100644 index 0000000000..a8bad639d6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/cvtfp_int.S @@ -0,0 +1,61 @@ +%default {"srcdouble":"1","tgtlong":"1"} +/* 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. This model + * differs from what is delivered normally via the x86 fpu, so we have + * to play some games. + */ + /* float/double to int/long vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $$4, rINST # rINST <- B + .if $srcdouble + fldl VREG_ADDRESS(rINST) # %st0 <- vB + .else + flds VREG_ADDRESS(rINST) # %st0 <- vB + .endif + ftst + fnstcw LOCAL0(%esp) # remember original rounding mode + movzwl LOCAL0(%esp), %eax + movb $$0xc, %ah + movw %ax, LOCAL0+2(%esp) + fldcw LOCAL0+2(%esp) # set "to zero" rounding mode + andb $$0xf, %cl # ecx <- A + .if $tgtlong + fistpll VREG_ADDRESS(%ecx) # convert and store + .else + fistpl VREG_ADDRESS(%ecx) # convert and store + .endif + fldcw LOCAL0(%esp) # restore previous rounding mode + .if $tgtlong + movl $$0x80000000, %eax + xorl VREG_HIGH_ADDRESS(%ecx), %eax + orl VREG_ADDRESS(%ecx), %eax + .else + cmpl $$0x80000000, VREG_ADDRESS(%ecx) + .endif + je .L${opcode}_special_case # fix up result + +.L${opcode}_finish: + xor %eax, %eax + mov %eax, VREG_REF_ADDRESS(%ecx) + .if $tgtlong + mov %eax, VREG_REF_HIGH_ADDRESS(%ecx) + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + +.L${opcode}_special_case: + fnstsw %ax + sahf + jp .L${opcode}_isNaN + adcl $$-1, VREG_ADDRESS(%ecx) + .if $tgtlong + adcl $$-1, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .L${opcode}_finish +.L${opcode}_isNaN: + movl $$0, VREG_ADDRESS(%ecx) + .if $tgtlong + movl $$0, VREG_HIGH_ADDRESS(%ecx) + .endif + jmp .L${opcode}_finish diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S new file mode 100644 index 0000000000..a24ef70df5 --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 ExecuteMterpImpl + .type ExecuteMterpImpl, %function + +/* + * On entry: + * 0 Thread* self + * 1 code_item + * 2 ShadowFrame + * 3 JValue* result_register + * + */ + +ExecuteMterpImpl: + .cfi_startproc + /* 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 + + /* Remember the return register */ + movl IN_ARG3(%esp), %eax + movl %eax, SHADOWFRAME_RESULT_REGISTER_OFFSET(%edx) + + /* Remember the code_item */ + movl IN_ARG1(%esp), %ecx + movl %ecx, SHADOWFRAME_CODE_ITEM_OFFSET(%edx) + + /* set up "named" registers */ + movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(%edx), %eax + leal SHADOWFRAME_VREGS_OFFSET(%edx), rFP + leal (rFP, %eax, 4), rREFS + movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax + lea CODEITEM_INSNS_OFFSET(%ecx), rPC + lea (rPC, %eax, 2), rPC + EXPORT_PC + + /* Starting ibase */ + REFRESH_IBASE + + /* start executing the instruction at rPC */ + FETCH_INST + GOTO_NEXT + /* NOTE: no fallthrough */ diff --git a/runtime/interpreter/mterp/x86/fallback.S b/runtime/interpreter/mterp/x86/fallback.S new file mode 100644 index 0000000000..8d61166f63 --- /dev/null +++ b/runtime/interpreter/mterp/x86/fallback.S @@ -0,0 +1,3 @@ +/* Transfer stub to alternate interpreter */ + jmp MterpFallback + diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S new file mode 100644 index 0000000000..a2a36c4f64 --- /dev/null +++ b/runtime/interpreter/mterp/x86/footer.S @@ -0,0 +1,186 @@ +/* + * =========================================================================== + * 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. + */ +#define MTERP_LOGGING 0 +common_errDivideByZero: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogDivideByZeroException +#endif + jmp MterpCommonFallback + +common_errArrayIndex: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogArrayIndexException +#endif + jmp MterpCommonFallback + +common_errNegativeArraySize: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogNegativeArraySizeException +#endif + jmp MterpCommonFallback + +common_errNoSuchMethod: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogNoSuchMethodException +#endif + jmp MterpCommonFallback + +common_errNullObject: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogNullObjectException +#endif + jmp MterpCommonFallback + +common_exceptionThrown: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG0(%esp) + call MterpLogExceptionThrownException +#endif + jmp MterpCommonFallback + +MterpSuspendFallback: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG0(%esp) + movl THREAD_FLAGS_OFFSET(%eax), %eax + movl %eax, OUT_ARG2(%esp) + call 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: + movl rSELF, %eax + testl $$-1, THREAD_EXCEPTION_OFFSET(%eax) + 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: + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpHandleException + testl %eax, %eax + 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) + /* resume execution at catch block */ + 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: + movl rSELF, %eax + EXPORT_PC + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck + REFRESH_IBASE +1: + GOTO_NEXT + +/* + * Bail out to reference interpreter. + */ +MterpFallback: + EXPORT_PC +#if MTERP_LOGGING + movl rSELF, %eax + movl %eax, OUT_ARG0(%esp) + lea OFF_FP_SHADOWFRAME(rFP), %ecx + movl %ecx, OUT_ARG1(%esp) + call MterpLogFallback +#endif +MterpCommonFallback: + xor %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: + movl OFF_FP_RESULT_REGISTER(rFP), %edx + movl %eax, (%edx) + 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 + + .cfi_endproc + .size ExecuteMterpImpl, .-ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/x86/fpcmp.S b/runtime/interpreter/mterp/x86/fpcmp.S new file mode 100644 index 0000000000..2b98667974 --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 */ + movzbl 3(rPC), %ecx # ecx<- CC + movzbl 2(rPC), %eax # eax<- BB + movs${suff} VREG_ADDRESS(%eax), %xmm0 + xor %eax, %eax + ucomis${suff} VREG_ADDRESS(%ecx), %xmm0 + jp .L${opcode}_nan_is_${nanval} + je .L${opcode}_finish + jb .L${opcode}_less +.L${opcode}_nan_is_pos: + incl %eax + jmp .L${opcode}_finish +.L${opcode}_nan_is_neg: +.L${opcode}_less: + decl %eax +.L${opcode}_finish: + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/fpcvt.S b/runtime/interpreter/mterp/x86/fpcvt.S new file mode 100644 index 0000000000..780828518f --- /dev/null +++ b/runtime/interpreter/mterp/x86/fpcvt.S @@ -0,0 +1,17 @@ +%default {"instr":"","load":"","store":"","wide":"0"} +/* + * Generic 32-bit FP conversion operation. + */ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- A+ + sarl $$4, rINST # rINST <- B + $load VREG_ADDRESS(rINST) # %st0 <- vB + andb $$0xf, %cl # ecx <- A + $instr + $store VREG_ADDRESS(%ecx) # vA <- %st0 + .if $wide + CLEAR_WIDE_REF %ecx + .else + CLEAR_REF %ecx + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S new file mode 100644 index 0000000000..24817856fa --- /dev/null +++ b/runtime/interpreter/mterp/x86/header.S @@ -0,0 +1,282 @@ +/* + * 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 ABI general notes: + +Caller save set: + eax, edx, ecx, st(0)-st(7) +Callee save set: + ebx, esi, edi, ebp +Return regs: + 32-bit in eax + 64-bit in edx:eax (low-order 32 in eax) + fp on top of fp stack st(0) + +Parameters passed on stack, pushed right-to-left. On entry to target, first +parm is at 4(%esp). Traditional entry code is: + +functEntry: + push %ebp # save old frame pointer + mov %ebp,%esp # establish new frame pointer + sub FrameSize,%esp # Allocate storage for spill, locals & outs + +Once past the prologue, arguments are referenced at ((argno + 2)*4)(%ebp) + +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 notes: + +Some key interpreter variables will be assigned to registers. + + nick reg purpose + rPC esi interpreted program counter, used for fetching instructions + rFP edi 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 edx base of instruction handler table + rREFS ebp 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" + +/* Frame size must be 16-byte aligned. + * Remember about 4 bytes for return address + */ +#define FRAME_SIZE 44 + +/* 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) +/* 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) +/* Out Arg offsets, relative to %esp */ +#define OUT_ARG3 ( 12) +#define OUT_ARG2 ( 8) +#define OUT_ARG1 ( 4) +#define OUT_ARG0 ( 0) /* <- ExecuteMterpImpl esp + 0 */ + +/* During bringup, we'll use the shadow frame model instead of rFP */ +/* single-purpose registers, given names for clarity */ +#define rSELF IN_ARG0(%esp) +#define rPC %esi +#define rFP %edi +#define rINST %ebx +#define rINSTw %bx +#define rINSTbh %bh +#define rINSTbl %bl +#define rIBASE %edx +#define rREFS %ebp + +/* + * 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) + +/* + * + * The reference interpreter performs explicit suspect checks, which is somewhat wasteful. + * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually + * mterp should do so as well. + */ +#define MTERP_SUSPEND 0 + +/* + * "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must + * be done *before* something throws. + * + * 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 + movl 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. + * + * TODO: Consider spilling the IBase instead of restoring it from Thread structure. + */ +.macro REFRESH_IBASE + movl rSELF, rIBASE + movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE +.endm + +/* + * If rSELF is already loaded then we can use it from known reg. + */ +.macro REFRESH_IBASE_FROM_SELF _reg + movl THREAD_CURRENT_IBASE_OFFSET(\_reg), 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 + movzwl (rPC), rINST +.endm + +/* + * Remove opcode from rINST, compute the address of handler and jump to it. + */ +.macro GOTO_NEXT + movzx rINSTbl,%eax + movzbl rINSTbh,rINST + shll $$${handler_size_bits}, %eax + addl rIBASE, %eax + jmp *%eax +.endm + +/* + * Advance rPC by instruction count. + */ +.macro ADVANCE_PC _count + leal 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_HIGH_ADDRESS(_vreg) 4(rFP,_vreg,4) +#define VREG_REF_ADDRESS(_vreg) (rREFS,_vreg,4) +#define VREG_REF_HIGH_ADDRESS(_vreg) 4(rREFS,_vreg,4) + +.macro GET_VREG _reg _vreg + movl (rFP,\_vreg,4), \_reg +.endm + +/* Read wide value to xmm. */ +.macro GET_WIDE_FP_VREG _reg _vreg + movq (rFP,\_vreg,4), \_reg +.endm + +.macro SET_VREG _reg _vreg + movl \_reg, (rFP,\_vreg,4) + movl $$0, (rREFS,\_vreg,4) +.endm + +/* Write wide value from xmm. xmm is clobbered. */ +.macro SET_WIDE_FP_VREG _reg _vreg + movq \_reg, (rFP,\_vreg,4) + pxor \_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 $$0, 4(rREFS,\_vreg,4) +.endm + +.macro CLEAR_REF _vreg + movl $$0, (rREFS,\_vreg,4) +.endm + +.macro CLEAR_WIDE_REF _vreg + movl $$0, (rREFS,\_vreg,4) + movl $$0, 4(rREFS,\_vreg,4) +.endm diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S new file mode 100644 index 0000000000..80f782285a --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 + EXPORT_PC + movl rSELF, %ecx + movl %ecx, OUT_ARG0(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG1(%esp) + movl rPC, OUT_ARG2(%esp) + REFRESH_INST ${opnum} + movl rINST, OUT_ARG3(%esp) + call $helper + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_add_double.S b/runtime/interpreter/mterp/x86/op_add_double.S new file mode 100644 index 0000000000..de2708f442 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_double.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_add_double_2addr.S b/runtime/interpreter/mterp/x86/op_add_double_2addr.S new file mode 100644 index 0000000000..538c9ab76e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_double_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"adds","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_add_float.S b/runtime/interpreter/mterp/x86/op_add_float.S new file mode 100644 index 0000000000..80b173658b --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_float.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_add_float_2addr.S b/runtime/interpreter/mterp/x86/op_add_float_2addr.S new file mode 100644 index 0000000000..6649253976 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_float_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"adds","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_add_int.S b/runtime/interpreter/mterp/x86/op_add_int.S new file mode 100644 index 0000000000..f71a56b65e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_int.S @@ -0,0 +1 @@ +%include "x86/binop.S" {"instr":"addl (rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_add_int_2addr.S b/runtime/interpreter/mterp/x86/op_add_int_2addr.S new file mode 100644 index 0000000000..5d43b6517d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_int_2addr.S @@ -0,0 +1 @@ +%include "x86/binop2addr.S" {"instr":"addl %eax, (rFP,%ecx,4)"} diff --git a/runtime/interpreter/mterp/x86/op_add_int_lit16.S b/runtime/interpreter/mterp/x86/op_add_int_lit16.S new file mode 100644 index 0000000000..4f34d173f2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_int_lit16.S @@ -0,0 +1 @@ +%include "x86/binopLit16.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_add_int_lit8.S b/runtime/interpreter/mterp/x86/op_add_int_lit8.S new file mode 100644 index 0000000000..3f14744dcd --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"addl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_add_long.S b/runtime/interpreter/mterp/x86/op_add_long.S new file mode 100644 index 0000000000..dce0c26527 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_long.S @@ -0,0 +1 @@ +%include "x86/binopWide.S" {"instr1":"addl (rFP,%ecx,4), rIBASE", "instr2":"adcl 4(rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_add_long_2addr.S b/runtime/interpreter/mterp/x86/op_add_long_2addr.S new file mode 100644 index 0000000000..7847640e38 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_add_long_2addr.S @@ -0,0 +1 @@ +%include "x86/binopWide2addr.S" {"instr1":"addl %eax, (rFP,rINST,4)","instr2":"adcl %ecx, 4(rFP,rINST,4)"} diff --git a/runtime/interpreter/mterp/x86/op_aget.S b/runtime/interpreter/mterp/x86/op_aget.S new file mode 100644 index 0000000000..52b5236a8c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aget.S @@ -0,0 +1,19 @@ +%default { "load":"movl", "shift":"4", "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 + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + $load $data_offset(%eax,%ecx,$shift), %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_aget_boolean.S b/runtime/interpreter/mterp/x86/op_aget_boolean.S new file mode 100644 index 0000000000..d910c94e45 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aget_boolean.S @@ -0,0 +1 @@ +%include "x86/op_aget.S" { "load":"movzbl", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aget_byte.S b/runtime/interpreter/mterp/x86/op_aget_byte.S new file mode 100644 index 0000000000..aba9ffc25a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aget_byte.S @@ -0,0 +1 @@ +%include "x86/op_aget.S" { "load":"movsbl", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aget_char.S b/runtime/interpreter/mterp/x86/op_aget_char.S new file mode 100644 index 0000000000..748e4108b3 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aget_char.S @@ -0,0 +1 @@ +%include "x86/op_aget.S" { "load":"movzwl", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aget_object.S b/runtime/interpreter/mterp/x86/op_aget_object.S new file mode 100644 index 0000000000..61f3e9194f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aget_object.S @@ -0,0 +1,20 @@ +/* + * Array object get. vAA <- vBB[vCC]. + * + * for: aget-object + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # ecs <- vCC (requested index) + EXPORT_PC + movl %eax, OUT_ARG0(%esp) + movl %ecx, OUT_ARG1(%esp) + call artAGetObjectFromMterp # (array, index) + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + SET_VREG_OBJECT %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_aget_short.S b/runtime/interpreter/mterp/x86/op_aget_short.S new file mode 100644 index 0000000000..6eaf5d922d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aget_short.S @@ -0,0 +1 @@ +%include "x86/op_aget.S" { "load":"movswl", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aget_wide.S b/runtime/interpreter/mterp/x86/op_aget_wide.S new file mode 100644 index 0000000000..663adc67f5 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aget_wide.S @@ -0,0 +1,16 @@ +/* + * Array get, 64 bits. vAA <- vBB[vCC]. + */ + /* aget-wide vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax + movq (%eax), %xmm0 # xmm0 <- vBB[vCC] + SET_WIDE_FP_VREG %xmm0 rINST # vAA <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_and_int.S b/runtime/interpreter/mterp/x86/op_and_int.S new file mode 100644 index 0000000000..6272c4e30d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_and_int.S @@ -0,0 +1 @@ +%include "x86/binop.S" {"instr":"andl (rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_and_int_2addr.S b/runtime/interpreter/mterp/x86/op_and_int_2addr.S new file mode 100644 index 0000000000..95df873171 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_and_int_2addr.S @@ -0,0 +1 @@ +%include "x86/binop2addr.S" {"instr":"andl %eax, (rFP,%ecx,4)"} diff --git a/runtime/interpreter/mterp/x86/op_and_int_lit16.S b/runtime/interpreter/mterp/x86/op_and_int_lit16.S new file mode 100644 index 0000000000..b062064106 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_and_int_lit16.S @@ -0,0 +1 @@ +%include "x86/binopLit16.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_and_int_lit8.S b/runtime/interpreter/mterp/x86/op_and_int_lit8.S new file mode 100644 index 0000000000..99915dfa30 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_and_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"andl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_and_long.S b/runtime/interpreter/mterp/x86/op_and_long.S new file mode 100644 index 0000000000..f8514ea802 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_and_long.S @@ -0,0 +1 @@ +%include "x86/binopWide.S" {"instr1":"andl (rFP,%ecx,4), rIBASE", "instr2":"andl 4(rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_and_long_2addr.S b/runtime/interpreter/mterp/x86/op_and_long_2addr.S new file mode 100644 index 0000000000..37249b8153 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_and_long_2addr.S @@ -0,0 +1 @@ +%include "x86/binopWide2addr.S" {"instr1":"andl %eax, (rFP,rINST,4)","instr2":"andl %ecx, 4(rFP,rINST,4)"} diff --git a/runtime/interpreter/mterp/x86/op_aput.S b/runtime/interpreter/mterp/x86/op_aput.S new file mode 100644 index 0000000000..2ea465df90 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aput.S @@ -0,0 +1,20 @@ +%default { "reg":"rINST", "store":"movl", "shift":"4", "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 + * + */ + /* op vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal $data_offset(%eax,%ecx,$shift), %eax + GET_VREG rINST rINST + $store $reg, (%eax) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_aput_boolean.S b/runtime/interpreter/mterp/x86/op_aput_boolean.S new file mode 100644 index 0000000000..e7fdd53924 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aput_boolean.S @@ -0,0 +1 @@ +%include "x86/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BOOLEAN_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aput_byte.S b/runtime/interpreter/mterp/x86/op_aput_byte.S new file mode 100644 index 0000000000..491d03cd72 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aput_byte.S @@ -0,0 +1 @@ +%include "x86/op_aput.S" { "reg":"rINSTbl", "store":"movb", "shift":"1", "data_offset":"MIRROR_BYTE_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aput_char.S b/runtime/interpreter/mterp/x86/op_aput_char.S new file mode 100644 index 0000000000..ca42cf0c74 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aput_char.S @@ -0,0 +1 @@ +%include "x86/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_CHAR_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aput_object.S b/runtime/interpreter/mterp/x86/op_aput_object.S new file mode 100644 index 0000000000..2af5acb288 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aput_object.S @@ -0,0 +1,15 @@ +/* + * Store an object into an array. vBB[vCC] <- vAA. + */ + /* op vAA, vBB, vCC */ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST ${opnum} + movl rINST, OUT_ARG2(%esp) + call MterpAputObject # (array, index) + REFRESH_IBASE + testl %eax, %eax + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_aput_short.S b/runtime/interpreter/mterp/x86/op_aput_short.S new file mode 100644 index 0000000000..5e634821cc --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aput_short.S @@ -0,0 +1 @@ +%include "x86/op_aput.S" { "reg":"rINSTw", "store":"movw", "shift":"2", "data_offset":"MIRROR_SHORT_ARRAY_DATA_OFFSET" } diff --git a/runtime/interpreter/mterp/x86/op_aput_wide.S b/runtime/interpreter/mterp/x86/op_aput_wide.S new file mode 100644 index 0000000000..7a3337166e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_aput_wide.S @@ -0,0 +1,17 @@ +/* + * Array put, 64 bits. vBB[vCC] <- vAA. + * + */ + /* aput-wide vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB (array object) + GET_VREG %ecx %ecx # 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. + leal MIRROR_WIDE_ARRAY_DATA_OFFSET(%eax,%ecx,8), %eax + GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- vAA + movq %xmm0, (%eax) # vBB[vCC] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_array_length.S b/runtime/interpreter/mterp/x86/op_array_length.S new file mode 100644 index 0000000000..3e42a7cddd --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_array_length.S @@ -0,0 +1,12 @@ +/* + * Return the length of an array. + */ + mov rINST, %eax # eax <- BA + sarl $$4, rINST # rINST <- B + GET_VREG %ecx rINST # ecx <- vB (object ref) + testl %ecx, %ecx # is null? + je common_errNullObject + andb $$0xf, %al # eax <- A + movl MIRROR_ARRAY_LENGTH_OFFSET(%ecx), rINST + SET_VREG rINST %eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_check_cast.S b/runtime/interpreter/mterp/x86/op_check_cast.S new file mode 100644 index 0000000000..018432a92a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_check_cast.S @@ -0,0 +1,18 @@ +/* + * Check to see if a cast from one class to another is allowed. + */ + /* check-cast vAA, class@BBBB */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + leal VREG_ADDRESS(rINST), %ecx + movl %ecx, OUT_ARG1(%esp) + movl OFF_FP_METHOD(rFP),%eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpCheckCast # (index, &obj, method, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_cmp_long.S b/runtime/interpreter/mterp/x86/op_cmp_long.S new file mode 100644 index 0000000000..bd86738950 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_cmp_long.S @@ -0,0 +1,27 @@ +/* + * 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 */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG_HIGH %eax %eax # eax <- v[BB+1], BB is clobbered + cmpl VREG_HIGH_ADDRESS(%ecx), %eax + jl .L${opcode}_smaller + jg .L${opcode}_bigger + movzbl 2(rPC), %eax # eax <- BB, restore BB + GET_VREG %eax %eax # eax <- v[BB] + sub VREG_ADDRESS(%ecx), %eax + ja .L${opcode}_bigger + jb .L${opcode}_smaller +.L${opcode}_finish: + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 + +.L${opcode}_bigger: + movl $$1, %eax + jmp .L${opcode}_finish + +.L${opcode}_smaller: + movl $$-1, %eax + jmp .L${opcode}_finish diff --git a/runtime/interpreter/mterp/x86/op_cmpg_double.S b/runtime/interpreter/mterp/x86/op_cmpg_double.S new file mode 100644 index 0000000000..a73ba550d2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_cmpg_double.S @@ -0,0 +1 @@ +%include "x86/fpcmp.S" {"suff":"d","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86/op_cmpg_float.S b/runtime/interpreter/mterp/x86/op_cmpg_float.S new file mode 100644 index 0000000000..648051b583 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_cmpg_float.S @@ -0,0 +1 @@ +%include "x86/fpcmp.S" {"suff":"s","nanval":"pos"} diff --git a/runtime/interpreter/mterp/x86/op_cmpl_double.S b/runtime/interpreter/mterp/x86/op_cmpl_double.S new file mode 100644 index 0000000000..058163e899 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_cmpl_double.S @@ -0,0 +1 @@ +%include "x86/fpcmp.S" {"suff":"d","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86/op_cmpl_float.S b/runtime/interpreter/mterp/x86/op_cmpl_float.S new file mode 100644 index 0000000000..302f078475 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_cmpl_float.S @@ -0,0 +1 @@ +%include "x86/fpcmp.S" {"suff":"s","nanval":"neg"} diff --git a/runtime/interpreter/mterp/x86/op_const.S b/runtime/interpreter/mterp/x86/op_const.S new file mode 100644 index 0000000000..dc695307b3 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const.S @@ -0,0 +1,4 @@ + /* const vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # grab all 32 bits at once + SET_VREG %eax rINST # vAA<- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_const_16.S b/runtime/interpreter/mterp/x86/op_const_16.S new file mode 100644 index 0000000000..f5707cf22d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_16.S @@ -0,0 +1,4 @@ + /* const/16 vAA, #+BBBB */ + movswl 2(rPC), %ecx # ecx <- ssssBBBB + SET_VREG %ecx rINST # vAA <- ssssBBBB + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_4.S b/runtime/interpreter/mterp/x86/op_const_4.S new file mode 100644 index 0000000000..c336411549 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_4.S @@ -0,0 +1,7 @@ + /* const/4 vA, #+B */ + movsx rINSTbl, %eax # eax <-ssssssBx + movl $$0xf, rINST + andl %eax, rINST # rINST <- A + sarl $$4, %eax + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_const_class.S b/runtime/interpreter/mterp/x86/op_const_class.S new file mode 100644 index 0000000000..eceb8bc60b --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_class.S @@ -0,0 +1,14 @@ + /* const/class vAA, Class@BBBB */ + EXPORT_PC + movzwl 2(rPC), %eax # eax<- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, OUT_ARG1(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpConstClass # (index, tgt_reg, shadow_frame, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_high16.S b/runtime/interpreter/mterp/x86/op_const_high16.S new file mode 100644 index 0000000000..da78d1b63e --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 rINST # vAA <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_string.S b/runtime/interpreter/mterp/x86/op_const_string.S new file mode 100644 index 0000000000..9acd6fe76c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_string.S @@ -0,0 +1,14 @@ + /* const/string vAA, String@BBBB */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, OUT_ARG1(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpConstString # (index, tgt_reg, shadow_frame, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S new file mode 100644 index 0000000000..5c728b269d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S @@ -0,0 +1,14 @@ + /* const/string vAA, String@BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, OUT_ARG1(%esp) + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpConstString # (index, tgt_reg, shadow_frame, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_const_wide.S b/runtime/interpreter/mterp/x86/op_const_wide.S new file mode 100644 index 0000000000..745490ea83 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_wide.S @@ -0,0 +1,7 @@ + /* const-wide vAA, #+HHHHhhhhBBBBbbbb */ + movl 2(rPC), %eax # eax <- lsw + movzbl rINSTbl, %ecx # ecx <- AA + movl 6(rPC), rINST # rINST <- msw + SET_VREG %eax %ecx + SET_VREG_HIGH rINST %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 5 diff --git a/runtime/interpreter/mterp/x86/op_const_wide_16.S b/runtime/interpreter/mterp/x86/op_const_wide_16.S new file mode 100644 index 0000000000..8029cfe80b --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_wide_16.S @@ -0,0 +1,8 @@ + /* const-wide/16 vAA, #+BBBB */ + movswl 2(rPC), %eax # eax <- ssssBBBB + movl rIBASE, %ecx # preserve rIBASE (cltd trashes it) + cltd # rIBASE:eax <- ssssssssssssBBBB + SET_VREG_HIGH rIBASE rINST # store msw + SET_VREG %eax rINST # store lsw + movl %ecx, rIBASE # restore rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_const_wide_32.S b/runtime/interpreter/mterp/x86/op_const_wide_32.S new file mode 100644 index 0000000000..3e23d3a106 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_wide_32.S @@ -0,0 +1,8 @@ + /* const-wide/32 vAA, #+BBBBbbbb */ + movl 2(rPC), %eax # eax <- BBBBbbbb + movl rIBASE, %ecx # preserve rIBASE (cltd trashes it) + cltd # rIBASE:eax <- ssssssssssssBBBB + SET_VREG_HIGH rIBASE rINST # store msw + SET_VREG %eax rINST # store lsw + movl %ecx, rIBASE # restore rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_const_wide_high16.S b/runtime/interpreter/mterp/x86/op_const_wide_high16.S new file mode 100644 index 0000000000..d2a11191b7 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_const_wide_high16.S @@ -0,0 +1,7 @@ + /* const-wide/high16 vAA, #+BBBB000000000000 */ + movzwl 2(rPC), %eax # eax <- 0000BBBB + sall $$16, %eax # eax <- BBBB0000 + SET_VREG_HIGH %eax rINST # v[AA+1] <- eax + xorl %eax, %eax + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_div_double.S b/runtime/interpreter/mterp/x86/op_div_double.S new file mode 100644 index 0000000000..575716dc9d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_double.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_div_double_2addr.S b/runtime/interpreter/mterp/x86/op_div_double_2addr.S new file mode 100644 index 0000000000..8229a31d67 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_double_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"divs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_div_float.S b/runtime/interpreter/mterp/x86/op_div_float.S new file mode 100644 index 0000000000..250f1dccc6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_float.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_div_float_2addr.S b/runtime/interpreter/mterp/x86/op_div_float_2addr.S new file mode 100644 index 0000000000..c30d148356 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_float_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"divs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_div_int.S b/runtime/interpreter/mterp/x86/op_div_int.S new file mode 100644 index 0000000000..5fc8fa519e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_int.S @@ -0,0 +1 @@ +%include "x86/bindiv.S" {"result":"%eax","special":"$0x80000000","rem":"0"} diff --git a/runtime/interpreter/mterp/x86/op_div_int_2addr.S b/runtime/interpreter/mterp/x86/op_div_int_2addr.S new file mode 100644 index 0000000000..04cf1bae6a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_int_2addr.S @@ -0,0 +1 @@ +%include "x86/bindiv2addr.S" {"result":"%eax","special":"$0x80000000"} diff --git a/runtime/interpreter/mterp/x86/op_div_int_lit16.S b/runtime/interpreter/mterp/x86/op_div_int_lit16.S new file mode 100644 index 0000000000..dd396bb68a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_int_lit16.S @@ -0,0 +1 @@ +%include "x86/bindivLit16.S" {"result":"%eax","special":"$0x80000000"} diff --git a/runtime/interpreter/mterp/x86/op_div_int_lit8.S b/runtime/interpreter/mterp/x86/op_div_int_lit8.S new file mode 100644 index 0000000000..3cbd9d0cf5 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_int_lit8.S @@ -0,0 +1 @@ +%include "x86/bindivLit8.S" {"result":"%eax","special":"$0x80000000"} diff --git a/runtime/interpreter/mterp/x86/op_div_long.S b/runtime/interpreter/mterp/x86/op_div_long.S new file mode 100644 index 0000000000..577282686d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_long.S @@ -0,0 +1,23 @@ +%default {"routine":"art_quick_ldiv"} +/* art_quick_* methods has quick abi, + * so use eax, ecx, edx, ebx for args + */ + /* div vAA, vBB, vCC */ + .extern $routine + mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx + mov rINST, LOCAL1(%esp) # save rINST/%ebx + movzbl 3(rPC), %eax # eax <- CC + GET_VREG %ecx %eax + GET_VREG_HIGH %ebx %eax + movl %ecx, %edx + orl %ebx, %ecx + jz common_errDivideByZero + movzbl 2(rPC), %eax # eax <- BB + GET_VREG_HIGH %ecx %eax + GET_VREG %eax %eax + call $routine + mov LOCAL1(%esp), rINST # restore rINST/%ebx + SET_VREG_HIGH rIBASE rINST + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_div_long_2addr.S b/runtime/interpreter/mterp/x86/op_div_long_2addr.S new file mode 100644 index 0000000000..26960420cb --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_div_long_2addr.S @@ -0,0 +1,25 @@ +%default {"routine":"art_quick_ldiv"} +/* art_quick_* methods has quick abi, + * so use eax, ecx, edx, ebx for args + */ + /* div/2addr vA, vB */ + .extern $routine + mov rIBASE, LOCAL0(%esp) # save rIBASE/%edx + movzbl rINSTbl, %eax + shrl $$4, %eax # eax <- B + andb $$0xf, rINSTbl # rINST <- A + mov rINST, LOCAL1(%esp) # save rINST/%ebx + movl %ebx, %ecx + GET_VREG %edx %eax + GET_VREG_HIGH %ebx %eax + movl %edx, %eax + orl %ebx, %eax + jz common_errDivideByZero + GET_VREG %eax %ecx + GET_VREG_HIGH %ecx %ecx + call $routine + mov LOCAL1(%esp), rINST # restore rINST/%ebx + SET_VREG_HIGH rIBASE rINST + SET_VREG %eax rINST + mov LOCAL0(%esp), rIBASE # restore rIBASE/%edx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_double_to_float.S b/runtime/interpreter/mterp/x86/op_double_to_float.S new file mode 100644 index 0000000000..5135d60ed7 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_double_to_float.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"load":"fldl","store":"fstps"} diff --git a/runtime/interpreter/mterp/x86/op_double_to_int.S b/runtime/interpreter/mterp/x86/op_double_to_int.S new file mode 100644 index 0000000000..9c4e11cf9e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_double_to_int.S @@ -0,0 +1 @@ +%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"0"} diff --git a/runtime/interpreter/mterp/x86/op_double_to_long.S b/runtime/interpreter/mterp/x86/op_double_to_long.S new file mode 100644 index 0000000000..fe0eee24d0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_double_to_long.S @@ -0,0 +1 @@ +%include "x86/cvtfp_int.S" {"srcdouble":"1","tgtlong":"1"} diff --git a/runtime/interpreter/mterp/x86/op_fill_array_data.S b/runtime/interpreter/mterp/x86/op_fill_array_data.S new file mode 100644 index 0000000000..0cb05f6cf7 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_fill_array_data.S @@ -0,0 +1,12 @@ + /* fill-array-data vAA, +BBBBBBBB */ + EXPORT_PC + movl 2(rPC), %ecx # ecx <- BBBBbbbb + leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2 + GET_VREG %eax rINST # eax <- vAA (array object) + movl %eax, OUT_ARG0(%esp) + movl %ecx, OUT_ARG1(%esp) + call MterpFillArrayData # (obj, payload) + REFRESH_IBASE + testl %eax, %eax # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_filled_new_array.S b/runtime/interpreter/mterp/x86/op_filled_new_array.S new file mode 100644 index 0000000000..c08b09f4d2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_filled_new_array.S @@ -0,0 +1,20 @@ +%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 + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) + call $helper + REFRESH_IBASE + testl %eax, %eax # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_filled_new_array_range.S b/runtime/interpreter/mterp/x86/op_filled_new_array_range.S new file mode 100644 index 0000000000..841059e4e2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_filled_new_array_range.S @@ -0,0 +1 @@ +%include "x86/op_filled_new_array.S" { "helper":"MterpFilledNewArrayRange" } diff --git a/runtime/interpreter/mterp/x86/op_float_to_double.S b/runtime/interpreter/mterp/x86/op_float_to_double.S new file mode 100644 index 0000000000..12a3e14caa --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_float_to_double.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"load":"flds","store":"fstpl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86/op_float_to_int.S b/runtime/interpreter/mterp/x86/op_float_to_int.S new file mode 100644 index 0000000000..ac57388744 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_float_to_int.S @@ -0,0 +1 @@ +%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"0"} diff --git a/runtime/interpreter/mterp/x86/op_float_to_long.S b/runtime/interpreter/mterp/x86/op_float_to_long.S new file mode 100644 index 0000000000..be1d9821b3 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_float_to_long.S @@ -0,0 +1 @@ +%include "x86/cvtfp_int.S" {"srcdouble":"0","tgtlong":"1"} diff --git a/runtime/interpreter/mterp/x86/op_goto.S b/runtime/interpreter/mterp/x86/op_goto.S new file mode 100644 index 0000000000..411399d3ad --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_goto.S @@ -0,0 +1,19 @@ +/* + * 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 */ + movsbl rINSTbl, %eax # eax <- ssssssAA + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_goto_16.S b/runtime/interpreter/mterp/x86/op_goto_16.S new file mode 100644 index 0000000000..4f04f9e479 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_goto_16.S @@ -0,0 +1,19 @@ +/* + * 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 */ + movswl 2(rPC), %eax # eax <- ssssAAAA + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_goto_32.S b/runtime/interpreter/mterp/x86/op_goto_32.S new file mode 100644 index 0000000000..48f6e5afd7 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_goto_32.S @@ -0,0 +1,24 @@ +/* + * 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". Because + * we need the V bit set, we'll use an adds to convert from Dalvik + * offset to byte offset. + */ + /* goto/32 +AAAAAAAA */ + movl 2(rPC), %eax # eax <- AAAAAAAA + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 1f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_if_eq.S b/runtime/interpreter/mterp/x86/op_if_eq.S new file mode 100644 index 0000000000..5413d98580 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_eq.S @@ -0,0 +1 @@ +%include "x86/bincmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86/op_if_eqz.S b/runtime/interpreter/mterp/x86/op_if_eqz.S new file mode 100644 index 0000000000..53dc99ef90 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_eqz.S @@ -0,0 +1 @@ +%include "x86/zcmp.S" { "revcmp":"ne" } diff --git a/runtime/interpreter/mterp/x86/op_if_ge.S b/runtime/interpreter/mterp/x86/op_if_ge.S new file mode 100644 index 0000000000..c2ba3c6d51 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_ge.S @@ -0,0 +1 @@ +%include "x86/bincmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86/op_if_gez.S b/runtime/interpreter/mterp/x86/op_if_gez.S new file mode 100644 index 0000000000..cd2c77237d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_gez.S @@ -0,0 +1 @@ +%include "x86/zcmp.S" { "revcmp":"l" } diff --git a/runtime/interpreter/mterp/x86/op_if_gt.S b/runtime/interpreter/mterp/x86/op_if_gt.S new file mode 100644 index 0000000000..9fe84bb786 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_gt.S @@ -0,0 +1 @@ +%include "x86/bincmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86/op_if_gtz.S b/runtime/interpreter/mterp/x86/op_if_gtz.S new file mode 100644 index 0000000000..b454ffdb34 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_gtz.S @@ -0,0 +1 @@ +%include "x86/zcmp.S" { "revcmp":"le" } diff --git a/runtime/interpreter/mterp/x86/op_if_le.S b/runtime/interpreter/mterp/x86/op_if_le.S new file mode 100644 index 0000000000..93571a7083 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_le.S @@ -0,0 +1 @@ +%include "x86/bincmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86/op_if_lez.S b/runtime/interpreter/mterp/x86/op_if_lez.S new file mode 100644 index 0000000000..779c77f2be --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_lez.S @@ -0,0 +1 @@ +%include "x86/zcmp.S" { "revcmp":"g" } diff --git a/runtime/interpreter/mterp/x86/op_if_lt.S b/runtime/interpreter/mterp/x86/op_if_lt.S new file mode 100644 index 0000000000..1fb1521054 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_lt.S @@ -0,0 +1 @@ +%include "x86/bincmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86/op_if_ltz.S b/runtime/interpreter/mterp/x86/op_if_ltz.S new file mode 100644 index 0000000000..155c356e4c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_ltz.S @@ -0,0 +1 @@ +%include "x86/zcmp.S" { "revcmp":"ge" } diff --git a/runtime/interpreter/mterp/x86/op_if_ne.S b/runtime/interpreter/mterp/x86/op_if_ne.S new file mode 100644 index 0000000000..7e1b065fc0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_ne.S @@ -0,0 +1 @@ +%include "x86/bincmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86/op_if_nez.S b/runtime/interpreter/mterp/x86/op_if_nez.S new file mode 100644 index 0000000000..8951f5b19f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_if_nez.S @@ -0,0 +1 @@ +%include "x86/zcmp.S" { "revcmp":"e" } diff --git a/runtime/interpreter/mterp/x86/op_iget.S b/runtime/interpreter/mterp/x86/op_iget.S new file mode 100644 index 0000000000..868ffd0a5c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget.S @@ -0,0 +1,29 @@ +%default { "is_object":"0", "helper":"artGet32InstanceFromCode"} +/* + * General instance field get. + * + * for: iget, iget-object, iget-boolean, iget-byte, iget-char, iget-short + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call $helper + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + .if $is_object + SET_VREG_OBJECT %eax rINST # fp[A] <-value + .else + SET_VREG %eax rINST # fp[A] <-value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iget_boolean.S b/runtime/interpreter/mterp/x86/op_iget_boolean.S new file mode 100644 index 0000000000..9ddad041dc --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_boolean.S @@ -0,0 +1 @@ +%include "x86/op_iget.S" { "helper":"artGetBooleanInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86/op_iget_boolean_quick.S b/runtime/interpreter/mterp/x86/op_iget_boolean_quick.S new file mode 100644 index 0000000000..02b0c16cf2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_boolean_quick.S @@ -0,0 +1 @@ +%include "x86/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86/op_iget_byte.S b/runtime/interpreter/mterp/x86/op_iget_byte.S new file mode 100644 index 0000000000..8250788923 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_byte.S @@ -0,0 +1 @@ +%include "x86/op_iget.S" { "helper":"artGetByteInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86/op_iget_byte_quick.S b/runtime/interpreter/mterp/x86/op_iget_byte_quick.S new file mode 100644 index 0000000000..02b0c16cf2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_byte_quick.S @@ -0,0 +1 @@ +%include "x86/op_iget_quick.S" { "load":"movsbl" } diff --git a/runtime/interpreter/mterp/x86/op_iget_char.S b/runtime/interpreter/mterp/x86/op_iget_char.S new file mode 100644 index 0000000000..e9d2156c88 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_char.S @@ -0,0 +1 @@ +%include "x86/op_iget.S" { "helper":"artGetCharInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86/op_iget_char_quick.S b/runtime/interpreter/mterp/x86/op_iget_char_quick.S new file mode 100644 index 0000000000..a5d971278d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_char_quick.S @@ -0,0 +1 @@ +%include "x86/op_iget_quick.S" { "load":"movzwl" } diff --git a/runtime/interpreter/mterp/x86/op_iget_object.S b/runtime/interpreter/mterp/x86/op_iget_object.S new file mode 100644 index 0000000000..3abeefcf81 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_object.S @@ -0,0 +1 @@ +%include "x86/op_iget.S" { "is_object":"1", "helper":"artGetObjInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86/op_iget_object_quick.S b/runtime/interpreter/mterp/x86/op_iget_object_quick.S new file mode 100644 index 0000000000..b09772f720 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_object_quick.S @@ -0,0 +1,17 @@ + /* For: iget-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + movl %ecx, OUT_ARG0(%esp) + movl %eax, OUT_ARG1(%esp) + EXPORT_PC + call artIGetObjectFromMterp # (obj, offset) + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $$0xf,rINSTbl # rINST <- A + SET_VREG_OBJECT %eax rINST # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iget_quick.S b/runtime/interpreter/mterp/x86/op_iget_quick.S new file mode 100644 index 0000000000..372071ce9c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_quick.S @@ -0,0 +1,13 @@ +%default { "load":"movl"} + /* For: iget-quick, iget-boolean-quick, iget-byte-quick, iget-char-quick, iget-short-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + ${load} (%ecx,%eax,1), %eax + andb $$0xf,rINSTbl # rINST <- A + SET_VREG %eax rINST # fp[A] <- value + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iget_short.S b/runtime/interpreter/mterp/x86/op_iget_short.S new file mode 100644 index 0000000000..c8fad89040 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_short.S @@ -0,0 +1 @@ +%include "x86/op_iget.S" { "helper":"artGetShortInstanceFromCode" } diff --git a/runtime/interpreter/mterp/x86/op_iget_short_quick.S b/runtime/interpreter/mterp/x86/op_iget_short_quick.S new file mode 100644 index 0000000000..2c3aeb67eb --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_short_quick.S @@ -0,0 +1 @@ +%include "x86/op_iget_quick.S" { "load":"movswl" } diff --git a/runtime/interpreter/mterp/x86/op_iget_wide.S b/runtime/interpreter/mterp/x86/op_iget_wide.S new file mode 100644 index 0000000000..58e5a65a89 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_wide.S @@ -0,0 +1,25 @@ +/* + * 64-bit instance field get. + * + * for: iget-wide + */ + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + mov rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artGet64InstanceFromCode + mov rSELF, %ecx + cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException # bail out + andb $$0xf, rINSTbl # rINST <- A + SET_VREG %eax rINST + SET_VREG_HIGH %edx rINST + REFRESH_IBASE_FROM_SELF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iget_wide_quick.S b/runtime/interpreter/mterp/x86/op_iget_wide_quick.S new file mode 100644 index 0000000000..8be336be73 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iget_wide_quick.S @@ -0,0 +1,11 @@ + /* iget-wide-quick vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + movzwl 2(rPC), %eax # eax <- field byte offset + testl %ecx, %ecx # is object null? + je common_errNullObject + movq (%ecx,%eax,1), %xmm0 + andb $$0xf, rINSTbl # rINST <- A + SET_WIDE_FP_VREG %xmm0 rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_instance_of.S b/runtime/interpreter/mterp/x86/op_instance_of.S new file mode 100644 index 0000000000..c9bfba5c36 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_instance_of.S @@ -0,0 +1,26 @@ +/* + * 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), %eax # eax <- BBBB + movl %eax, OUT_ARG0(%esp) + movl rINST, %eax # eax <- BA + sarl $$4, %eax # eax <- B + leal VREG_ADDRESS(%eax), %ecx # Get object address + movl %ecx, OUT_ARG1(%esp) + movl OFF_FP_METHOD(rFP),%eax + movl %eax, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpInstanceOf # (index, &obj, method, self) + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + andb $$0xf, rINSTbl # rINSTbl <- A + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_int_to_byte.S b/runtime/interpreter/mterp/x86/op_int_to_byte.S new file mode 100644 index 0000000000..b4e8d22c98 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_int_to_byte.S @@ -0,0 +1 @@ +%include "x86/unop.S" {"instr":"movsbl %al, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_int_to_char.S b/runtime/interpreter/mterp/x86/op_int_to_char.S new file mode 100644 index 0000000000..4608971469 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_int_to_char.S @@ -0,0 +1 @@ +%include "x86/unop.S" {"instr":"movzwl %ax,%eax"} diff --git a/runtime/interpreter/mterp/x86/op_int_to_double.S b/runtime/interpreter/mterp/x86/op_int_to_double.S new file mode 100644 index 0000000000..3e9921eb8f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_int_to_double.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"load":"fildl","store":"fstpl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86/op_int_to_float.S b/runtime/interpreter/mterp/x86/op_int_to_float.S new file mode 100644 index 0000000000..849540da09 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_int_to_float.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"load":"fildl","store":"fstps"} diff --git a/runtime/interpreter/mterp/x86/op_int_to_long.S b/runtime/interpreter/mterp/x86/op_int_to_long.S new file mode 100644 index 0000000000..736ea69869 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_int_to_long.S @@ -0,0 +1,12 @@ + /* int to long vA, vB */ + movzbl rINSTbl, %eax # eax <- +A + sarl $$4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + andb $$0xf, rINSTbl # rINST <- A + movl rIBASE, %ecx # cltd trashes rIBASE/edx + cltd # rINST:eax<- sssssssBBBBBBBB + SET_VREG_HIGH rIBASE rINST # v[A+1] <- rIBASE + SET_VREG %eax rINST # v[A+0] <- %eax + movl %ecx, rIBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + diff --git a/runtime/interpreter/mterp/x86/op_int_to_short.S b/runtime/interpreter/mterp/x86/op_int_to_short.S new file mode 100644 index 0000000000..90d0ae65b1 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_int_to_short.S @@ -0,0 +1 @@ +%include "x86/unop.S" {"instr":"movswl %ax, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_invoke_direct.S b/runtime/interpreter/mterp/x86/op_invoke_direct.S new file mode 100644 index 0000000000..76fb9a6786 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_direct.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeDirect" } diff --git a/runtime/interpreter/mterp/x86/op_invoke_direct_range.S b/runtime/interpreter/mterp/x86/op_invoke_direct_range.S new file mode 100644 index 0000000000..a6ab6049f5 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_direct_range.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeDirectRange" } diff --git a/runtime/interpreter/mterp/x86/op_invoke_interface.S b/runtime/interpreter/mterp/x86/op_invoke_interface.S new file mode 100644 index 0000000000..91c24f5db6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_interface.S @@ -0,0 +1,8 @@ +%include "x86/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/op_invoke_interface_range.S b/runtime/interpreter/mterp/x86/op_invoke_interface_range.S new file mode 100644 index 0000000000..e478beb596 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_interface_range.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeInterfaceRange" } diff --git a/runtime/interpreter/mterp/x86/op_invoke_static.S b/runtime/interpreter/mterp/x86/op_invoke_static.S new file mode 100644 index 0000000000..b4c1236f7a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_static.S @@ -0,0 +1,2 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeStatic" } + diff --git a/runtime/interpreter/mterp/x86/op_invoke_static_range.S b/runtime/interpreter/mterp/x86/op_invoke_static_range.S new file mode 100644 index 0000000000..3dc8a26856 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_static_range.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeStaticRange" } diff --git a/runtime/interpreter/mterp/x86/op_invoke_super.S b/runtime/interpreter/mterp/x86/op_invoke_super.S new file mode 100644 index 0000000000..be20edd07c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_super.S @@ -0,0 +1,8 @@ +%include "x86/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/op_invoke_super_range.S b/runtime/interpreter/mterp/x86/op_invoke_super_range.S new file mode 100644 index 0000000000..f36bf72bcf --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_super_range.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeSuperRange" } diff --git a/runtime/interpreter/mterp/x86/op_invoke_virtual.S b/runtime/interpreter/mterp/x86/op_invoke_virtual.S new file mode 100644 index 0000000000..7e9c456a95 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_virtual.S @@ -0,0 +1,8 @@ +%include "x86/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/op_invoke_virtual_quick.S b/runtime/interpreter/mterp/x86/op_invoke_virtual_quick.S new file mode 100644 index 0000000000..2dc9ab6298 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_virtual_quick.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeVirtualQuick" } diff --git a/runtime/interpreter/mterp/x86/op_invoke_virtual_range.S b/runtime/interpreter/mterp/x86/op_invoke_virtual_range.S new file mode 100644 index 0000000000..d1d20d29ac --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_virtual_range.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeVirtualRange" } diff --git a/runtime/interpreter/mterp/x86/op_invoke_virtual_range_quick.S b/runtime/interpreter/mterp/x86/op_invoke_virtual_range_quick.S new file mode 100644 index 0000000000..21bfc55b73 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_invoke_virtual_range_quick.S @@ -0,0 +1 @@ +%include "x86/invoke.S" { "helper":"MterpInvokeVirtualQuickRange" } diff --git a/runtime/interpreter/mterp/x86/op_iput.S b/runtime/interpreter/mterp/x86/op_iput.S new file mode 100644 index 0000000000..f8a65499d7 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput.S @@ -0,0 +1,25 @@ +%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), %eax # eax<- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl, %ecx # ecx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $$0xf, rINSTbl # rINST<- A + GET_VREG %eax, rINST + movl %eax, OUT_ARG2(%esp) # fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call $handler + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_boolean.S b/runtime/interpreter/mterp/x86/op_iput_boolean.S new file mode 100644 index 0000000000..11cab8880f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_boolean.S @@ -0,0 +1 @@ +%include "x86/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86/op_iput_boolean_quick.S b/runtime/interpreter/mterp/x86/op_iput_boolean_quick.S new file mode 100644 index 0000000000..93865de169 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_boolean_quick.S @@ -0,0 +1 @@ +%include "x86/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86/op_iput_byte.S b/runtime/interpreter/mterp/x86/op_iput_byte.S new file mode 100644 index 0000000000..11cab8880f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_byte.S @@ -0,0 +1 @@ +%include "x86/op_iput.S" { "handler":"artSet8InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86/op_iput_byte_quick.S b/runtime/interpreter/mterp/x86/op_iput_byte_quick.S new file mode 100644 index 0000000000..93865de169 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_byte_quick.S @@ -0,0 +1 @@ +%include "x86/op_iput_quick.S" { "reg":"rINSTbl", "store":"movb" } diff --git a/runtime/interpreter/mterp/x86/op_iput_char.S b/runtime/interpreter/mterp/x86/op_iput_char.S new file mode 100644 index 0000000000..abbf2bdc6e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_char.S @@ -0,0 +1 @@ +%include "x86/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86/op_iput_char_quick.S b/runtime/interpreter/mterp/x86/op_iput_char_quick.S new file mode 100644 index 0000000000..4ec80290c6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_char_quick.S @@ -0,0 +1 @@ +%include "x86/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86/op_iput_object.S b/runtime/interpreter/mterp/x86/op_iput_object.S new file mode 100644 index 0000000000..20d57aa35e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_object.S @@ -0,0 +1,13 @@ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST ${opnum} + movl rINST, OUT_ARG2(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG3(%esp) + call MterpIputObject + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_object_quick.S b/runtime/interpreter/mterp/x86/op_iput_object_quick.S new file mode 100644 index 0000000000..4c7f4bd0cf --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_object_quick.S @@ -0,0 +1,11 @@ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST ${opnum} + movl rINST, OUT_ARG2(%esp) + call MterpIputObjectQuick + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_quick.S b/runtime/interpreter/mterp/x86/op_iput_quick.S new file mode 100644 index 0000000000..e2f7caf936 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_quick.S @@ -0,0 +1,13 @@ +%default { "reg":"rINST", "store":"movl" } + /* For: iput-quick, iput-object-quick */ + /* op vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + andb $$0xf, rINSTbl # rINST <- A + GET_VREG rINST rINST # rINST <- v[A] + movzwl 2(rPC), %eax # eax <- field byte offset + ${store} ${reg}, (%ecx,%eax,1) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_short.S b/runtime/interpreter/mterp/x86/op_iput_short.S new file mode 100644 index 0000000000..abbf2bdc6e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_short.S @@ -0,0 +1 @@ +%include "x86/op_iput.S" { "handler":"artSet16InstanceFromMterp" } diff --git a/runtime/interpreter/mterp/x86/op_iput_short_quick.S b/runtime/interpreter/mterp/x86/op_iput_short_quick.S new file mode 100644 index 0000000000..4ec80290c6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_short_quick.S @@ -0,0 +1 @@ +%include "x86/op_iput_quick.S" { "reg":"rINSTw", "store":"movw" } diff --git a/runtime/interpreter/mterp/x86/op_iput_wide.S b/runtime/interpreter/mterp/x86/op_iput_wide.S new file mode 100644 index 0000000000..92cb770201 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_wide.S @@ -0,0 +1,19 @@ + /* iput-wide vA, vB, field@CCCC */ + .extern artSet64InstanceFromMterp + EXPORT_PC + movzwl 2(rPC), %eax # eax <- 0000CCCC + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movzbl rINSTbl,%ecx # ecx <- BA + sarl $$4,%ecx # ecx <- B + GET_VREG %ecx, %ecx + movl %ecx, OUT_ARG1(%esp) # the object pointer + andb $$0xf,rINSTbl # rINST <- A + leal VREG_ADDRESS(rINST), %eax + movl %eax, OUT_ARG2(%esp) # &fp[A] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG3(%esp) # referrer + call artSet64InstanceFromMterp + testl %eax, %eax + jnz MterpPossibleException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_iput_wide_quick.S b/runtime/interpreter/mterp/x86/op_iput_wide_quick.S new file mode 100644 index 0000000000..72285c5a5b --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_iput_wide_quick.S @@ -0,0 +1,12 @@ + /* iput-wide-quick vA, vB, offset@CCCC */ + movzbl rINSTbl, %ecx # ecx<- BA + sarl $$4, %ecx # ecx<- B + GET_VREG %ecx %ecx # vB (object we're operating on) + testl %ecx, %ecx # is object null? + je common_errNullObject + movzwl 2(rPC), %eax # eax<- field byte offset + leal (%ecx,%eax,1), %ecx # ecx<- Address of 64-bit target + andb $$0xf, rINSTbl # rINST<- A + GET_WIDE_FP_VREG %xmm0 rINST # xmm0<- fp[A]/fp[A+1] + movq %xmm0, (%ecx) # obj.field<- r0/r1 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_long_to_double.S b/runtime/interpreter/mterp/x86/op_long_to_double.S new file mode 100644 index 0000000000..2c7f905259 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_long_to_double.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"load":"fildll","store":"fstpl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86/op_long_to_float.S b/runtime/interpreter/mterp/x86/op_long_to_float.S new file mode 100644 index 0000000000..e500e39d46 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_long_to_float.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"load":"fildll","store":"fstps"} diff --git a/runtime/interpreter/mterp/x86/op_long_to_int.S b/runtime/interpreter/mterp/x86/op_long_to_int.S new file mode 100644 index 0000000000..1c39b96d5c --- /dev/null +++ b/runtime/interpreter/mterp/x86/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/op_move.S" diff --git a/runtime/interpreter/mterp/x86/op_monitor_enter.S b/runtime/interpreter/mterp/x86/op_monitor_enter.S new file mode 100644 index 0000000000..8236fb3420 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_monitor_enter.S @@ -0,0 +1,14 @@ +/* + * Synchronize on an object. + */ + /* monitor-enter vAA */ + EXPORT_PC + GET_VREG %ecx rINST + movl %ecx, OUT_ARG0(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG1(%esp) + call artLockObjectFromCode # (object, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_monitor_exit.S b/runtime/interpreter/mterp/x86/op_monitor_exit.S new file mode 100644 index 0000000000..56d4eb318e --- /dev/null +++ b/runtime/interpreter/mterp/x86/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 */ + EXPORT_PC + GET_VREG %ecx rINST + movl %ecx, OUT_ARG0(%esp) + movl rSELF, %eax + movl %eax, OUT_ARG1(%esp) + call artUnlockObjectFromCode # (object, self) + REFRESH_IBASE + testl %eax, %eax + jnz MterpException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_move.S b/runtime/interpreter/mterp/x86/op_move.S new file mode 100644 index 0000000000..0a531be901 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move.S @@ -0,0 +1,13 @@ +%default { "is_object":"0" } + /* for move, move-object, long-to-int */ + /* op vA, vB */ + movzbl rINSTbl, %eax # eax <- BA + andb $$0xf, %al # eax <- A + shrl $$4, rINST # rINST <- B + GET_VREG rINST rINST + .if $is_object + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_move_16.S b/runtime/interpreter/mterp/x86/op_move_16.S new file mode 100644 index 0000000000..0773f41109 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_16.S @@ -0,0 +1,12 @@ +%default { "is_object":"0" } + /* for: move/16, move-object/16 */ + /* op vAAAA, vBBBB */ + movzwl 4(rPC), %ecx # ecx <- BBBB + movzwl 2(rPC), %eax # eax <- AAAA + GET_VREG rINST %ecx + .if $is_object + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_move_exception.S b/runtime/interpreter/mterp/x86/op_move_exception.S new file mode 100644 index 0000000000..e37cdfa877 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_exception.S @@ -0,0 +1,6 @@ + /* move-exception vAA */ + movl rSELF, %ecx + movl THREAD_EXCEPTION_OFFSET(%ecx), %eax + SET_VREG_OBJECT %eax rINST # fp[AA] <- exception object + movl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_move_from16.S b/runtime/interpreter/mterp/x86/op_move_from16.S new file mode 100644 index 0000000000..623a4d354f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_from16.S @@ -0,0 +1,12 @@ +%default { "is_object":"0" } + /* for: move/from16, move-object/from16 */ + /* op vAA, vBBBB */ + movzx rINSTbl, %eax # eax <- AA + movw 2(rPC), rINSTw # rINSTw <- BBBB + GET_VREG rINST rINST # rINST <- fp[BBBB] + .if $is_object + SET_VREG_OBJECT rINST %eax # fp[A] <- fp[B] + .else + SET_VREG rINST %eax # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_move_object.S b/runtime/interpreter/mterp/x86/op_move_object.S new file mode 100644 index 0000000000..a6a7c90195 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_object.S @@ -0,0 +1 @@ +%include "x86/op_move.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86/op_move_object_16.S b/runtime/interpreter/mterp/x86/op_move_object_16.S new file mode 100644 index 0000000000..e0c8527a29 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_object_16.S @@ -0,0 +1 @@ +%include "x86/op_move_16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86/op_move_object_from16.S b/runtime/interpreter/mterp/x86/op_move_object_from16.S new file mode 100644 index 0000000000..e623820470 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_object_from16.S @@ -0,0 +1 @@ +%include "x86/op_move_from16.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86/op_move_result.S b/runtime/interpreter/mterp/x86/op_move_result.S new file mode 100644 index 0000000000..414f2cb715 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_result.S @@ -0,0 +1,11 @@ +%default { "is_object":"0" } + /* for: move-result, move-result-object */ + /* op vAA */ + movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType. + movl (%eax), %eax # r0 <- result.i. + .if $is_object + SET_VREG_OBJECT %eax rINST # fp[A] <- fp[B] + .else + SET_VREG %eax rINST # fp[A] <- fp[B] + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_move_result_object.S b/runtime/interpreter/mterp/x86/op_move_result_object.S new file mode 100644 index 0000000000..cbf5e1db60 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_result_object.S @@ -0,0 +1 @@ +%include "x86/op_move_result.S" {"is_object":"1"} diff --git a/runtime/interpreter/mterp/x86/op_move_result_wide.S b/runtime/interpreter/mterp/x86/op_move_result_wide.S new file mode 100644 index 0000000000..0c1683b45a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_result_wide.S @@ -0,0 +1,7 @@ + /* move-result-wide vAA */ + movl OFF_FP_RESULT_REGISTER(rFP), %eax # get pointer to result JType. + movl 4(%eax), %ecx # Get high + movl (%eax), %eax # Get low + SET_VREG %eax rINST # v[AA+0] <- eax + SET_VREG_HIGH %ecx rINST # v[AA+1] <- ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_move_wide.S b/runtime/interpreter/mterp/x86/op_move_wide.S new file mode 100644 index 0000000000..9c0e9853ab --- /dev/null +++ b/runtime/interpreter/mterp/x86/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" */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, rINST # rINST <- B + andb $$0xf, %cl # ecx <- A + GET_WIDE_FP_VREG %xmm0 rINST # xmm0 <- v[B] + SET_WIDE_FP_VREG %xmm0 %ecx # v[A] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_move_wide_16.S b/runtime/interpreter/mterp/x86/op_move_wide_16.S new file mode 100644 index 0000000000..7522c277a2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/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" */ + movzwl 4(rPC), %ecx # ecx<- BBBB + movzwl 2(rPC), %eax # eax<- AAAA + GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B] + SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 3 diff --git a/runtime/interpreter/mterp/x86/op_move_wide_from16.S b/runtime/interpreter/mterp/x86/op_move_wide_from16.S new file mode 100644 index 0000000000..5ad2cb44b5 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_move_wide_from16.S @@ -0,0 +1,7 @@ + /* move-wide/from16 vAA, vBBBB */ + /* NOTE: regs can overlap, e.g. "move v6,v7" or "move v7,v6" */ + movzwl 2(rPC), %ecx # ecx <- BBBB + movzbl rINSTbl, %eax # eax <- AAAA + GET_WIDE_FP_VREG %xmm0 %ecx # xmm0 <- v[B] + SET_WIDE_FP_VREG %xmm0 %eax # v[A] <- xmm0 + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_mul_double.S b/runtime/interpreter/mterp/x86/op_mul_double.S new file mode 100644 index 0000000000..7cef4c0870 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_double.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_mul_double_2addr.S b/runtime/interpreter/mterp/x86/op_mul_double_2addr.S new file mode 100644 index 0000000000..bb722b6901 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_double_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"muls","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_mul_float.S b/runtime/interpreter/mterp/x86/op_mul_float.S new file mode 100644 index 0000000000..115623047a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_float.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_mul_float_2addr.S b/runtime/interpreter/mterp/x86/op_mul_float_2addr.S new file mode 100644 index 0000000000..e9316dff69 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_float_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"muls","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_mul_int.S b/runtime/interpreter/mterp/x86/op_mul_int.S new file mode 100644 index 0000000000..a367ab7102 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_int.S @@ -0,0 +1,12 @@ + /* + * 32-bit binary multiplication. + */ + /* mul vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + GET_VREG %eax %eax # eax <- vBB + mov rIBASE, LOCAL0(%esp) + imull (rFP,%ecx,4), %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S new file mode 100644 index 0000000000..60050759d0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S @@ -0,0 +1,10 @@ + /* mul vA, vB */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $$4, rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $$0xf, %cl # ecx <- A + mov rIBASE, LOCAL0(%esp) + imull (rFP,%ecx,4), %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S new file mode 100644 index 0000000000..1c0fde3cd1 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S @@ -0,0 +1,12 @@ + /* mul/lit16 vA, vB, #+CCCC */ + /* Need A in rINST, ssssCCCC in ecx, vB in eax */ + movzbl rINSTbl, %eax # eax <- 000000BA + sarl $$4, %eax # eax <- B + GET_VREG %eax %eax # eax <- vB + movswl 2(rPC), %ecx # ecx <- ssssCCCC + andb $$0xf, rINSTbl # rINST <- A + mov rIBASE, LOCAL0(%esp) + imull %ecx, %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S new file mode 100644 index 0000000000..4d7a22d898 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S @@ -0,0 +1,9 @@ + /* mul/lit8 vAA, vBB, #+CC */ + movzbl 2(rPC), %eax # eax <- BB + movsbl 3(rPC), %ecx # ecx <- ssssssCC + GET_VREG %eax %eax # eax <- rBB + mov rIBASE, LOCAL0(%esp) + imull %ecx, %eax # trashes rIBASE/edx + mov LOCAL0(%esp), rIBASE + SET_VREG %eax rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_mul_long.S b/runtime/interpreter/mterp/x86/op_mul_long.S new file mode 100644 index 0000000000..3746e41636 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_long.S @@ -0,0 +1,33 @@ +/* + * Signed 64-bit integer multiply. + * + * We could definately use more free registers for + * this code. We spill rINSTw (ebx), + * giving us eax, ebc, ecx and edx as computational + * temps. On top of that, we'll spill edi (rFP) + * for use as the vB pointer and esi (rPC) for use + * as the vC pointer. Yuck. + * + */ + /* mul-long vAA, vBB, vCC */ + movzbl 2(rPC), %eax # eax <- B + movzbl 3(rPC), %ecx # ecx <- C + mov rPC, LOCAL0(%esp) # save Interpreter PC + mov rFP, LOCAL1(%esp) # save FP + mov rIBASE, LOCAL2(%esp) # save rIBASE + leal (rFP,%eax,4), %esi # esi <- &v[B] + leal (rFP,%ecx,4), rFP # rFP <- &v[C] + movl 4(%esi), %ecx # ecx <- Bmsw + imull (rFP), %ecx # ecx <- (Bmsw*Clsw) + movl 4(rFP), %eax # eax <- Cmsw + imull (%esi), %eax # eax <- (Cmsw*Blsw) + addl %eax, %ecx # ecx <- (Bmsw*Clsw)+(Cmsw*Blsw) + movl (rFP), %eax # eax <- Clsw + mull (%esi) # eax <- (Clsw*Alsw) + mov LOCAL0(%esp), rPC # restore Interpreter PC + mov LOCAL1(%esp), rFP # restore FP + leal (%ecx,rIBASE), rIBASE # full result now in rIBASE:%eax + SET_VREG_HIGH rIBASE rINST # v[B+1] <- rIBASE + mov LOCAL2(%esp), rIBASE # restore IBASE + SET_VREG %eax rINST # v[B] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_mul_long_2addr.S b/runtime/interpreter/mterp/x86/op_mul_long_2addr.S new file mode 100644 index 0000000000..565a57cd39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_mul_long_2addr.S @@ -0,0 +1,35 @@ +/* + * Signed 64-bit integer multiply, 2-addr version + * + * We could definately use more free registers for + * this code. We must spill %edx (rIBASE) because it + * is used by imul. We'll also spill rINST (ebx), + * giving us eax, ebc, ecx and rIBASE as computational + * temps. On top of that, we'll spill %esi (edi) + * for use as the vA pointer and rFP (esi) for use + * as the vB pointer. Yuck. + */ + /* mul-long/2addr vA, vB */ + movzbl rINSTbl, %eax # eax <- BA + andb $$0xf, %al # eax <- A + CLEAR_WIDE_REF %eax # clear refs in advance + sarl $$4, rINST # rINST <- B + mov rPC, LOCAL0(%esp) # save Interpreter PC + mov rFP, LOCAL1(%esp) # save FP + mov rIBASE, LOCAL2(%esp) # save rIBASE + leal (rFP,%eax,4), %esi # esi <- &v[A] + leal (rFP,rINST,4), rFP # rFP <- &v[B] + movl 4(%esi), %ecx # ecx <- Amsw + imull (rFP), %ecx # ecx <- (Amsw*Blsw) + movl 4(rFP), %eax # eax <- Bmsw + imull (%esi), %eax # eax <- (Bmsw*Alsw) + addl %eax, %ecx # ecx <- (Amsw*Blsw)+(Bmsw*Alsw) + movl (rFP), %eax # eax <- Blsw + mull (%esi) # eax <- (Blsw*Alsw) + leal (%ecx,rIBASE), rIBASE # full result now in %edx:%eax + movl rIBASE, 4(%esi) # v[A+1] <- rIBASE + movl %eax, (%esi) # v[A] <- %eax + mov LOCAL0(%esp), rPC # restore Interpreter PC + mov LOCAL2(%esp), rIBASE # restore IBASE + mov LOCAL1(%esp), rFP # restore FP + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_neg_double.S b/runtime/interpreter/mterp/x86/op_neg_double.S new file mode 100644 index 0000000000..fac4322f8e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_neg_double.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"instr":"fchs","load":"fldl","store":"fstpl","wide":"1"} diff --git a/runtime/interpreter/mterp/x86/op_neg_float.S b/runtime/interpreter/mterp/x86/op_neg_float.S new file mode 100644 index 0000000000..30f071b991 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_neg_float.S @@ -0,0 +1 @@ +%include "x86/fpcvt.S" {"instr":"fchs","load":"flds","store":"fstps"} diff --git a/runtime/interpreter/mterp/x86/op_neg_int.S b/runtime/interpreter/mterp/x86/op_neg_int.S new file mode 100644 index 0000000000..67d4d182aa --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_neg_int.S @@ -0,0 +1 @@ +%include "x86/unop.S" {"instr":"negl %eax"} diff --git a/runtime/interpreter/mterp/x86/op_neg_long.S b/runtime/interpreter/mterp/x86/op_neg_long.S new file mode 100644 index 0000000000..7cc17f019f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_neg_long.S @@ -0,0 +1,13 @@ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + andb $$0xf, rINSTbl # rINST <- A + GET_VREG %eax %ecx # eax <- v[B+0] + GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1] + negl %eax + adcl $$0, %ecx + negl %ecx + SET_VREG %eax rINST # v[A+0] <- eax + SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 + diff --git a/runtime/interpreter/mterp/x86/op_new_array.S b/runtime/interpreter/mterp/x86/op_new_array.S new file mode 100644 index 0000000000..68521830b3 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_new_array.S @@ -0,0 +1,21 @@ +/* + * 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 + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST ${opnum} + movl rINST, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpNewArray + REFRESH_IBASE + testl %eax, %eax # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_new_instance.S b/runtime/interpreter/mterp/x86/op_new_instance.S new file mode 100644 index 0000000000..a3632e8c1f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_new_instance.S @@ -0,0 +1,16 @@ +/* + * Create a new instance of a class. + */ + /* new-instance vAA, class@BBBB */ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG1(%esp) + REFRESH_INST ${opnum} + movl rINST, OUT_ARG2(%esp) + call MterpNewInstance + REFRESH_IBASE + testl %eax, %eax # 0 means an exception is thrown + jz MterpPossibleException + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_nop.S b/runtime/interpreter/mterp/x86/op_nop.S new file mode 100644 index 0000000000..4cb68e392e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_nop.S @@ -0,0 +1 @@ + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_not_int.S b/runtime/interpreter/mterp/x86/op_not_int.S new file mode 100644 index 0000000000..335ab09a5a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_not_int.S @@ -0,0 +1 @@ +%include "x86/unop.S" {"instr":"notl %eax"} diff --git a/runtime/interpreter/mterp/x86/op_not_long.S b/runtime/interpreter/mterp/x86/op_not_long.S new file mode 100644 index 0000000000..55666a13ba --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_not_long.S @@ -0,0 +1,11 @@ + /* unop vA, vB */ + movzbl rINSTbl, %ecx # ecx <- BA + sarl $$4, %ecx # ecx <- B + andb $$0xf, rINSTbl # rINST <- A + GET_VREG %eax %ecx # eax <- v[B+0] + GET_VREG_HIGH %ecx %ecx # ecx <- v[B+1] + notl %eax + notl %ecx + SET_VREG %eax rINST # v[A+0] <- eax + SET_VREG_HIGH %ecx rINST # v[A+1] <- ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_or_int.S b/runtime/interpreter/mterp/x86/op_or_int.S new file mode 100644 index 0000000000..ebe2ec2cd6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_or_int.S @@ -0,0 +1 @@ +%include "x86/binop.S" {"instr":"orl (rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_or_int_2addr.S b/runtime/interpreter/mterp/x86/op_or_int_2addr.S new file mode 100644 index 0000000000..36c17db5a7 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_or_int_2addr.S @@ -0,0 +1 @@ +%include "x86/binop2addr.S" {"instr":"orl %eax, (rFP,%ecx,4)"} diff --git a/runtime/interpreter/mterp/x86/op_or_int_lit16.S b/runtime/interpreter/mterp/x86/op_or_int_lit16.S new file mode 100644 index 0000000000..0a88ff5902 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_or_int_lit16.S @@ -0,0 +1 @@ +%include "x86/binopLit16.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_or_int_lit8.S b/runtime/interpreter/mterp/x86/op_or_int_lit8.S new file mode 100644 index 0000000000..0670b6785e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_or_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"orl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_or_long.S b/runtime/interpreter/mterp/x86/op_or_long.S new file mode 100644 index 0000000000..09ca539f2a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_or_long.S @@ -0,0 +1 @@ +%include "x86/binopWide.S" {"instr1":"orl (rFP,%ecx,4), rIBASE", "instr2":"orl 4(rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_or_long_2addr.S b/runtime/interpreter/mterp/x86/op_or_long_2addr.S new file mode 100644 index 0000000000..2062e81e35 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_or_long_2addr.S @@ -0,0 +1 @@ +%include "x86/binopWide2addr.S" {"instr1":"orl %eax, (rFP,rINST,4)","instr2":"orl %ecx, 4(rFP,rINST,4)"} diff --git a/runtime/interpreter/mterp/x86/op_packed_switch.S b/runtime/interpreter/mterp/x86/op_packed_switch.S new file mode 100644 index 0000000000..4e39a48444 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_packed_switch.S @@ -0,0 +1,29 @@ +%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 */ + movl 2(rPC), %ecx # ecx <- BBBBbbbb + GET_VREG %eax rINST # eax <- vAA + leal (rPC,%ecx,2), %ecx # ecx <- PC + BBBBbbbb*2 + movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA + movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData + call $func + addl %eax, %eax + leal (rPC, %eax), rPC + FETCH_INST + REFRESH_IBASE + jg 1f +#if MTERP_SUSPEND + # REFRESH_IBASE - we did it above. +#else + jmp MterpCheckSuspendAndContinue +#endif +1: + GOTO_NEXT diff --git a/runtime/interpreter/mterp/x86/op_rem_double.S b/runtime/interpreter/mterp/x86/op_rem_double.S new file mode 100644 index 0000000000..4b52a06042 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_double.S @@ -0,0 +1,14 @@ + /* rem_double vAA, vBB, vCC */ + movzbl 3(rPC), %ecx # ecx <- BB + movzbl 2(rPC), %eax # eax <- CC + fldl VREG_ADDRESS(%ecx) # %st1 <- fp[vBB] + fldl VREG_ADDRESS(%eax) # %st0 <- fp[vCC] +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(rINST) # fp[vAA] <- %st + CLEAR_WIDE_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_rem_double_2addr.S b/runtime/interpreter/mterp/x86/op_rem_double_2addr.S new file mode 100644 index 0000000000..5a0e669787 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_double_2addr.S @@ -0,0 +1,15 @@ + /* rem_double/2addr vA, vB */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $$4, rINST # rINST <- B + fldl VREG_ADDRESS(rINST) # vB to fp stack + andb $$0xf, %cl # ecx <- A + fldl VREG_ADDRESS(%ecx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstpl VREG_ADDRESS(%ecx) # %st to vA + CLEAR_WIDE_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_rem_float.S b/runtime/interpreter/mterp/x86/op_rem_float.S new file mode 100644 index 0000000000..05e0bf1132 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_float.S @@ -0,0 +1,14 @@ + /* rem_float vAA, vBB, vCC */ + movzbl 3(rPC), %ecx # ecx <- BB + movzbl 2(rPC), %eax # eax <- CC + flds VREG_ADDRESS(%ecx) # vBB to fp stack + flds VREG_ADDRESS(%eax) # vCC to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(rINST) # %st to vAA + CLEAR_REF rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_rem_float_2addr.S b/runtime/interpreter/mterp/x86/op_rem_float_2addr.S new file mode 100644 index 0000000000..29f84e635a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_float_2addr.S @@ -0,0 +1,15 @@ + /* rem_float/2addr vA, vB */ + movzx rINSTbl, %ecx # ecx <- A+ + sarl $$4, rINST # rINST <- B + flds VREG_ADDRESS(rINST) # vB to fp stack + andb $$0xf, %cl # ecx <- A + flds VREG_ADDRESS(%ecx) # vA to fp stack +1: + fprem + fstsw %ax + sahf + jp 1b + fstp %st(1) + fstps VREG_ADDRESS(%ecx) # %st to vA + CLEAR_REF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_rem_int.S b/runtime/interpreter/mterp/x86/op_rem_int.S new file mode 100644 index 0000000000..d25b93ce3f --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_int.S @@ -0,0 +1 @@ +%include "x86/bindiv.S" {"result":"rIBASE","special":"$0","rem":"1"} diff --git a/runtime/interpreter/mterp/x86/op_rem_int_2addr.S b/runtime/interpreter/mterp/x86/op_rem_int_2addr.S new file mode 100644 index 0000000000..c788e0eed0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_int_2addr.S @@ -0,0 +1 @@ +%include "x86/bindiv2addr.S" {"result":"rIBASE","special":"$0"} diff --git a/runtime/interpreter/mterp/x86/op_rem_int_lit16.S b/runtime/interpreter/mterp/x86/op_rem_int_lit16.S new file mode 100644 index 0000000000..3df9d3911d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_int_lit16.S @@ -0,0 +1 @@ +%include "x86/bindivLit16.S" {"result":"rIBASE","special":"$0"} diff --git a/runtime/interpreter/mterp/x86/op_rem_int_lit8.S b/runtime/interpreter/mterp/x86/op_rem_int_lit8.S new file mode 100644 index 0000000000..56e19c6b17 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_int_lit8.S @@ -0,0 +1 @@ +%include "x86/bindivLit8.S" {"result":"rIBASE","special":"$0"} diff --git a/runtime/interpreter/mterp/x86/op_rem_long.S b/runtime/interpreter/mterp/x86/op_rem_long.S new file mode 100644 index 0000000000..0ffe1f668c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_long.S @@ -0,0 +1 @@ +%include "x86/op_div_long.S" {"routine":"art_quick_lmod"} diff --git a/runtime/interpreter/mterp/x86/op_rem_long_2addr.S b/runtime/interpreter/mterp/x86/op_rem_long_2addr.S new file mode 100644 index 0000000000..4b977352a1 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rem_long_2addr.S @@ -0,0 +1 @@ +%include "x86/op_div_long_2addr.S" {"routine":"art_quick_lmod"} diff --git a/runtime/interpreter/mterp/x86/op_return.S b/runtime/interpreter/mterp/x86/op_return.S new file mode 100644 index 0000000000..183b3bfdd5 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_return.S @@ -0,0 +1,17 @@ +/* + * Return a 32-bit value. + * + * for: return, return-object + */ + /* op vAA */ + .extern MterpThreadFenceForConstructor + call MterpThreadFenceForConstructor + movl rSELF, %eax + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + GET_VREG %eax rINST # eax <- vAA + xorl %ecx, %ecx + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86/op_return_object.S b/runtime/interpreter/mterp/x86/op_return_object.S new file mode 100644 index 0000000000..12c84b32a2 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_return_object.S @@ -0,0 +1 @@ +%include "x86/op_return.S" diff --git a/runtime/interpreter/mterp/x86/op_return_void.S b/runtime/interpreter/mterp/x86/op_return_void.S new file mode 100644 index 0000000000..f3e24c7990 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_return_void.S @@ -0,0 +1,11 @@ + .extern MterpThreadFenceForConstructor + call MterpThreadFenceForConstructor + movl rSELF, %eax + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + xorl %eax, %eax + xorl %ecx, %ecx + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S b/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S new file mode 100644 index 0000000000..add4e203fb --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_return_void_no_barrier.S @@ -0,0 +1,9 @@ + movl rSELF, %eax + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + xorl %eax, %eax + xorl %ecx, %ecx + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86/op_return_wide.S b/runtime/interpreter/mterp/x86/op_return_wide.S new file mode 100644 index 0000000000..34a3380326 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_return_wide.S @@ -0,0 +1,15 @@ +/* + * Return a 64-bit value. + */ + /* return-wide vAA */ + .extern MterpThreadFenceForConstructor + call MterpThreadFenceForConstructor + movl rSELF, %eax + testl $$(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST), THREAD_FLAGS_OFFSET(%eax) + jz 1f + movl %eax, OUT_ARG0(%esp) + call MterpSuspendCheck +1: + GET_VREG %eax rINST # eax <- v[AA+0] + GET_VREG_HIGH %ecx rINST # ecx <- v[AA+1] + jmp MterpReturn diff --git a/runtime/interpreter/mterp/x86/op_rsub_int.S b/runtime/interpreter/mterp/x86/op_rsub_int.S new file mode 100644 index 0000000000..d6449c6c47 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rsub_int.S @@ -0,0 +1,2 @@ +/* this op is "rsub-int", but can be thought of as "rsub-int/lit16" */ +%include "x86/binopLit16.S" {"instr":"subl %eax, %ecx","result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86/op_rsub_int_lit8.S b/runtime/interpreter/mterp/x86/op_rsub_int_lit8.S new file mode 100644 index 0000000000..15d0e359bf --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_rsub_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"subl %eax, %ecx" , "result":"%ecx"} diff --git a/runtime/interpreter/mterp/x86/op_sget.S b/runtime/interpreter/mterp/x86/op_sget.S new file mode 100644 index 0000000000..ed5aedf719 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sget.S @@ -0,0 +1,26 @@ +%default { "is_object":"0", "helper":"artGet32StaticFromCode" } +/* + * General SGET handler wrapper. + * + * for: sget, sget-object, sget-boolean, sget-byte, sget-char, sget-short + */ + /* op vAA, field@BBBB */ + .extern $helper + EXPORT_PC + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call $helper + movl rSELF, %ecx + REFRESH_IBASE_FROM_SELF %ecx + cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + .if $is_object + SET_VREG_OBJECT %eax rINST # fp[A] <- value + .else + SET_VREG %eax rINST # fp[A] <- value + .endif + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sget_boolean.S b/runtime/interpreter/mterp/x86/op_sget_boolean.S new file mode 100644 index 0000000000..f058dd8f7c --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sget_boolean.S @@ -0,0 +1 @@ +%include "x86/op_sget.S" {"helper":"artGetBooleanStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sget_byte.S b/runtime/interpreter/mterp/x86/op_sget_byte.S new file mode 100644 index 0000000000..c952f40772 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sget_byte.S @@ -0,0 +1 @@ +%include "x86/op_sget.S" {"helper":"artGetByteStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sget_char.S b/runtime/interpreter/mterp/x86/op_sget_char.S new file mode 100644 index 0000000000..d7bd410c7d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sget_char.S @@ -0,0 +1 @@ +%include "x86/op_sget.S" {"helper":"artGetCharStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sget_object.S b/runtime/interpreter/mterp/x86/op_sget_object.S new file mode 100644 index 0000000000..1c95f9a00e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sget_object.S @@ -0,0 +1 @@ +%include "x86/op_sget.S" {"is_object":"1", "helper":"artGetObjStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sget_short.S b/runtime/interpreter/mterp/x86/op_sget_short.S new file mode 100644 index 0000000000..6475306b26 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sget_short.S @@ -0,0 +1 @@ +%include "x86/op_sget.S" {"helper":"artGetShortStaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sget_wide.S b/runtime/interpreter/mterp/x86/op_sget_wide.S new file mode 100644 index 0000000000..76b993bf54 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sget_wide.S @@ -0,0 +1,21 @@ +/* + * SGET_WIDE handler wrapper. + * + */ + /* sget-wide vAA, field@BBBB */ + .extern artGet64StaticFromCode + EXPORT_PC + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref CCCC + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG2(%esp) # self + call artGet64StaticFromCode + movl rSELF, %ecx + cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx) + jnz MterpException + SET_VREG %eax rINST # fp[A]<- low part + SET_VREG_HIGH %edx rINST # fp[A+1]<- high part + REFRESH_IBASE_FROM_SELF %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_shl_int.S b/runtime/interpreter/mterp/x86/op_shl_int.S new file mode 100644 index 0000000000..6a41d1c70b --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shl_int.S @@ -0,0 +1 @@ +%include "x86/binop1.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_shl_int_2addr.S b/runtime/interpreter/mterp/x86/op_shl_int_2addr.S new file mode 100644 index 0000000000..72abb8ebe0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shl_int_2addr.S @@ -0,0 +1 @@ +%include "x86/shop2addr.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_shl_int_lit8.S b/runtime/interpreter/mterp/x86/op_shl_int_lit8.S new file mode 100644 index 0000000000..b8d6069177 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shl_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"sall %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_shl_long.S b/runtime/interpreter/mterp/x86/op_shl_long.S new file mode 100644 index 0000000000..56d13e3168 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shl_long.S @@ -0,0 +1,29 @@ +/* + * 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. x86 shifts automatically mask off + * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31 + * case specially. + */ + /* shl-long vAA, vBB, vCC */ + /* ecx gets shift count */ + /* Need to spill rINST */ + /* rINSTw gets AA */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE %eax # ecx <- v[BB+1] + GET_VREG %ecx %ecx # ecx <- vCC + GET_VREG %eax %eax # eax <- v[BB+0] + shldl %eax,rIBASE + sall %cl, %eax + testb $$32, %cl + je 2f + movl %eax, rIBASE + xorl %eax, %eax +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- %eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_shl_long_2addr.S b/runtime/interpreter/mterp/x86/op_shl_long_2addr.S new file mode 100644 index 0000000000..5da873f9b1 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shl_long_2addr.S @@ -0,0 +1,26 @@ +/* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shl-long/2addr vA, vB */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl rINSTbl, %ecx # ecx <- BA + andb $$0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- v[AA+0] + sarl $$4, %ecx # ecx <- B + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1] + GET_VREG %ecx %ecx # ecx <- vBB + shldl %eax, rIBASE + sall %cl, %eax + testb $$32, %cl + je 2f + movl %eax, rIBASE + xorl %eax, %eax +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_shr_int.S b/runtime/interpreter/mterp/x86/op_shr_int.S new file mode 100644 index 0000000000..687b2c3b7b --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shr_int.S @@ -0,0 +1 @@ +%include "x86/binop1.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_shr_int_2addr.S b/runtime/interpreter/mterp/x86/op_shr_int_2addr.S new file mode 100644 index 0000000000..533b0e95b9 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shr_int_2addr.S @@ -0,0 +1 @@ +%include "x86/shop2addr.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_shr_int_lit8.S b/runtime/interpreter/mterp/x86/op_shr_int_lit8.S new file mode 100644 index 0000000000..ebd1beafac --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shr_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"sarl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_shr_long.S b/runtime/interpreter/mterp/x86/op_shr_long.S new file mode 100644 index 0000000000..4490a9ad73 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shr_long.S @@ -0,0 +1,29 @@ +/* + * 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. x86 shifts automatically mask off + * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31 + * case specially. + */ + /* shr-long vAA, vBB, vCC */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE %eax # rIBASE<- v[BB+1] + GET_VREG %ecx %ecx # ecx <- vCC + GET_VREG %eax %eax # eax <- v[BB+0] + shrdl rIBASE, %eax + sarl %cl, rIBASE + testb $$32, %cl + je 2f + movl rIBASE, %eax + sarl $$31, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_shr_long_2addr.S b/runtime/interpreter/mterp/x86/op_shr_long_2addr.S new file mode 100644 index 0000000000..57494f9ac0 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_shr_long_2addr.S @@ -0,0 +1,26 @@ +/* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shl-long/2addr vA, vB */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl rINSTbl, %ecx # ecx <- BA + andb $$0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- v[AA+0] + sarl $$4, %ecx # ecx <- B + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1] + GET_VREG %ecx %ecx # ecx <- vBB + shrdl rIBASE, %eax + sarl %cl, rIBASE + testb $$32, %cl + je 2f + movl rIBASE, %eax + sarl $$31, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_sparse_switch.S b/runtime/interpreter/mterp/x86/op_sparse_switch.S new file mode 100644 index 0000000000..fdaec4762a --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sparse_switch.S @@ -0,0 +1 @@ +%include "x86/op_packed_switch.S" { "func":"MterpDoSparseSwitch" } diff --git a/runtime/interpreter/mterp/x86/op_sput.S b/runtime/interpreter/mterp/x86/op_sput.S new file mode 100644 index 0000000000..04a8f23f65 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sput.S @@ -0,0 +1,22 @@ +%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 + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + GET_VREG rINST rINST + movl rINST, OUT_ARG1(%esp) # fp[AA] + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG2(%esp) # referrer + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call $helper + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sput_boolean.S b/runtime/interpreter/mterp/x86/op_sput_boolean.S new file mode 100644 index 0000000000..63601bd2bb --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sput_boolean.S @@ -0,0 +1 @@ +%include "x86/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sput_byte.S b/runtime/interpreter/mterp/x86/op_sput_byte.S new file mode 100644 index 0000000000..63601bd2bb --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sput_byte.S @@ -0,0 +1 @@ +%include "x86/op_sput.S" {"helper":"artSet8StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sput_char.S b/runtime/interpreter/mterp/x86/op_sput_char.S new file mode 100644 index 0000000000..1749f7c9f8 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sput_char.S @@ -0,0 +1 @@ +%include "x86/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sput_object.S b/runtime/interpreter/mterp/x86/op_sput_object.S new file mode 100644 index 0000000000..0480e00cfd --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sput_object.S @@ -0,0 +1,13 @@ + EXPORT_PC + leal OFF_FP_SHADOWFRAME(rFP), %eax + movl %eax, OUT_ARG0(%esp) + movl rPC, OUT_ARG1(%esp) + REFRESH_INST ${opnum} + movl rINST, OUT_ARG2(%esp) + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) + call MterpSputObject + testl %eax, %eax + jz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sput_short.S b/runtime/interpreter/mterp/x86/op_sput_short.S new file mode 100644 index 0000000000..1749f7c9f8 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sput_short.S @@ -0,0 +1 @@ +%include "x86/op_sput.S" {"helper":"artSet16StaticFromCode"} diff --git a/runtime/interpreter/mterp/x86/op_sput_wide.S b/runtime/interpreter/mterp/x86/op_sput_wide.S new file mode 100644 index 0000000000..d58d5af1a6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sput_wide.S @@ -0,0 +1,20 @@ +/* + * SPUT_WIDE handler wrapper. + * + */ + /* sput-wide vAA, field@BBBB */ + .extern artSet64IndirectStaticFromMterp + EXPORT_PC + movzwl 2(rPC), %eax + movl %eax, OUT_ARG0(%esp) # field ref BBBB + movl OFF_FP_METHOD(rFP), %eax + movl %eax, OUT_ARG1(%esp) # referrer + leal VREG_ADDRESS(rINST), %eax + movl %eax, OUT_ARG2(%esp) # &fp[AA] + movl rSELF, %ecx + movl %ecx, OUT_ARG3(%esp) # self + call artSet64IndirectStaticFromMterp + testl %eax, %eax + jnz MterpException + REFRESH_IBASE + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_sub_double.S b/runtime/interpreter/mterp/x86/op_sub_double.S new file mode 100644 index 0000000000..e83afeb215 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_double.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_sub_double_2addr.S b/runtime/interpreter/mterp/x86/op_sub_double_2addr.S new file mode 100644 index 0000000000..af9a2ab3f8 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_double_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"subs","suff":"d"} diff --git a/runtime/interpreter/mterp/x86/op_sub_float.S b/runtime/interpreter/mterp/x86/op_sub_float.S new file mode 100644 index 0000000000..423d834069 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_float.S @@ -0,0 +1 @@ +%include "x86/sseBinop.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_sub_float_2addr.S b/runtime/interpreter/mterp/x86/op_sub_float_2addr.S new file mode 100644 index 0000000000..18de000b49 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_float_2addr.S @@ -0,0 +1 @@ +%include "x86/sseBinop2Addr.S" {"instr":"subs","suff":"s"} diff --git a/runtime/interpreter/mterp/x86/op_sub_int.S b/runtime/interpreter/mterp/x86/op_sub_int.S new file mode 100644 index 0000000000..7fe03fb169 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_int.S @@ -0,0 +1 @@ +%include "x86/binop.S" {"instr":"subl (rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_sub_int_2addr.S b/runtime/interpreter/mterp/x86/op_sub_int_2addr.S new file mode 100644 index 0000000000..cc9bf60f2e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_int_2addr.S @@ -0,0 +1 @@ +%include "x86/binop2addr.S" {"instr":"subl %eax, (rFP,%ecx,4)"} diff --git a/runtime/interpreter/mterp/x86/op_sub_long.S b/runtime/interpreter/mterp/x86/op_sub_long.S new file mode 100644 index 0000000000..014591e41e --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_long.S @@ -0,0 +1 @@ +%include "x86/binopWide.S" {"instr1":"subl (rFP,%ecx,4), rIBASE", "instr2":"sbbl 4(rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_sub_long_2addr.S b/runtime/interpreter/mterp/x86/op_sub_long_2addr.S new file mode 100644 index 0000000000..7498029ebf --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_sub_long_2addr.S @@ -0,0 +1 @@ +%include "x86/binopWide2addr.S" {"instr1":"subl %eax, (rFP,rINST,4)","instr2":"sbbl %ecx, 4(rFP,rINST,4)"} diff --git a/runtime/interpreter/mterp/x86/op_throw.S b/runtime/interpreter/mterp/x86/op_throw.S new file mode 100644 index 0000000000..15b20b56be --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_throw.S @@ -0,0 +1,11 @@ +/* + * Throw an exception object in the current thread. + */ + /* throw vAA */ + EXPORT_PC + GET_VREG %eax rINST # eax<- vAA (exception object) + testl %eax, %eax + jz common_errNullObject + movl rSELF,%ecx + movl %eax, THREAD_EXCEPTION_OFFSET(%ecx) + jmp MterpException diff --git a/runtime/interpreter/mterp/x86/op_unused_3e.S b/runtime/interpreter/mterp/x86/op_unused_3e.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_3e.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_3f.S b/runtime/interpreter/mterp/x86/op_unused_3f.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_3f.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_40.S b/runtime/interpreter/mterp/x86/op_unused_40.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_40.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_41.S b/runtime/interpreter/mterp/x86/op_unused_41.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_41.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_42.S b/runtime/interpreter/mterp/x86/op_unused_42.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_42.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_43.S b/runtime/interpreter/mterp/x86/op_unused_43.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_43.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_79.S b/runtime/interpreter/mterp/x86/op_unused_79.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_79.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_7a.S b/runtime/interpreter/mterp/x86/op_unused_7a.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_7a.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_f4.S b/runtime/interpreter/mterp/x86/op_unused_f4.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_f4.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_fa.S b/runtime/interpreter/mterp/x86/op_unused_fa.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_fa.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_fb.S b/runtime/interpreter/mterp/x86/op_unused_fb.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_fb.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_fc.S b/runtime/interpreter/mterp/x86/op_unused_fc.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_fc.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_fd.S b/runtime/interpreter/mterp/x86/op_unused_fd.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_fd.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_fe.S b/runtime/interpreter/mterp/x86/op_unused_fe.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_fe.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_unused_ff.S b/runtime/interpreter/mterp/x86/op_unused_ff.S new file mode 100644 index 0000000000..31d98c1f39 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_unused_ff.S @@ -0,0 +1 @@ +%include "x86/unused.S" diff --git a/runtime/interpreter/mterp/x86/op_ushr_int.S b/runtime/interpreter/mterp/x86/op_ushr_int.S new file mode 100644 index 0000000000..dfe25ff05d --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_ushr_int.S @@ -0,0 +1 @@ +%include "x86/binop1.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_ushr_int_2addr.S b/runtime/interpreter/mterp/x86/op_ushr_int_2addr.S new file mode 100644 index 0000000000..c14bc980ba --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_ushr_int_2addr.S @@ -0,0 +1 @@ +%include "x86/shop2addr.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_ushr_int_lit8.S b/runtime/interpreter/mterp/x86/op_ushr_int_lit8.S new file mode 100644 index 0000000000..e129f6bbe6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_ushr_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"shrl %cl, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_ushr_long.S b/runtime/interpreter/mterp/x86/op_ushr_long.S new file mode 100644 index 0000000000..287946ebde --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_ushr_long.S @@ -0,0 +1,29 @@ +/* + * 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. x86 shifts automatically mask off + * the low 5 bits of %cl, so have to handle the 64 > shiftcount > 31 + * case specially. + */ + /* shr-long vAA, vBB, vCC */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl 2(rPC), %eax # eax <- BB + movzbl 3(rPC), %ecx # ecx <- CC + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE %eax # rIBASE <- v[BB+1] + GET_VREG %ecx %ecx # ecx <- vCC + GET_VREG %eax %eax # eax <- v[BB+0] + shrdl rIBASE, %eax + shrl %cl, rIBASE + testb $$32, %cl + je 2f + movl rIBASE, %eax + xorl rIBASE, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[BB+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/op_ushr_long_2addr.S b/runtime/interpreter/mterp/x86/op_ushr_long_2addr.S new file mode 100644 index 0000000000..39c27242c4 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_ushr_long_2addr.S @@ -0,0 +1,26 @@ +/* + * Long integer shift, 2addr version. vA is 64-bit value/result, vB is + * 32-bit shift distance. + */ + /* shl-long/2addr vA, vB */ + /* ecx gets shift count */ + /* Need to spill rIBASE */ + /* rINSTw gets AA */ + movzbl rINSTbl, %ecx # ecx <- BA + andb $$0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- v[AA+0] + sarl $$4, %ecx # ecx <- B + movl rIBASE, LOCAL0(%esp) + GET_VREG_HIGH rIBASE rINST # rIBASE <- v[AA+1] + GET_VREG %ecx %ecx # ecx <- vBB + shrdl rIBASE, %eax + shrl %cl, rIBASE + testb $$32, %cl + je 2f + movl rIBASE, %eax + xorl rIBASE, rIBASE +2: + SET_VREG_HIGH rIBASE rINST # v[AA+1] <- rIBASE + movl LOCAL0(%esp), rIBASE + SET_VREG %eax rINST # v[AA+0] <- eax + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/op_xor_int.S b/runtime/interpreter/mterp/x86/op_xor_int.S new file mode 100644 index 0000000000..35aca6a821 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_xor_int.S @@ -0,0 +1 @@ +%include "x86/binop.S" {"instr":"xorl (rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_xor_int_2addr.S b/runtime/interpreter/mterp/x86/op_xor_int_2addr.S new file mode 100644 index 0000000000..d7b70e2ea1 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_xor_int_2addr.S @@ -0,0 +1 @@ +%include "x86/binop2addr.S" {"instr":"xorl %eax, (rFP,%ecx,4)"} diff --git a/runtime/interpreter/mterp/x86/op_xor_int_lit16.S b/runtime/interpreter/mterp/x86/op_xor_int_lit16.S new file mode 100644 index 0000000000..115f0a0410 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_xor_int_lit16.S @@ -0,0 +1 @@ +%include "x86/binopLit16.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_xor_int_lit8.S b/runtime/interpreter/mterp/x86/op_xor_int_lit8.S new file mode 100644 index 0000000000..243971c549 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_xor_int_lit8.S @@ -0,0 +1 @@ +%include "x86/binopLit8.S" {"instr":"xorl %ecx, %eax"} diff --git a/runtime/interpreter/mterp/x86/op_xor_long.S b/runtime/interpreter/mterp/x86/op_xor_long.S new file mode 100644 index 0000000000..0d3c0f5cac --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_xor_long.S @@ -0,0 +1 @@ +%include "x86/binopWide.S" {"instr1":"xorl (rFP,%ecx,4), rIBASE", "instr2":"xorl 4(rFP,%ecx,4), %eax"} diff --git a/runtime/interpreter/mterp/x86/op_xor_long_2addr.S b/runtime/interpreter/mterp/x86/op_xor_long_2addr.S new file mode 100644 index 0000000000..b5000e4426 --- /dev/null +++ b/runtime/interpreter/mterp/x86/op_xor_long_2addr.S @@ -0,0 +1 @@ +%include "x86/binopWide2addr.S" {"instr1":"xorl %eax, (rFP,rINST,4)","instr2":"xorl %ecx, 4(rFP,rINST,4)"} diff --git a/runtime/interpreter/mterp/x86/shop2addr.S b/runtime/interpreter/mterp/x86/shop2addr.S new file mode 100644 index 0000000000..94d35452f6 --- /dev/null +++ b/runtime/interpreter/mterp/x86/shop2addr.S @@ -0,0 +1,13 @@ +%default {"result":"%eax"} +/* + * Generic 32-bit "shift/2addr" operation. + */ + /* shift/2addr vA, vB */ + movzx rINSTbl, %ecx # eax <- BA + sarl $$4, %ecx # ecx <- B + GET_VREG %ecx %ecx # eax <- vBB + andb $$0xf, rINSTbl # rINST <- A + GET_VREG %eax rINST # eax <- vAA + $instr # ex: sarl %cl, %eax + SET_VREG $result rINST + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/sseBinop.S b/runtime/interpreter/mterp/x86/sseBinop.S new file mode 100644 index 0000000000..63a1e21a8f --- /dev/null +++ b/runtime/interpreter/mterp/x86/sseBinop.S @@ -0,0 +1,9 @@ +%default {"instr":"","suff":""} + movzbl 2(rPC), %ecx # ecx <- BB + movzbl 3(rPC), %eax # eax <- CC + movs${suff} VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + ${instr}${suff} VREG_ADDRESS(%eax), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(rINST) # vAA <- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 2 diff --git a/runtime/interpreter/mterp/x86/sseBinop2Addr.S b/runtime/interpreter/mterp/x86/sseBinop2Addr.S new file mode 100644 index 0000000000..d157e67b91 --- /dev/null +++ b/runtime/interpreter/mterp/x86/sseBinop2Addr.S @@ -0,0 +1,10 @@ +%default {"instr":"","suff":""} + movzx rINSTbl, %ecx # ecx <- A+ + andl $$0xf, %ecx # ecx <- A + movs${suff} VREG_ADDRESS(%ecx), %xmm0 # %xmm0 <- 1st src + sarl $$4, rINST # rINST<- B + ${instr}${suff} VREG_ADDRESS(rINST), %xmm0 + movs${suff} %xmm0, VREG_ADDRESS(%ecx) # vAA<- %xmm0 + pxor %xmm0, %xmm0 + movs${suff} %xmm0, VREG_REF_ADDRESS(rINST) # clear ref + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/unop.S b/runtime/interpreter/mterp/x86/unop.S new file mode 100644 index 0000000000..00d3e156fc --- /dev/null +++ b/runtime/interpreter/mterp/x86/unop.S @@ -0,0 +1,13 @@ +%default {"instr":""} +/* + * Generic 32-bit unary operation. Provide an "instr" line that + * specifies an instruction that performs "result = op eax". + */ + /* unop vA, vB */ + movzbl rINSTbl,%ecx # ecx <- A+ + sarl $$4,rINST # rINST <- B + GET_VREG %eax rINST # eax <- vB + andb $$0xf,%cl # ecx <- A + $instr + SET_VREG %eax %ecx + ADVANCE_PC_FETCH_AND_GOTO_NEXT 1 diff --git a/runtime/interpreter/mterp/x86/unused.S b/runtime/interpreter/mterp/x86/unused.S new file mode 100644 index 0000000000..c95ef947d3 --- /dev/null +++ b/runtime/interpreter/mterp/x86/unused.S @@ -0,0 +1,4 @@ +/* + * Bail to reference interpreter to throw. + */ + jmp MterpFallback diff --git a/runtime/interpreter/mterp/x86/zcmp.S b/runtime/interpreter/mterp/x86/zcmp.S new file mode 100644 index 0000000000..5ce4f0f6a7 --- /dev/null +++ b/runtime/interpreter/mterp/x86/zcmp.S @@ -0,0 +1,24 @@ +/* + * 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(rINST) # compare (vA, 0) + movl $$2, %eax # assume branch not taken + j${revcmp} 1f + movswl 2(rPC),%eax # fetch signed displacement +1: + addl %eax, %eax # eax <- AA * 2 + leal (rPC, %eax), rPC + FETCH_INST + jg 2f # AA * 2 > 0 => no suspend check +#if MTERP_SUSPEND + REFRESH_IBASE +#else + jmp MterpCheckSuspendAndContinue +#endif +2: + GOTO_NEXT diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc index 4fb6df19f1..2507fe9e6e 100644 --- a/runtime/jdwp/jdwp_socket.cc +++ b/runtime/jdwp/jdwp_socket.cc @@ -30,13 +30,13 @@ #include "base/stringprintf.h" #include "jdwp/jdwp_priv.h" -#define kBasePort 8000 -#define kMaxPort 8040 - namespace art { namespace JDWP { +static constexpr uint16_t kBasePort = 8000; +static constexpr uint16_t kMaxPort = 8040; + /* * JDWP network state. * @@ -275,11 +275,33 @@ bool JdwpSocketState::Establish(const JdwpOptions* options) { * Start by resolving the host name. */ #if defined(__linux__) + // Initial size of the work buffer used in gethostbyname_r. + // + // The call to gethostbyname_r below requires a user-allocated buffer, + // the size of which depends on the system. The initial implementation + // used to use a 128-byte buffer, but that was not enough on some + // systems (maybe because of IPv6), causing failures in JDWP host + // testing; thus it was increased to 256. + // + // However, we should not use a fixed size: gethostbyname_r's + // documentation states that if the work buffer is too small (i.e. if + // gethostbyname_r returns `ERANGE`), then the function should be + // called again with a bigger buffer. Which we do now, starting with + // an initial 256-byte buffer, and doubling it until gethostbyname_r + // accepts this size. + static constexpr size_t kInitialAuxBufSize = 256; + + std::vector<char> auxBuf(kInitialAuxBufSize); hostent he; - char auxBuf[128]; int error; - int cc = gethostbyname_r(options->host.c_str(), &he, auxBuf, sizeof(auxBuf), &pEntry, &error); - if (cc != 0) { + int cc; + while ((cc = gethostbyname_r( + options->host.c_str(), &he, auxBuf.data(), auxBuf.size(), &pEntry, &error)) + == ERANGE) { + // The work buffer `auxBuf` is too small; enlarge it. + auxBuf.resize(auxBuf.size() * 2); + } + if (cc != 0 || pEntry == nullptr) { LOG(WARNING) << "gethostbyname_r('" << options->host << "') failed: " << hstrerror(error); return false; } @@ -298,7 +320,8 @@ bool JdwpSocketState::Establish(const JdwpOptions* options) { addr.addrInet.sin_port = htons(options->port); - LOG(INFO) << "Connecting out to " << inet_ntoa(addr.addrInet.sin_addr) << ":" << ntohs(addr.addrInet.sin_port); + LOG(INFO) << "Connecting out to " << inet_ntoa(addr.addrInet.sin_addr) << ":" + << ntohs(addr.addrInet.sin_port); /* * Create a socket. @@ -313,13 +336,15 @@ bool JdwpSocketState::Establish(const JdwpOptions* options) { * Try to connect. */ if (connect(clientSock, &addr.addrPlain, sizeof(addr)) != 0) { - PLOG(ERROR) << "Unable to connect to " << inet_ntoa(addr.addrInet.sin_addr) << ":" << ntohs(addr.addrInet.sin_port); + PLOG(ERROR) << "Unable to connect to " << inet_ntoa(addr.addrInet.sin_addr) << ":" + << ntohs(addr.addrInet.sin_port); close(clientSock); clientSock = -1; return false; } - LOG(INFO) << "Connection established to " << options->host << " (" << inet_ntoa(addr.addrInet.sin_addr) << ":" << ntohs(addr.addrInet.sin_port) << ")"; + LOG(INFO) << "Connection established to " << options->host << " (" + << inet_ntoa(addr.addrInet.sin_addr) << ":" << ntohs(addr.addrInet.sin_port) << ")"; SetAwaitingHandshake(true); input_count_ = 0; @@ -438,7 +463,8 @@ bool JdwpSocketState::ProcessIncoming() { } } if (clientSock >= 0 && FD_ISSET(clientSock, &readfds)) { - readCount = read(clientSock, input_buffer_ + input_count_, sizeof(input_buffer_) - input_count_); + readCount = + read(clientSock, input_buffer_ + input_count_, sizeof(input_buffer_) - input_count_); if (readCount < 0) { /* read failed */ if (errno != EINTR) { @@ -479,7 +505,8 @@ bool JdwpSocketState::ProcessIncoming() { errno = 0; int cc = TEMP_FAILURE_RETRY(write(clientSock, input_buffer_, kMagicHandshakeLen)); if (cc != kMagicHandshakeLen) { - PLOG(ERROR) << "Failed writing handshake bytes (" << cc << " of " << kMagicHandshakeLen << ")"; + PLOG(ERROR) << "Failed writing handshake bytes (" + << cc << " of " << kMagicHandshakeLen << ")"; goto fail; } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 8f4d24f385..f540814fbe 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -202,6 +202,13 @@ void Jit::StopProfileSaver() { } } +bool Jit::JitAtFirstUse() { + if (instrumentation_cache_ != nullptr) { + return instrumentation_cache_->HotMethodThreshold() == 0; + } + return false; +} + Jit::~Jit() { DCHECK(!save_profiling_info_ || !ProfileSaver::IsStarted()); if (dump_info_on_shutdown_) { @@ -217,7 +224,6 @@ Jit::~Jit() { } void Jit::CreateInstrumentationCache(size_t compile_threshold, size_t warmup_threshold) { - CHECK_GT(compile_threshold, 0U); instrumentation_cache_.reset( new jit::JitInstrumentationCache(compile_threshold, warmup_threshold)); } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 429edf65a6..a80f51f397 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -86,6 +86,8 @@ class Jit { // into the specified class linker to the jit debug interface, void DumpTypeInfoForLoadedTypes(ClassLinker* linker); + bool JitAtFirstUse(); + private: Jit(); bool LoadCompiler(std::string* error_msg); @@ -142,6 +144,10 @@ class JitOptions { void SetSaveProfilingInfo(bool b) { save_profiling_info_ = b; } + void SetJitAtFirstUse() { + use_jit_ = true; + compile_threshold_ = 0; + } private: bool use_jit_; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 64b2c899aa..bec6e13d53 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -719,7 +719,7 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_S } } -void JitCodeCache::GetCompiledArtMethods(const std::set<const std::string>& dex_base_locations, +void JitCodeCache::GetCompiledArtMethods(const std::set<std::string>& dex_base_locations, std::vector<ArtMethod*>& methods) { MutexLock mu(Thread::Current(), lock_); for (auto it : method_code_map_) { diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 67fa928f61..69fc5532c1 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -153,7 +153,7 @@ class JitCodeCache { void* MoreCore(const void* mspace, intptr_t increment); // Adds to `methods` all the compiled ArtMethods which are part of any of the given dex locations. - void GetCompiledArtMethods(const std::set<const std::string>& dex_base_locations, + void GetCompiledArtMethods(const std::set<std::string>& dex_base_locations, std::vector<ArtMethod*>& methods) REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc index 4cbaf2cbfa..d597b36e95 100644 --- a/runtime/jit/jit_instrumentation.cc +++ b/runtime/jit/jit_instrumentation.cc @@ -162,14 +162,25 @@ void JitInstrumentationListener::MethodEntered(Thread* thread, mirror::Object* /*this_object*/, ArtMethod* method, uint32_t /*dex_pc*/) { + if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) { + // The compiler requires a ProfilingInfo object. + ProfilingInfo::Create(thread, method, /* retry_allocation */ true); + JitCompileTask compile_task(method, JitCompileTask::kCompile); + compile_task.Run(thread); + return; + } + instrumentation_cache_->AddSamples(thread, method, 1); } -void JitInstrumentationListener::BackwardBranch(Thread* thread, - ArtMethod* method, - int32_t dex_pc_offset) { - CHECK_LE(dex_pc_offset, 0); - instrumentation_cache_->AddSamples(thread, method, 1); +void JitInstrumentationListener::Branch(Thread* thread, + ArtMethod* method, + uint32_t dex_pc ATTRIBUTE_UNUSED, + int32_t dex_pc_offset) { + if (dex_pc_offset < 0) { + // Increment method hotness if it is a backward branch. + instrumentation_cache_->AddSamples(thread, method, 1); + } } void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread, diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h index 15969e4d53..06559ad2e4 100644 --- a/runtime/jit/jit_instrumentation.h +++ b/runtime/jit/jit_instrumentation.h @@ -70,7 +70,7 @@ class JitInstrumentationListener : public instrumentation::InstrumentationListen void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/, ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { } - void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset) + void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_); void InvokeVirtualOrInterface(Thread* thread, @@ -84,7 +84,7 @@ class JitInstrumentationListener : public instrumentation::InstrumentationListen static constexpr uint32_t kJitEvents = instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kBackwardBranch | + instrumentation::Instrumentation::kBranch | instrumentation::Instrumentation::kInvokeVirtualOrInterface; private: @@ -101,6 +101,11 @@ class JitInstrumentationCache { SHARED_REQUIRES(Locks::mutator_lock_); void CreateThreadPool(); void DeleteThreadPool(Thread* self); + + size_t HotMethodThreshold() const { + return hot_method_threshold_; + } + // Wait until there is no more pending compilation tasks. void WaitForCompilationToFinish(Thread* self); diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index a132701796..b4b872ff50 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -24,8 +24,11 @@ #include "art_method-inl.h" #include "base/mutex.h" +#include "base/scoped_flock.h" #include "base/stl_util.h" +#include "base/unix_file/fd_file.h" #include "jit/profiling_info.h" +#include "os.h" #include "safe_map.h" namespace art { @@ -37,8 +40,17 @@ bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, return true; } + ScopedFlock flock; + std::string error; + if (!flock.Init(filename.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC, /* block */ false, &error)) { + LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error; + return false; + } + + int fd = flock.GetFile()->Fd(); + ProfileCompilationInfo info; - if (!info.Load(filename)) { + if (!info.Load(fd)) { LOG(WARNING) << "Could not load previous profile data from file " << filename; return false; } @@ -54,9 +66,14 @@ bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, } } + if (!flock.GetFile()->ClearContent()) { + PLOG(WARNING) << "Could not clear profile file: " << filename; + return false; + } + // This doesn't need locking because we are trying to lock the file for exclusive // access and fail immediately if we can't. - bool result = info.Save(filename); + bool result = info.Save(fd); if (result) { VLOG(profiler) << "Successfully saved profile info to " << filename << " Size: " << GetFileSizeBytes(filename); @@ -66,64 +83,20 @@ bool ProfileCompilationInfo::SaveProfilingInfo(const std::string& filename, return result; } -enum OpenMode { - READ, - READ_WRITE -}; - -static int OpenFile(const std::string& filename, OpenMode open_mode) { - int fd = -1; - switch (open_mode) { - case READ: - fd = open(filename.c_str(), O_RDONLY); - break; - case READ_WRITE: - // TODO(calin) allow the shared uid of the app to access the file. - fd = open(filename.c_str(), O_WRONLY | O_TRUNC | O_NOFOLLOW | O_CLOEXEC); - break; - } - - if (fd < 0) { - PLOG(WARNING) << "Failed to open profile file " << filename; - return -1; - } - - // Lock the file for exclusive access but don't wait if we can't lock it. - int err = flock(fd, LOCK_EX | LOCK_NB); - if (err < 0) { - PLOG(WARNING) << "Failed to lock profile file " << filename; - return -1; - } - return fd; -} - -static bool CloseDescriptorForFile(int fd, const std::string& filename) { - // Now unlock the file, allowing another process in. - int err = flock(fd, LOCK_UN); - if (err < 0) { - PLOG(WARNING) << "Failed to unlock profile file " << filename; - return false; - } - - // Done, close the file. - err = ::close(fd); - if (err < 0) { - PLOG(WARNING) << "Failed to close descriptor for profile file" << filename; - return false; - } - - return true; -} - -static void WriteToFile(int fd, const std::ostringstream& os) { +static bool WriteToFile(int fd, const std::ostringstream& os) { std::string data(os.str()); const char *p = data.c_str(); size_t length = data.length(); do { - int n = ::write(fd, p, length); + int n = TEMP_FAILURE_RETRY(write(fd, p, length)); + if (n < 0) { + PLOG(WARNING) << "Failed to write to descriptor: " << fd; + return false; + } p += n; length -= n; } while (length > 0); + return true; } static constexpr const char kFieldSeparator = ','; @@ -137,13 +110,8 @@ static constexpr const char kLineSeparator = '\n'; * /system/priv-app/app/app.apk,131232145,11,23,454,54 * /system/priv-app/app/app.apk:classes5.dex,218490184,39,13,49,1 **/ -bool ProfileCompilationInfo::Save(const std::string& filename) { - int fd = OpenFile(filename, READ_WRITE); - if (fd == -1) { - return false; - } - - // TODO(calin): Merge with a previous existing profile. +bool ProfileCompilationInfo::Save(uint32_t fd) { + DCHECK_GE(fd, 0u); // TODO(calin): Profile this and see how much memory it takes. If too much, // write to file directly. std::ostringstream os; @@ -158,9 +126,7 @@ bool ProfileCompilationInfo::Save(const std::string& filename) { os << kLineSeparator; } - WriteToFile(fd, os); - - return CloseDescriptorForFile(fd, filename); + return WriteToFile(fd, os); } // TODO(calin): This a duplicate of Utils::Split fixing the case where the first character @@ -222,7 +188,9 @@ bool ProfileCompilationInfo::ProcessLine(const std::string& line) { LOG(WARNING) << "Cannot parse method_idx " << parts[i]; return false; } - AddData(dex_location, checksum, method_idx); + if (!AddData(dex_location, checksum, method_idx)) { + return false; + } } return true; } @@ -249,23 +217,18 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l return new_line_pos == -1 ? new_line_pos : new_line_pos + 1; } -bool ProfileCompilationInfo::Load(const std::string& filename) { - int fd = OpenFile(filename, READ); - if (fd == -1) { - return false; - } +bool ProfileCompilationInfo::Load(uint32_t fd) { + DCHECK_GE(fd, 0u); std::string current_line; const int kBufferSize = 1024; char buffer[kBufferSize]; - bool success = true; - while (success) { - int n = read(fd, buffer, kBufferSize); + while (true) { + int n = TEMP_FAILURE_RETRY(read(fd, buffer, kBufferSize)); if (n < 0) { - PLOG(WARNING) << "Error when reading profile file " << filename; - success = false; - break; + PLOG(WARNING) << "Error when reading profile file"; + return false; } else if (n == 0) { break; } @@ -278,17 +241,13 @@ bool ProfileCompilationInfo::Load(const std::string& filename) { break; } if (!ProcessLine(current_line)) { - success = false; - break; + return false; } // Reset the current line (we just processed it). current_line.clear(); } } - if (!success) { - info_.clear(); - } - return CloseDescriptorForFile(fd, filename) && success; + return true; } bool ProfileCompilationInfo::Load(const ProfileCompilationInfo& other) { @@ -369,4 +328,8 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* return os.str(); } +bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) { + return info_.Equals(other.info_); +} + } // namespace art diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index 26e1ac385f..ffd14335d7 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -39,15 +39,18 @@ class ArtMethod; */ class ProfileCompilationInfo { public: + // Saves profile information about the given methods in the given file. + // Note that the saving proceeds only if the file can be locked for exclusive access. + // If not (the locking is not blocking), the function does not save and returns false. static bool SaveProfilingInfo(const std::string& filename, const std::vector<ArtMethod*>& methods); - // Loads profile information from the given file. - bool Load(const std::string& profile_filename); + // Loads profile information from the given file descriptor. + bool Load(uint32_t fd); // Loads the data from another ProfileCompilationInfo object. bool Load(const ProfileCompilationInfo& info); - // Saves the profile data to the given file. - bool Save(const std::string& profile_filename); + // Saves the profile data to the given file descriptor. + bool Save(uint32_t fd); // Returns the number of methods that were profiled. uint32_t GetNumberOfMethods() const; @@ -61,6 +64,9 @@ class ProfileCompilationInfo { std::string DumpInfo(const std::vector<const DexFile*>* dex_files, bool print_full_dex_location = true) const; + // For testing purposes. + bool Equals(ProfileCompilationInfo& other); + private: bool AddData(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); bool ProcessLine(const std::string& line); @@ -69,10 +75,18 @@ class ProfileCompilationInfo { explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} uint32_t checksum; std::set<uint16_t> method_set; + + bool operator==(const DexFileData& other) const { + return checksum == other.checksum && method_set == other.method_set; + } }; using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>; + friend class ProfileCompilationInfoTest; + friend class CompilerDriverProfileTest; + friend class ProfileAssistantTest; + DexFileToProfileInfoMap info_; }; diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc new file mode 100644 index 0000000000..482ea06395 --- /dev/null +++ b/runtime/jit/profile_compilation_info_test.cc @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "base/unix_file/fd_file.h" +#include "art_method-inl.h" +#include "class_linker-inl.h" +#include "common_runtime_test.h" +#include "dex_file.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "handle_scope-inl.h" +#include "jit/offline_profiling_info.h" +#include "scoped_thread_state_change.h" + +namespace art { + +class ProfileCompilationInfoTest : public CommonRuntimeTest { + protected: + std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader, + const std::string& clazz) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_loader(hs.NewHandle( + reinterpret_cast<mirror::ClassLoader*>(self->DecodeJObject(class_loader)))); + mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); + + const auto pointer_size = class_linker->GetImagePointerSize(); + std::vector<ArtMethod*> methods; + for (auto& m : klass->GetVirtualMethods(pointer_size)) { + methods.push_back(&m); + } + return methods; + } + + bool AddData(const std::string& dex_location, + uint32_t checksum, + uint16_t method_index, + ProfileCompilationInfo* info) { + return info->AddData(dex_location, checksum, method_index); + } + + uint32_t GetFd(const ScratchFile& file) { + return static_cast<uint32_t>(file.GetFd()); + } +}; + +TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { + ScratchFile profile; + + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Save virtual methods from Main. + std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;"); + ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), main_methods)); + + // Check that what we saved is in the profile. + ProfileCompilationInfo info1; + ASSERT_TRUE(info1.Load(GetFd(profile))); + ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + ASSERT_TRUE(info1.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + } + } + + // Save virtual methods from Second. + std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;"); + ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(), second_methods)); + + // Check that what we saved is in the profile (methods form Main and Second). + ProfileCompilationInfo info2; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(info2.Load(GetFd(profile))); + ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + } + for (ArtMethod* m : second_methods) { + ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + } + } +} + +TEST_F(ProfileCompilationInfoTest, SaveFd) { + ScratchFile profile; + + ProfileCompilationInfo saved_info; + // Save a few methods. + for (uint16_t i = 0; i < 10; i++) { + ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); + } + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + ASSERT_TRUE(loaded_info.Equals(saved_info)); + + // Save more methods. + for (uint16_t i = 0; i < 100; i++) { + ASSERT_TRUE(AddData("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddData("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); + ASSERT_TRUE(AddData("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info)); + } + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Check that we get back everything we saved. + ProfileCompilationInfo loaded_info2; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info2.Load(GetFd(profile))); + ASSERT_TRUE(loaded_info2.Equals(saved_info)); +} + +TEST_F(ProfileCompilationInfoTest, AddDataFail) { + ScratchFile profile; + + ProfileCompilationInfo info; + ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info)); + // Trying to add info for an existing file but with a different checksum. + ASSERT_FALSE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info)); +} + +TEST_F(ProfileCompilationInfoTest, LoadFail) { + ScratchFile profile; + + ProfileCompilationInfo info1; + ASSERT_TRUE(AddData("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1)); + // Use the same file, change the checksum. + ProfileCompilationInfo info2; + ASSERT_TRUE(AddData("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2)); + + ASSERT_FALSE(info1.Load(info2)); +} + +} // namespace art diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index ec289ea2b5..b1a5a4b24c 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -22,16 +22,16 @@ namespace art { -// An arbitrary value to throttle save requests. Set to 500ms for now. +// An arbitrary value to throttle save requests. Set to 2s for now. static constexpr const uint64_t kMilisecondsToNano = 1000000; -static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 500 * kMilisecondsToNano; +static constexpr const uint64_t kMinimumTimeBetweenCodeCacheUpdatesNs = 2000 * kMilisecondsToNano; // TODO: read the constants from ProfileOptions, // Add a random delay each time we go to sleep so that we don't hammer the CPU // with all profile savers running at the same time. -static constexpr const uint64_t kRandomDelayMaxMs = 10 * 1000; // 10 seconds -static constexpr const uint64_t kMaxBackoffMs = 4 * 60 * 1000; // 4 minutes -static constexpr const uint64_t kSavePeriodMs = 4 * 1000; // 4 seconds +static constexpr const uint64_t kRandomDelayMaxMs = 20 * 1000; // 20 seconds +static constexpr const uint64_t kMaxBackoffMs = 5 * 60 * 1000; // 5 minutes +static constexpr const uint64_t kSavePeriodMs = 10 * 1000; // 10 seconds static constexpr const double kBackoffCoef = 1.5; static constexpr const uint32_t kMinimumNrOrMethodsToSave = 10; @@ -42,13 +42,12 @@ pthread_t ProfileSaver::profiler_pthread_ = 0U; ProfileSaver::ProfileSaver(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, const std::vector<std::string>& code_paths) - : output_filename_(output_filename), - jit_code_cache_(jit_code_cache), - tracked_dex_base_locations_(code_paths.begin(), code_paths.end()), + : jit_code_cache_(jit_code_cache), code_cache_last_update_time_ns_(0), shutting_down_(false), wait_lock_("ProfileSaver wait lock"), period_condition_("ProfileSaver period condition", wait_lock_) { + AddTrackedLocations(output_filename, code_paths); } void ProfileSaver::Run() { @@ -86,29 +85,47 @@ void ProfileSaver::Run() { } bool ProfileSaver::ProcessProfilingInfo() { - VLOG(profiler) << "Initiating save profiling information to: " << output_filename_; - uint64_t last_update_time_ns = jit_code_cache_->GetLastUpdateTimeNs(); if (last_update_time_ns - code_cache_last_update_time_ns_ - > kMinimumTimeBetweenCodeCacheUpdatesNs) { - VLOG(profiler) << "Not enough time has passed since the last code cache update."; + < kMinimumTimeBetweenCodeCacheUpdatesNs) { + VLOG(profiler) << "Not enough time has passed since the last code cache update." + << "Last update: " << last_update_time_ns + << " Last save: " << code_cache_last_update_time_ns_; return false; } uint64_t start = NanoTime(); code_cache_last_update_time_ns_ = last_update_time_ns; - std::vector<ArtMethod*> methods; + SafeMap<std::string, std::set<std::string>> tracked_locations; { - ScopedObjectAccess soa(Thread::Current()); - jit_code_cache_->GetCompiledArtMethods(tracked_dex_base_locations_, methods); - } - if (methods.size() < kMinimumNrOrMethodsToSave) { - VLOG(profiler) << "Not enough information to save. Nr of methods: " << methods.size(); - return false; + // Make a copy so that we don't hold the lock while doing I/O. + MutexLock mu(Thread::Current(), *Locks::profiler_lock_); + tracked_locations = tracked_dex_base_locations_; } + for (const auto& it : tracked_locations) { + if (ShuttingDown(Thread::Current())) { + return true; + } + const std::string& filename = it.first; + const std::set<std::string>& locations = it.second; + std::vector<ArtMethod*> methods; + { + ScopedObjectAccess soa(Thread::Current()); + jit_code_cache_->GetCompiledArtMethods(locations, methods); + } + if (methods.size() < kMinimumNrOrMethodsToSave) { + VLOG(profiler) << "Not enough information to save to: " << filename + <<" Nr of methods: " << methods.size(); + return false; + } - ProfileCompilationInfo::SaveProfilingInfo(output_filename_, methods); - VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start); + if (!ProfileCompilationInfo::SaveProfilingInfo(filename, methods)) { + LOG(WARNING) << "Could not save profiling info to " << filename; + return false; + } + + VLOG(profiler) << "Profile process time: " << PrettyDuration(NanoTime() - start); + } return true; } @@ -135,9 +152,13 @@ void ProfileSaver::Start(const std::string& output_filename, DCHECK(jit_code_cache != nullptr); MutexLock mu(Thread::Current(), *Locks::profiler_lock_); - // Don't start two profile saver threads. if (instance_ != nullptr) { - DCHECK(false) << "Tried to start two profile savers"; + // If we already have an instance, make sure it uses the same jit_code_cache. + // This may be called multiple times via Runtime::registerAppInfo (e.g. for + // apps which share the same runtime). + DCHECK_EQ(instance_->jit_code_cache_, jit_code_cache); + // Add the code_paths to the tracked locations. + instance_->AddTrackedLocations(output_filename, code_paths); return; } @@ -159,7 +180,7 @@ void ProfileSaver::Stop() { { MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_); - VLOG(profiler) << "Stopping profile saver thread for file: " << instance_->output_filename_; + VLOG(profiler) << "Stopping profile saver thread"; profile_saver = instance_; profiler_pthread = profiler_pthread_; if (instance_ == nullptr) { @@ -200,4 +221,15 @@ bool ProfileSaver::IsStarted() { return instance_ != nullptr; } +void ProfileSaver::AddTrackedLocations(const std::string& output_filename, + const std::vector<std::string>& code_paths) { + auto it = tracked_dex_base_locations_.find(output_filename); + if (it == tracked_dex_base_locations_.end()) { + tracked_dex_base_locations_.Put(output_filename, + std::set<std::string>(code_paths.begin(), code_paths.end())); + } else { + it->second.insert(code_paths.begin(), code_paths.end()); + } +} + } // namespace art diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h index d60142b205..3342790e43 100644 --- a/runtime/jit/profile_saver.h +++ b/runtime/jit/profile_saver.h @@ -20,12 +20,14 @@ #include "base/mutex.h" #include "jit_code_cache.h" #include "offline_profiling_info.h" +#include "safe_map.h" namespace art { class ProfileSaver { public: - // Starts the profile saver thread. + // Starts the profile saver thread if not already started. + // If the saver is already running it adds (output_filename, code_paths) to its tracked locations. static void Start(const std::string& output_filename, jit::JitCodeCache* jit_code_cache, const std::vector<std::string>& code_paths) @@ -58,14 +60,18 @@ class ProfileSaver { // Returns true if the saver is shutting down (ProfileSaver::Stop() has been called). bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_); + void AddTrackedLocations(const std::string& output_filename, + const std::vector<std::string>& code_paths) + REQUIRES(Locks::profiler_lock_); + // The only instance of the saver. static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_); // Profile saver thread. static pthread_t profiler_pthread_ GUARDED_BY(Locks::profiler_lock_); - const std::string output_filename_; jit::JitCodeCache* jit_code_cache_; - const std::set<const std::string> tracked_dex_base_locations_; + SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_ + GUARDED_BY(Locks::profiler_lock_); uint64_t code_cache_last_update_time_ns_; bool shutting_down_ GUARDED_BY(Locks::profiler_lock_); diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 3571edb277..18c52e47b6 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -583,6 +583,10 @@ void MemMap::MadviseDontNeedAndZero() { } } +bool MemMap::Sync() { + return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0; +} + bool MemMap::Protect(int prot) { if (base_begin_ == nullptr && base_size_ == 0) { prot_ = prot; diff --git a/runtime/mem_map.h b/runtime/mem_map.h index ed213659e3..ebd550a552 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -126,6 +126,8 @@ class MemMap { return name_; } + bool Sync(); + bool Protect(int prot); void MadviseDontNeedAndZero(); diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc index edcbcf2dca..81c855e736 100644 --- a/runtime/mem_map_test.cc +++ b/runtime/mem_map_test.cc @@ -251,6 +251,10 @@ TEST_F(MemMapTest, RemapAtEnd32bit) { #endif TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { + // Some MIPS32 hardware (namely the Creator Ci20 development board) + // cannot allocate in the 2GB-4GB region. + TEST_DISABLED_FOR_MIPS(); + CommonInit(); // This test may not work under valgrind. if (RUNNING_ON_MEMORY_TOOL == 0) { @@ -271,8 +275,8 @@ TEST_F(MemMapTest, MapAnonymousExactAddr32bitHighAddr) { break; } } - ASSERT_GE(reinterpret_cast<uintptr_t>(map->End()), 2u * GB); ASSERT_TRUE(map.get() != nullptr) << error_msg; + ASSERT_GE(reinterpret_cast<uintptr_t>(map->End()), 2u * GB); ASSERT_TRUE(error_msg.empty()); ASSERT_EQ(BaseBegin(map.get()), reinterpret_cast<void*>(start_addr)); } diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 53118e07e1..d5783c04b5 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -47,11 +47,15 @@ inline uint32_t Class::GetObjectSize() { return GetField32(ObjectSizeOffset()); } +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline Class* Class::GetSuperClass() { // Can only get super class for loaded classes (hack for when runtime is // initializing) - DCHECK(IsLoaded() || IsErroneous() || !Runtime::Current()->IsStarted()) << IsLoaded(); - return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(Class, super_class_)); + DCHECK(IsLoaded<kVerifyFlags>() || + IsErroneous<kVerifyFlags>() || + !Runtime::Current()->IsStarted()) << IsLoaded(); + return GetFieldObject<Class, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(Class, super_class_)); } inline ClassLoader* Class::GetClassLoader() { @@ -153,7 +157,7 @@ inline ArraySlice<ArtMethod> Class::GetCopiedMethodsSliceUnchecked(size_t pointe inline LengthPrefixedArray<ArtMethod>* Class::GetMethodsPtr() { return reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( - GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_))); + static_cast<uintptr_t>(GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_)))); } template<VerifyObjectFlags kVerifyFlags> @@ -204,7 +208,7 @@ inline void Class::SetMethodsPtrUnchecked(LengthPrefixedArray<ArtMethod>* new_me inline void Class::SetMethodsPtrInternal(LengthPrefixedArray<ArtMethod>* new_methods) { SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, methods_), - reinterpret_cast<uint64_t>(new_methods)); + static_cast<uint64_t>(reinterpret_cast<uintptr_t>(new_methods))); } template<VerifyObjectFlags kVerifyFlags> @@ -226,9 +230,12 @@ inline ArtMethod* Class::GetVirtualMethodUnchecked(size_t i, size_t pointer_size return &GetVirtualMethodsSliceUnchecked(pointer_size).At(i); } +template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline PointerArray* Class::GetVTable() { - DCHECK(IsResolved() || IsErroneous()); - return GetFieldObject<PointerArray>(OFFSET_OF_OBJECT_MEMBER(Class, vtable_)); + DCHECK(IsResolved<kVerifyFlags>() || IsErroneous<kVerifyFlags>()); + return GetFieldObject<PointerArray, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(Class, vtable_)); } inline PointerArray* Class::GetVTableDuringLinking() { @@ -499,8 +506,11 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho return FindVirtualMethodForVirtual(method, pointer_size); } +template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline IfTable* Class::GetIfTable() { - return GetFieldObject<IfTable>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_)); + return GetFieldObject<IfTable, kVerifyFlags, kReadBarrierOption>( + OFFSET_OF_OBJECT_MEMBER(Class, iftable_)); } inline int32_t Class::GetIfTableCount() { @@ -516,7 +526,7 @@ inline void Class::SetIfTable(IfTable* new_iftable) { } inline LengthPrefixedArray<ArtField>* Class::GetIFieldsPtr() { - DCHECK(IsLoaded() || IsErroneous()); + DCHECK(IsLoaded() || IsErroneous()) << GetStatus(); return GetFieldPtr<LengthPrefixedArray<ArtField>*>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_)); } @@ -747,9 +757,12 @@ inline uint32_t Class::ComputeClassSize(bool has_embedded_tables, return size; } -template <typename Visitor> +template <bool kVisitNativeRoots, + VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption, + typename Visitor> inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) { - VisitInstanceFieldsReferences(klass, visitor); + VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor); // Right after a class is allocated, but not yet loaded // (kStatusNotReady, see ClassLinker::LoadClass()), GC may find it // and scan it. IsTemp() may call Class::GetAccessFlags() but may @@ -757,14 +770,16 @@ inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) // status is kStatusNotReady. To avoid it, rely on IsResolved() // only. This is fine because a temp class never goes into the // kStatusResolved state. - if (IsResolved()) { + if (IsResolved<kVerifyFlags>()) { // Temp classes don't ever populate imt/vtable or static fields and they are not even // allocated with the right size for those. Also, unresolved classes don't have fields // linked yet. - VisitStaticFieldsReferences(this, visitor); + VisitStaticFieldsReferences<kVerifyFlags, kReadBarrierOption>(this, visitor); + } + if (kVisitNativeRoots) { + // Since this class is reachable, we must also visit the associated roots when we scan it. + VisitNativeRoots(visitor, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); } - // Since this class is reachable, we must also visit the associated roots when we scan it. - VisitNativeRoots(visitor, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); } template<ReadBarrierOption kReadBarrierOption> diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 3a06b8240d..3017820ea1 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -494,10 +494,11 @@ class MANAGED Class FINAL : public Object { (IsAbstract() && IsArrayClass()); } - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsObjectArrayClass() SHARED_REQUIRES(Locks::mutator_lock_) { - return GetComponentType<kVerifyFlags>() != nullptr && - !GetComponentType<kVerifyFlags>()->IsPrimitive(); + mirror::Class* const component_type = GetComponentType<kVerifyFlags, kReadBarrierOption>(); + return component_type != nullptr && !component_type->IsPrimitive(); } template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> @@ -656,6 +657,8 @@ class MANAGED Class FINAL : public Object { // to themselves. Classes for primitive types may not assign to each other. ALWAYS_INLINE bool IsAssignableFrom(Class* src) SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE Class* GetSuperClass() SHARED_REQUIRES(Locks::mutator_lock_); // Get first common super class. It will never return null. @@ -706,6 +709,10 @@ class MANAGED Class FINAL : public Object { ALWAYS_INLINE LengthPrefixedArray<ArtMethod>* GetMethodsPtr() SHARED_REQUIRES(Locks::mutator_lock_); + static MemberOffset MethodsOffset() { + return MemberOffset(OFFSETOF_MEMBER(Class, methods_)); + } + ALWAYS_INLINE IterationRange<StrideIterator<ArtMethod>> GetMethods(size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); @@ -787,6 +794,8 @@ class MANAGED Class FINAL : public Object { ArtMethod* GetVirtualMethodDuringLinking(size_t i, size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE PointerArray* GetVTable() SHARED_REQUIRES(Locks::mutator_lock_); ALWAYS_INLINE PointerArray* GetVTableDuringLinking() SHARED_REQUIRES(Locks::mutator_lock_); @@ -937,6 +946,8 @@ class MANAGED Class FINAL : public Object { ALWAYS_INLINE int32_t GetIfTableCount() SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE IfTable* GetIfTable() SHARED_REQUIRES(Locks::mutator_lock_); ALWAYS_INLINE void SetIfTable(IfTable* new_iftable) SHARED_REQUIRES(Locks::mutator_lock_); @@ -1222,7 +1233,8 @@ class MANAGED Class FINAL : public Object { // Fix up all of the native pointers in the class by running them through the visitor. Only sets // the corresponding entry in dest if visitor(obj) != obj to prevent dirty memory. Dest should be - // initialized to a copy of *this to prevent issues. + // initialized to a copy of *this to prevent issues. Does not visit the ArtMethod and ArtField + // roots. template <typename Visitor> void FixupNativePointers(mirror::Class* dest, size_t pointer_size, const Visitor& visitor) SHARED_REQUIRES(Locks::mutator_lock_); @@ -1273,7 +1285,10 @@ class MANAGED Class FINAL : public Object { static MemberOffset EmbeddedImTableOffset(size_t pointer_size); static MemberOffset EmbeddedVTableOffset(size_t pointer_size); - template <typename Visitor> + template <bool kVisitNativeRoots, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier, + typename Visitor> void VisitReferences(mirror::Class* klass, const Visitor& visitor) SHARED_REQUIRES(Locks::mutator_lock_); @@ -1357,6 +1372,8 @@ class MANAGED Class FINAL : public Object { // The slice methods_ [copied_methods_offset_, |methods_|) are the methods that are copied from // interfaces such as miranda or default methods. These are copied for resolution purposes as this // class is where they are (logically) declared as far as the virtual dispatch is concerned. + // + // Note that this field is used by the native debugger as the unique identifier for the type. uint64_t methods_; // Static fields length-prefixed array. diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h index e22ddd7e90..84fa80f023 100644 --- a/runtime/mirror/class_loader-inl.h +++ b/runtime/mirror/class_loader-inl.h @@ -25,15 +25,20 @@ namespace art { namespace mirror { -template <VerifyObjectFlags kVerifyFlags, typename Visitor> +template <bool kVisitClasses, + VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption, + typename Visitor> inline void ClassLoader::VisitReferences(mirror::Class* klass, const Visitor& visitor) { // Visit instance fields first. - VisitInstanceFieldsReferences(klass, visitor); - // Visit classes loaded after. - ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - ClassTable* const class_table = GetClassTable(); - if (class_table != nullptr) { - class_table->VisitRoots(visitor); + VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor); + if (kVisitClasses) { + // Visit classes loaded after. + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ClassTable* const class_table = GetClassTable(); + if (class_table != nullptr) { + class_table->VisitRoots(visitor); + } } } diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h index c2a65d62e2..1957e13e36 100644 --- a/runtime/mirror/class_loader.h +++ b/runtime/mirror/class_loader.h @@ -63,7 +63,10 @@ class MANAGED ClassLoader : public Object { private: // Visit instance fields of the class loader as well as its associated classes. // Null class loader is handled by ClassLinker::VisitClassRoots. - template <VerifyObjectFlags kVerifyFlags, typename Visitor> + template <bool kVisitClasses, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier, + typename Visitor> void VisitReferences(mirror::Class* klass, const Visitor& visitor) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::classlinker_classes_lock_); diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 975af61ca8..2ecc9fb1a8 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -122,18 +122,23 @@ inline void DexCache::SetElementPtrSize(PtrType* ptr_array, } } -template <VerifyObjectFlags kVerifyFlags, typename Visitor> +template <bool kVisitNativeRoots, + VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption, + typename Visitor> inline void DexCache::VisitReferences(mirror::Class* klass, const Visitor& visitor) { // Visit instance fields first. - VisitInstanceFieldsReferences(klass, visitor); + VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor); // Visit arrays after. - GcRoot<mirror::String>* strings = GetStrings(); - for (size_t i = 0, num_strings = NumStrings(); i != num_strings; ++i) { - visitor.VisitRootIfNonNull(strings[i].AddressWithoutBarrier()); - } - GcRoot<mirror::Class>* resolved_types = GetResolvedTypes(); - for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) { - visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier()); + if (kVisitNativeRoots) { + GcRoot<mirror::String>* strings = GetStrings(); + for (size_t i = 0, num_strings = NumStrings(); i != num_strings; ++i) { + visitor.VisitRootIfNonNull(strings[i].AddressWithoutBarrier()); + } + GcRoot<mirror::Class>* resolved_types = GetResolvedTypes(); + for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) { + visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier()); + } } } diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 349a319992..692c6cb0c1 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -50,11 +50,11 @@ void DexCache::Init(const DexFile* dex_file, CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr); SetDexFile(dex_file); - SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location); - SetField64<false>(StringsOffset(), reinterpret_cast<uintptr_t>(strings)); - SetField64<false>(ResolvedTypesOffset(), reinterpret_cast<uintptr_t>(resolved_types)); - SetField64<false>(ResolvedMethodsOffset(), reinterpret_cast<uintptr_t>(resolved_methods)); - SetField64<false>(ResolvedFieldsOffset(), reinterpret_cast<uintptr_t>(resolved_fields)); + SetLocation(location); + SetStrings(strings); + SetResolvedTypes(resolved_types); + SetResolvedMethods(resolved_methods); + SetResolvedFields(resolved_fields); SetField32<false>(NumStringsOffset(), num_strings); SetField32<false>(NumResolvedTypesOffset(), num_resolved_types); SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods); @@ -79,5 +79,9 @@ void DexCache::Fixup(ArtMethod* trampoline, size_t pointer_size) { } } +void DexCache::SetLocation(mirror::String* location) { + SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 32eb59540d..0002076ce0 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -137,18 +137,40 @@ class MANAGED DexCache FINAL : public Object { return GetFieldPtr<GcRoot<String>*>(StringsOffset()); } + void SetStrings(GcRoot<String>* strings) ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) { + SetFieldPtr<false>(StringsOffset(), strings); + } + GcRoot<Class>* GetResolvedTypes() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) { return GetFieldPtr<GcRoot<Class>*>(ResolvedTypesOffset()); } + void SetResolvedTypes(GcRoot<Class>* resolved_types) + ALWAYS_INLINE + SHARED_REQUIRES(Locks::mutator_lock_) { + SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types); + } + ArtMethod** GetResolvedMethods() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) { return GetFieldPtr<ArtMethod**>(ResolvedMethodsOffset()); } + void SetResolvedMethods(ArtMethod** resolved_methods) + ALWAYS_INLINE + SHARED_REQUIRES(Locks::mutator_lock_) { + SetFieldPtr<false>(ResolvedMethodsOffset(), resolved_methods); + } + ArtField** GetResolvedFields() ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) { return GetFieldPtr<ArtField**>(ResolvedFieldsOffset()); } + void SetResolvedFields(ArtField** resolved_fields) + ALWAYS_INLINE + SHARED_REQUIRES(Locks::mutator_lock_) { + SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields); + } + size_t NumStrings() SHARED_REQUIRES(Locks::mutator_lock_) { return GetField32(NumStringsOffset()); } @@ -169,11 +191,12 @@ class MANAGED DexCache FINAL : public Object { return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_)); } - void SetDexFile(const DexFile* dex_file) SHARED_REQUIRES(Locks::mutator_lock_) - ALWAYS_INLINE { - return SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file); + void SetDexFile(const DexFile* dex_file) SHARED_REQUIRES(Locks::mutator_lock_) { + SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file); } + void SetLocation(mirror::String* location) SHARED_REQUIRES(Locks::mutator_lock_); + // NOTE: Get/SetElementPtrSize() are intended for working with ArtMethod** and ArtField** // provided by GetResolvedMethods/Fields() and ArtMethod::GetDexCacheResolvedMethods(), // so they need to be public. @@ -186,7 +209,10 @@ class MANAGED DexCache FINAL : public Object { private: // Visit instance fields of the dex cache as well as its associated arrays. - template <VerifyObjectFlags kVerifyFlags, typename Visitor> + template <bool kVisitNativeRoots, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier, + typename Visitor> void VisitReferences(mirror::Class* klass, const Visitor& visitor) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h index b21ecdf6e8..605deac608 100644 --- a/runtime/mirror/iftable.h +++ b/runtime/mirror/iftable.h @@ -34,8 +34,11 @@ class MANAGED IfTable FINAL : public ObjectArray<Object> { ALWAYS_INLINE void SetInterface(int32_t i, Class* interface) SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> PointerArray* GetMethodArray(int32_t i) SHARED_REQUIRES(Locks::mutator_lock_) { - auto* method_array = down_cast<PointerArray*>(Get((i * kMax) + kMethodArray)); + auto* method_array = down_cast<PointerArray*>(Get<kVerifyFlags, kReadBarrierOption>( + (i * kMax) + kMethodArray)); DCHECK(method_array != nullptr); return method_array; } diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index 460342807a..760de9ab40 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -1031,7 +1031,10 @@ inline bool Object::CasFieldStrongRelaxedObjectWithoutWriteBarrier( return success; } -template<bool kIsStatic, typename Visitor> +template<bool kIsStatic, + VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption, + typename Visitor> inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) { if (!kIsStatic && (ref_offsets != mirror::Class::kClassWalkSuper)) { // Instance fields and not the slow-path. @@ -1047,9 +1050,12 @@ inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& v // There is no reference offset bitmap. In the non-static case, walk up the class // inheritance hierarchy and find reference offsets the hard way. In the static case, just // consider this class. - for (mirror::Class* klass = kIsStatic ? AsClass() : GetClass(); klass != nullptr; - klass = kIsStatic ? nullptr : klass->GetSuperClass()) { - size_t num_reference_fields = + for (mirror::Class* klass = kIsStatic + ? AsClass<kVerifyFlags, kReadBarrierOption>() + : GetClass<kVerifyFlags, kReadBarrierOption>(); + klass != nullptr; + klass = kIsStatic ? nullptr : klass->GetSuperClass<kVerifyFlags, kReadBarrierOption>()) { + const size_t num_reference_fields = kIsStatic ? klass->NumReferenceStaticFields() : klass->NumReferenceInstanceFields(); if (num_reference_fields == 0u) { continue; @@ -1072,49 +1078,54 @@ inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& v } } -template<typename Visitor> +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, typename Visitor> inline void Object::VisitInstanceFieldsReferences(mirror::Class* klass, const Visitor& visitor) { - VisitFieldsReferences<false>(klass->GetReferenceInstanceOffsets<kVerifyNone>(), visitor); + VisitFieldsReferences<false, kVerifyFlags, kReadBarrierOption>( + klass->GetReferenceInstanceOffsets<kVerifyFlags>(), visitor); } -template<typename Visitor> +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, typename Visitor> inline void Object::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) { DCHECK(!klass->IsTemp()); - klass->VisitFieldsReferences<true>(0, visitor); + klass->VisitFieldsReferences<true, kVerifyFlags, kReadBarrierOption>(0, visitor); } -template<VerifyObjectFlags kVerifyFlags> +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline bool Object::IsClassLoader() { - return GetClass<kVerifyFlags>()->IsClassLoaderClass(); + return GetClass<kVerifyFlags, kReadBarrierOption>()->IsClassLoaderClass(); } -template<VerifyObjectFlags kVerifyFlags> +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline mirror::ClassLoader* Object::AsClassLoader() { - DCHECK(IsClassLoader<kVerifyFlags>()); + DCHECK((IsClassLoader<kVerifyFlags, kReadBarrierOption>())); return down_cast<mirror::ClassLoader*>(this); } -template<VerifyObjectFlags kVerifyFlags> +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline bool Object::IsDexCache() { - return GetClass<kVerifyFlags>()->IsDexCacheClass(); + return GetClass<kVerifyFlags, kReadBarrierOption>()->IsDexCacheClass(); } -template<VerifyObjectFlags kVerifyFlags> +template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline mirror::DexCache* Object::AsDexCache() { - DCHECK(IsDexCache<kVerifyFlags>()); + DCHECK((IsDexCache<kVerifyFlags, kReadBarrierOption>())); return down_cast<mirror::DexCache*>(this); } -template <VerifyObjectFlags kVerifyFlags, typename Visitor, typename JavaLangRefVisitor> +template <bool kVisitNativeRoots, + VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption, + typename Visitor, + typename JavaLangRefVisitor> inline void Object::VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor) { - mirror::Class* klass = GetClass<kVerifyFlags>(); + mirror::Class* klass = GetClass<kVerifyFlags, kReadBarrierOption>(); visitor(this, ClassOffset(), false); const uint32_t class_flags = klass->GetClassFlags<kVerifyNone>(); if (LIKELY(class_flags == kClassFlagNormal)) { DCHECK(!klass->IsVariableSize()); VisitInstanceFieldsReferences(klass, visitor); - DCHECK(!klass->IsClassClass()); + DCHECK((!klass->IsClassClass<kVerifyFlags, kReadBarrierOption>())); DCHECK(!klass->IsStringClass()); DCHECK(!klass->IsClassLoaderClass()); DCHECK(!klass->IsArrayClass()); @@ -1123,23 +1134,29 @@ inline void Object::VisitReferences(const Visitor& visitor, DCHECK(!klass->IsStringClass()); if (class_flags == kClassFlagClass) { DCHECK(klass->IsClassClass()); - AsClass<kVerifyNone>()->VisitReferences(klass, visitor); + AsClass<kVerifyNone>()->VisitReferences<kVisitNativeRoots, + kVerifyFlags, + kReadBarrierOption>(klass, visitor); } else if (class_flags == kClassFlagObjectArray) { - DCHECK(klass->IsObjectArrayClass()); + DCHECK((klass->IsObjectArrayClass<kVerifyFlags, kReadBarrierOption>())); AsObjectArray<mirror::Object, kVerifyNone>()->VisitReferences(visitor); } else if ((class_flags & kClassFlagReference) != 0) { VisitInstanceFieldsReferences(klass, visitor); ref_visitor(klass, AsReference()); } else if (class_flags == kClassFlagDexCache) { - mirror::DexCache* const dex_cache = AsDexCache<kVerifyFlags>(); - dex_cache->VisitReferences<kVerifyFlags>(klass, visitor); + mirror::DexCache* const dex_cache = AsDexCache<kVerifyFlags, kReadBarrierOption>(); + dex_cache->VisitReferences<kVisitNativeRoots, + kVerifyFlags, + kReadBarrierOption>(klass, visitor); } else { - mirror::ClassLoader* const class_loader = AsClassLoader<kVerifyFlags>(); - class_loader->VisitReferences<kVerifyFlags>(klass, visitor); + mirror::ClassLoader* const class_loader = AsClassLoader<kVerifyFlags, kReadBarrierOption>(); + class_loader->VisitReferences<kVisitNativeRoots, + kVerifyFlags, + kReadBarrierOption>(klass, visitor); } } else if (kIsDebugBuild) { - CHECK(!klass->IsClassClass()); - CHECK(!klass->IsObjectArrayClass()); + CHECK((!klass->IsClassClass<kVerifyFlags, kReadBarrierOption>())); + CHECK((!klass->IsObjectArrayClass<kVerifyFlags, kReadBarrierOption>())); // String still has instance fields for reflection purposes but these don't exist in // actual string instances. if (!klass->IsStringClass()) { @@ -1147,7 +1164,7 @@ inline void Object::VisitReferences(const Visitor& visitor, mirror::Class* super_class = klass; do { total_reference_instance_fields += super_class->NumReferenceInstanceFields(); - super_class = super_class->GetSuperClass(); + super_class = super_class->GetSuperClass<kVerifyFlags, kReadBarrierOption>(); } while (super_class != nullptr); // The only reference field should be the object's class. This field is handled at the // beginning of the function. diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 71e704e704..d635002d12 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -164,14 +164,18 @@ class MANAGED LOCKABLE Object { template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> ObjectArray<T>* AsObjectArray() SHARED_REQUIRES(Locks::mutator_lock_); - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_); - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ClassLoader* AsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_); - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsDexCache() SHARED_REQUIRES(Locks::mutator_lock_); - template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> DexCache* AsDexCache() SHARED_REQUIRES(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, @@ -456,6 +460,13 @@ class MANAGED LOCKABLE Object { SetFieldPtrWithSize<kTransactionActive, kCheckTransaction, kVerifyFlags>( field_offset, new_value, sizeof(void*)); } + template<bool kTransactionActive, bool kCheckTransaction = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T> + void SetFieldPtr64(MemberOffset field_offset, T new_value) + SHARED_REQUIRES(Locks::mutator_lock_) { + SetFieldPtrWithSize<kTransactionActive, kCheckTransaction, kVerifyFlags>( + field_offset, new_value, 8u); + } template<bool kTransactionActive, bool kCheckTransaction = true, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, typename T> @@ -475,7 +486,9 @@ class MANAGED LOCKABLE Object { } // TODO fix thread safety analysis broken by the use of template. This should be // SHARED_REQUIRES(Locks::mutator_lock_). - template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + template <bool kVisitNativeRoots = true, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor, typename JavaLangRefVisitor = VoidFunctor> void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor) @@ -495,6 +508,11 @@ class MANAGED LOCKABLE Object { SHARED_REQUIRES(Locks::mutator_lock_) { return GetFieldPtrWithSize<T, kVerifyFlags, kIsVolatile>(field_offset, sizeof(void*)); } + template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> + T GetFieldPtr64(MemberOffset field_offset) + SHARED_REQUIRES(Locks::mutator_lock_) { + return GetFieldPtrWithSize<T, kVerifyFlags, kIsVolatile>(field_offset, 8u); + } template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false> ALWAYS_INLINE T GetFieldPtrWithSize(MemberOffset field_offset, size_t pointer_size) @@ -511,13 +529,20 @@ class MANAGED LOCKABLE Object { } // TODO: Fixme when anotatalysis works with visitors. - template<bool kIsStatic, typename Visitor> + template<bool kIsStatic, + VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier, + typename Visitor> void VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) HOT_ATTR NO_THREAD_SAFETY_ANALYSIS; - template<typename Visitor> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier, + typename Visitor> void VisitInstanceFieldsReferences(mirror::Class* klass, const Visitor& visitor) HOT_ATTR SHARED_REQUIRES(Locks::mutator_lock_); - template<typename Visitor> + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier, + typename Visitor> void VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor) HOT_ATTR SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index 5337760fb8..6f9d64297a 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -55,13 +55,13 @@ inline ObjectArray<T>* ObjectArray<T>::Alloc(Thread* self, Class* object_array_c Runtime::Current()->GetHeap()->GetCurrentAllocator()); } -template<class T> +template<class T> template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline T* ObjectArray<T>::Get(int32_t i) { if (!CheckIsValidIndex(i)) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; } - return GetFieldObject<T>(OffsetOfElement(i)); + return GetFieldObject<T, kVerifyFlags, kReadBarrierOption>(OffsetOfElement(i)); } template<class T> template<VerifyObjectFlags kVerifyFlags> diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h index b45cafd2a3..1b1295cedb 100644 --- a/runtime/mirror/object_array.h +++ b/runtime/mirror/object_array.h @@ -37,7 +37,9 @@ class MANAGED ObjectArray: public Array { static ObjectArray<T>* Alloc(Thread* self, Class* object_array_class, int32_t length) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); - T* Get(int32_t i) ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + ALWAYS_INLINE T* Get(int32_t i) SHARED_REQUIRES(Locks::mutator_lock_); // Returns true if the object can be stored into the array. If not, throws // an ArrayStoreException and returns false. diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index da6cf1f198..6643ac2231 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -154,10 +154,8 @@ static jobject DexFile_openDexFileNative(JNIEnv* env, jstring javaSourceName, jstring javaOutputName, jint flags ATTRIBUTE_UNUSED, - // class_loader will be used for app images. - jobject class_loader ATTRIBUTE_UNUSED, - // dex_elements will be used for app images. - jobject dex_elements ATTRIBUTE_UNUSED) { + jobject class_loader, + jobjectArray dex_elements) { ScopedUtfChars sourceName(env, javaSourceName); if (sourceName.c_str() == nullptr) { return 0; @@ -174,6 +172,8 @@ static jobject DexFile_openDexFileNative(JNIEnv* env, dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), + class_loader, + dex_elements, /*out*/ &oat_file, /*out*/ &error_msgs); diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 8febb62a5f..8f108faa1f 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -314,32 +314,33 @@ static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { size_t largeObjectsSize = 0; size_t largeObjectsUsed = 0; gc::Heap* heap = Runtime::Current()->GetHeap(); - for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { - if (space->IsImageSpace()) { - // Currently don't include the image space. - } else if (space->IsZygoteSpace()) { - gc::space::ZygoteSpace* zygote_space = space->AsZygoteSpace(); - zygoteSize += zygote_space->Size(); - zygoteUsed += zygote_space->GetBytesAllocated(); - } else if (space->IsMallocSpace()) { - // This is a malloc space. - gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); - allocSize += malloc_space->GetFootprint(); - allocUsed += malloc_space->GetBytesAllocated(); - } else if (space->IsBumpPointerSpace()) { - ScopedObjectAccess soa(env); - gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); - allocSize += bump_pointer_space->Size(); - allocUsed += bump_pointer_space->GetBytesAllocated(); + { + ScopedObjectAccess soa(env); + for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { + if (space->IsImageSpace()) { + // Currently don't include the image space. + } else if (space->IsZygoteSpace()) { + gc::space::ZygoteSpace* zygote_space = space->AsZygoteSpace(); + zygoteSize += zygote_space->Size(); + zygoteUsed += zygote_space->GetBytesAllocated(); + } else if (space->IsMallocSpace()) { + // This is a malloc space. + gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); + allocSize += malloc_space->GetFootprint(); + allocUsed += malloc_space->GetBytesAllocated(); + } else if (space->IsBumpPointerSpace()) { + gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); + allocSize += bump_pointer_space->Size(); + allocUsed += bump_pointer_space->GetBytesAllocated(); + } } - } - for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) { - if (space->IsLargeObjectSpace()) { - largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated(); - largeObjectsUsed += largeObjectsSize; + for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) { + if (space->IsLargeObjectSpace()) { + largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated(); + largeObjectsUsed += largeObjectsSize; + } } } - size_t allocFree = allocSize - allocUsed; size_t zygoteFree = zygoteSize - zygoteUsed; size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed; diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index 67d825efaa..a7881ac52f 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -65,6 +65,7 @@ static void EnableDebugFeatures(uint32_t debug_flags) { DEBUG_ENABLE_SAFEMODE = 1 << 3, DEBUG_ENABLE_JNI_LOGGING = 1 << 4, DEBUG_GENERATE_DEBUG_INFO = 1 << 5, + DEBUG_ALWAYS_JIT = 1 << 6, }; Runtime* const runtime = Runtime::Current(); @@ -109,6 +110,13 @@ static void EnableDebugFeatures(uint32_t debug_flags) { // This is for backwards compatibility with Dalvik. debug_flags &= ~DEBUG_ENABLE_ASSERT; + if ((debug_flags & DEBUG_ALWAYS_JIT) != 0) { + jit::JitOptions* jit_options = runtime->GetJITOptions(); + CHECK(jit_options != nullptr); + jit_options->SetJitAtFirstUse(); + debug_flags &= ~DEBUG_ALWAYS_JIT; + } + if (debug_flags != 0) { LOG(ERROR) << StringPrintf("Unknown bits set in debug_flags: %#x", debug_flags); } diff --git a/runtime/oat.h b/runtime/oat.h index 13fd6a47e5..989e3f9d89 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -31,7 +31,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '7', '4', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '7', '5', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index d2f563be7a..82b39331dd 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -46,6 +46,7 @@ #include "oat_file_manager.h" #include "os.h" #include "runtime.h" +#include "type_lookup_table.h" #include "utils.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "vmap_table.h" @@ -266,16 +267,15 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { i); return false; } - - const char* dex_file_location_data = reinterpret_cast<const char*>(oat); - oat += dex_file_location_size; - if (UNLIKELY(oat > End())) { + if (UNLIKELY(static_cast<size_t>(End() - oat) < dex_file_location_size)) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu with truncated dex file " "location", GetLocation().c_str(), i); return false; } + const char* dex_file_location_data = reinterpret_cast<const char*>(oat); + oat += dex_file_location_size; std::string dex_file_location = ResolveRelativeEncodedDexLocation( abs_dex_location, @@ -318,6 +318,17 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { Size()); return false; } + if (UNLIKELY(Size() - dex_file_offset < sizeof(DexFile::Header))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " + "offset %u of %zu but the size of dex file header is %zu", + GetLocation().c_str(), + i, + dex_file_location.c_str(), + dex_file_offset, + Size(), + sizeof(DexFile::Header)); + return false; + } const uint8_t* dex_file_pointer = Begin() + dex_file_offset; if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) { @@ -339,34 +350,75 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { return false; } const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer); + if (Size() - dex_file_offset < header->file_size_) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with dex file " + "offset %u and size %u truncated at %zu", + GetLocation().c_str(), + i, + dex_file_location.c_str(), + dex_file_offset, + header->file_size_, + Size()); + return false; + } - if (UNLIKELY(oat > End())) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated " - "lookup table offset", GetLocation().c_str(), i, + uint32_t class_offsets_offset; + if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &class_offsets_offset))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' truncated " + "after class offsets offset", + GetLocation().c_str(), + i, dex_file_location.c_str()); return false; } - uint32_t lookup_table_offset = *reinterpret_cast<const uint32_t*>(oat); - oat += sizeof(lookup_table_offset); - if (Begin() + lookup_table_offset > End()) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated " - "lookup table", GetLocation().c_str(), i, + if (UNLIKELY(class_offsets_offset > Size()) || + UNLIKELY((Size() - class_offsets_offset) / sizeof(uint32_t) < header->class_defs_size_)) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with truncated " + "class offsets, offset %u of %zu, class defs %u", + GetLocation().c_str(), + i, + dex_file_location.c_str(), + class_offsets_offset, + Size(), + header->class_defs_size_); + return false; + } + if (UNLIKELY(!IsAligned<alignof(uint32_t)>(class_offsets_offset))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with unaligned " + "class offsets, offset %u", + GetLocation().c_str(), + i, + dex_file_location.c_str(), + class_offsets_offset); + return false; + } + const uint32_t* class_offsets_pointer = + reinterpret_cast<const uint32_t*>(Begin() + class_offsets_offset); + + uint32_t lookup_table_offset; + if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &lookup_table_offset))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " + "after lookup table offset", + GetLocation().c_str(), + i, dex_file_location.c_str()); return false; } const uint8_t* lookup_table_data = lookup_table_offset != 0u ? Begin() + lookup_table_offset : nullptr; - - const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat); - - oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_); - if (UNLIKELY(oat > End())) { + if (lookup_table_offset != 0u && + (UNLIKELY(lookup_table_offset > Size()) || + UNLIKELY(Size() - lookup_table_offset < + TypeLookupTable::RawDataLength(header->class_defs_size_)))) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with truncated " - "method offsets", + "type lookup table, offset %u of %zu, class defs %u", GetLocation().c_str(), i, - dex_file_location.c_str()); + dex_file_location.c_str(), + lookup_table_offset, + Size(), + header->class_defs_size_); return false; } @@ -398,7 +450,7 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_checksum, dex_file_pointer, lookup_table_data, - methods_offsets_pointer, + class_offsets_pointer, current_dex_cache_arrays); oat_dex_files_storage_.push_back(oat_dex_file); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index d6b08684b9..a8f84a2bfa 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -38,6 +38,7 @@ #include "os.h" #include "profiler.h" #include "runtime.h" +#include "scoped_thread_state_change.h" #include "ScopedFd.h" #include "utils.h" @@ -326,6 +327,17 @@ bool OatFileAssistant::OdexFileIsUpToDate() { return cached_odex_file_is_up_to_date_; } +std::string OatFileAssistant::ArtFileName(const OatFile* oat_file) const { + const std::string oat_file_location = oat_file->GetLocation(); + // Replace extension with .art + const size_t last_ext = oat_file_location.find_last_of('.'); + if (last_ext == std::string::npos) { + LOG(ERROR) << "No extension in oat file " << oat_file_location; + return std::string(); + } + return oat_file_location.substr(0, last_ext) + ".art"; +} + const std::string* OatFileAssistant::OatFileName() { if (!cached_oat_file_name_attempted_) { cached_oat_file_name_attempted_ = true; @@ -1003,5 +1015,22 @@ ProfileFile* OatFileAssistant::GetOldProfile() { return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr; } +gc::space::ImageSpace* OatFileAssistant::OpenImageSpace(const OatFile* oat_file) { + DCHECK(oat_file != nullptr); + std::string art_file = ArtFileName(oat_file); + if (art_file.empty()) { + return nullptr; + } + std::string error_msg; + ScopedObjectAccess soa(Thread::Current()); + gc::space::ImageSpace* ret = gc::space::ImageSpace::CreateFromAppImage(art_file.c_str(), + oat_file, + &error_msg); + if (ret == nullptr && (VLOG_IS_ON(image) || OS::FileExists(art_file.c_str()))) { + LOG(INFO) << "Failed to open app image " << art_file.c_str() << " " << error_msg; + } + return ret; +} + } // namespace art diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index f781532b14..7b45bca946 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -30,6 +30,12 @@ namespace art { +namespace gc { +namespace space { +class ImageSpace; +} // namespace space +} // namespace gc + // Class for assisting with oat file management. // // This class collects common utilities for determining the status of an oat @@ -163,6 +169,9 @@ class OatFileAssistant { // the OatFileAssistant object. std::unique_ptr<OatFile> GetBestOatFile(); + // Open and returns an image space associated with the oat file. + gc::space::ImageSpace* OpenImageSpace(const OatFile* oat_file); + // Loads the dex files in the given oat file for the given dex location. // The oat file should be up to date for the given dex location. // This loads multiple dex files in the case of multidex. @@ -214,6 +223,9 @@ class OatFileAssistant { bool OatFileNeedsRelocation(); bool OatFileIsUpToDate(); + // Return image file name. Does not cache since it relies on the oat file. + std::string ArtFileName(const OatFile* oat_file) const; + // These methods return the status for a given opened oat file with respect // to the dex location. OatStatus GivenOatFileStatus(const OatFile& file); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index f994f0c99d..25dcbe4c63 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -996,6 +996,8 @@ class RaceGenerateTask : public Task { dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat( dex_location_.c_str(), oat_location_.c_str(), + /*class_loader*/nullptr, + /*dex_elements*/nullptr, &oat_file, &error_msgs); CHECK(!dex_files.empty()) << Join(error_msgs, '\n'); diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 36a967f4ab..de90f0aa04 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -22,10 +22,15 @@ #include "base/logging.h" #include "base/stl_util.h" +#include "class_linker.h" #include "dex_file-inl.h" #include "gc/space/image_space.h" +#include "handle_scope-inl.h" +#include "mirror/class_loader.h" #include "oat_file_assistant.h" +#include "scoped_thread_state_change.h" #include "thread-inl.h" +#include "thread_list.h" namespace art { @@ -34,6 +39,9 @@ namespace art { // normal builds. static constexpr bool kDuplicateClassesCheck = kIsDebugBuild; +// If true, then we attempt to load the application image if it exists. +static constexpr bool kEnableAppImage = true; + const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) { WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); DCHECK(oat_file != nullptr); @@ -284,6 +292,8 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( const char* dex_location, const char* oat_location, + jobject class_loader, + jobjectArray dex_elements, const OatFile** out_oat_file, std::vector<std::string>* error_msgs) { CHECK(dex_location != nullptr); @@ -291,12 +301,13 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // Verify we aren't holding the mutator lock, which could starve GC if we // have to generate or relocate an oat file. - Locks::mutator_lock_->AssertNotHeld(Thread::Current()); - + Thread* const self = Thread::Current(); + Locks::mutator_lock_->AssertNotHeld(self); + Runtime* const runtime = Runtime::Current(); OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA, - !Runtime::Current()->IsAotCompiler()); + !runtime->IsAotCompiler()); // Lock the target oat location to avoid races generating and loading the // oat file. @@ -312,11 +323,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // Update the oat file on disk if we can. This may fail, but that's okay. // Best effort is all that matters here. if (!oat_file_assistant.MakeUpToDate(/*out*/&error_msg)) { - LOG(WARNING) << error_msg; + LOG(INFO) << error_msg; } // Get the oat file on disk. std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release()); + if (oat_file != nullptr) { // Take the file only if it has no collisions, or we must take it because of preopting. bool accept_oat_file = !HasCollisions(oat_file.get(), /*out*/ &error_msg); @@ -351,7 +363,50 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( // Load the dex files from the oat file. if (source_oat_file != nullptr) { - dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location); + bool added_image_space = false; + if (source_oat_file->IsExecutable()) { + std::unique_ptr<gc::space::ImageSpace> image_space( + kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr); + if (image_space != nullptr) { + ScopedObjectAccess soa(self); + StackHandleScope<1> hs(self); + Handle<mirror::ClassLoader> h_loader( + hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader))); + // Can not load app image without class loader. + if (h_loader.Get() != nullptr) { + std::string temp_error_msg; + // Add image space has a race condition since other threads could be reading from the + // spaces array. + { + ScopedThreadSuspension sts(self, kSuspended); + ScopedSuspendAll ssa("Add image space"); + runtime->GetHeap()->AddSpace(image_space.get()); + } + added_image_space = true; + if (!runtime->GetClassLinker()->AddImageSpace(image_space.get(), + h_loader, + dex_elements, + dex_location, + /*out*/&dex_files, + /*out*/&temp_error_msg)) { + LOG(INFO) << "Failed to add image file " << temp_error_msg; + dex_files.clear(); + { + ScopedThreadSuspension sts(self, kSuspended); + ScopedSuspendAll ssa("Remove image space"); + runtime->GetHeap()->RemoveSpace(image_space.get()); + } + added_image_space = false; + // Non-fatal, don't update error_msg. + } + image_space.release(); + } + } + } + if (!added_image_space) { + DCHECK(dex_files.empty()); + dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location); + } if (dex_files.empty()) { error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation()); } diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 4690e4550b..c508c4bf07 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "base/mutex.h" +#include "jni.h" namespace art { @@ -101,6 +102,8 @@ class OatFileManager { std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat( const char* dex_location, const char* oat_location, + jobject class_loader, + jobjectArray dex_elements, /*out*/ const OatFile** out_oat_file, /*out*/ std::vector<std::string>* error_msgs) REQUIRES(!Locks::oat_file_manager_lock_, !Locks::mutator_lock_); diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 2b92303fe2..aa64ee3702 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -309,8 +309,8 @@ bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options, const std::string option(options[i].first); // TODO: support -Djava.class.path if (option == "bootclasspath") { - auto boot_class_path - = reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second); + auto boot_class_path = static_cast<std::vector<std::unique_ptr<const DexFile>>*>( + const_cast<void*>(options[i].second)); if (runtime_options != nullptr) { runtime_options->Set(M::BootClassPathDexList, boot_class_path); @@ -565,13 +565,6 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options, args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize)); } - if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kDefaultMethods) { - LOG(WARNING) << "Default method support has been enabled. The verifier will be less strict " - << "in some cases. All existing invoke opcodes have an unstable updated " - << "specification and are nearly guaranteed to change over time. Do not attempt " - << "to write shipping code against the invoke opcodes with this flag."; - } - if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kLambdas) { LOG(WARNING) << "Experimental lambdas have been enabled. All lambda opcodes have " << "an unstable specification and are nearly guaranteed to change over time. " @@ -698,8 +691,8 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -X[no]image-dex2oat (Whether to create and use a boot image)\n"); UsageMessage(stream, " -Xno-dex-file-fallback " "(Don't fall back to dex files without oat files)\n"); - UsageMessage(stream, " -Xexperimental:{lambdas,default-methods} " - "(Enable new experimental dalvik opcodes and semantics, off by default)\n"); + UsageMessage(stream, " -Xexperimental:lambdas " + "(Enable new and experimental dalvik opcodes and semantics)\n"); UsageMessage(stream, "\n"); UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n"); diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h index 6cea90219e..1bb816bb20 100644 --- a/runtime/quick/inline_method_analyser.h +++ b/runtime/quick/inline_method_analyser.h @@ -37,12 +37,21 @@ class MethodVerifier; enum InlineMethodOpcode : uint16_t { kIntrinsicDoubleCvt, kIntrinsicFloatCvt, + kIntrinsicFloatIsInfinite, + kIntrinsicDoubleIsInfinite, + kIntrinsicFloatIsNaN, + kIntrinsicDoubleIsNaN, kIntrinsicReverseBits, kIntrinsicReverseBytes, + kIntrinsicBitCount, + kIntrinsicCompare, + kIntrinsicHighestOneBit, + kIntrinsicLowestOneBit, kIntrinsicNumberOfLeadingZeros, kIntrinsicNumberOfTrailingZeros, kIntrinsicRotateRight, kIntrinsicRotateLeft, + kIntrinsicSignum, kIntrinsicAbsInt, kIntrinsicAbsLong, kIntrinsicAbsFloat, diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1101acdf61..0c06ca672c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -71,7 +71,7 @@ #include "gc/space/image_space.h" #include "gc/space/space-inl.h" #include "handle_scope-inl.h" -#include "image.h" +#include "image-inl.h" #include "instrumentation.h" #include "intern_table.h" #include "interpreter/interpreter.h" @@ -1097,7 +1097,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { if (GetHeap()->HasBootImageSpace()) { ATRACE_BEGIN("InitFromImage"); std::string error_msg; - bool result = class_linker_->InitFromImage(&error_msg); + bool result = class_linker_->InitFromBootImage(&error_msg); ATRACE_END(); if (!result) { LOG(ERROR) << "Could not initialize from image: " << error_msg; @@ -1131,10 +1131,14 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } std::vector<std::unique_ptr<const DexFile>> boot_class_path; - OpenDexFiles(dex_filenames, - dex_locations, - runtime_options.GetOrDefault(Opt::Image), - &boot_class_path); + if (runtime_options.Exists(Opt::BootClassPathDexList)) { + boot_class_path.swap(*runtime_options.GetOrDefault(Opt::BootClassPathDexList)); + } else { + OpenDexFiles(dex_filenames, + dex_locations, + runtime_options.GetOrDefault(Opt::Image), + &boot_class_path); + } instruction_set_ = runtime_options.GetOrDefault(Opt::ImageInstructionSet); std::string error_msg; if (!class_linker_->InitWithoutImage(std::move(boot_class_path), &error_msg)) { diff --git a/runtime/runtime.h b/runtime/runtime.h index 20acffb697..c8c2ee530d 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -366,7 +366,7 @@ class Runtime { SHARED_REQUIRES(Locks::mutator_lock_); // Returns a special method that calls into a trampoline for runtime method resolution - ArtMethod* GetResolutionMethod() SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* GetResolutionMethod(); bool HasResolutionMethod() const { return resolution_method_ != nullptr; @@ -377,8 +377,8 @@ class Runtime { ArtMethod* CreateResolutionMethod() SHARED_REQUIRES(Locks::mutator_lock_); // Returns a special method that calls into a trampoline for runtime imt conflicts. - ArtMethod* GetImtConflictMethod() SHARED_REQUIRES(Locks::mutator_lock_); - ArtMethod* GetImtUnimplementedMethod() SHARED_REQUIRES(Locks::mutator_lock_); + ArtMethod* GetImtConflictMethod(); + ArtMethod* GetImtUnimplementedMethod(); bool HasImtConflictMethod() const { return imt_conflict_method_ != nullptr; diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc index c54461e60e..e75481c78a 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -13,8 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "runtime_options.h" +#include <memory> + #include "gc/heap.h" #include "monitor.h" #include "runtime.h" diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 5624285b09..308f3bafc2 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -113,12 +113,12 @@ RUNTIME_OPTIONS_KEY (unsigned int, ZygoteMaxFailedBoots, 10) RUNTIME_OPTIONS_KEY (Unit, NoDexFileFallback) RUNTIME_OPTIONS_KEY (std::string, CpuAbiList) RUNTIME_OPTIONS_KEY (std::string, Fingerprint) -RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{, lambdas, default-methods} +RUNTIME_OPTIONS_KEY (ExperimentalFlags, Experimental, ExperimentalFlags::kNone) // -Xexperimental:{none, lambdas} // Not parse-able from command line, but can be provided explicitly. // (Do not add anything here that is defined in ParsedOptions::MakeParser) -RUNTIME_OPTIONS_KEY (const std::vector<const DexFile*>*, \ - BootClassPathDexList) // TODO: make unique_ptr +RUNTIME_OPTIONS_KEY (std::vector<std::unique_ptr<const DexFile>>*, \ + BootClassPathDexList) RUNTIME_OPTIONS_KEY (InstructionSet, ImageInstructionSet, kRuntimeISA) RUNTIME_OPTIONS_KEY (CompilerCallbacks*, CompilerCallbacksPtr) // TDOO: make unique_ptr RUNTIME_OPTIONS_KEY (bool (*)(), HookIsSensitiveThread) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index fc1a4450d3..c8714a60a8 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -190,7 +190,7 @@ static constexpr uint32_t kDumpWaitTimeout = kIsTargetBuild ? 10000 : 20000; class DumpCheckpoint FINAL : public Closure { public: explicit DumpCheckpoint(std::ostream* os) - : os_(os), barrier_(0), backtrace_map_(BacktraceMap::Create(GetTid())) {} + : os_(os), barrier_(0), backtrace_map_(BacktraceMap::Create(getpid())) {} void Run(Thread* thread) OVERRIDE { // Note thread and self may not be equal if thread was already suspended at the point of the @@ -1191,6 +1191,7 @@ void ThreadList::SuspendAllDaemonThreadsForShutdown() { } LOG(WARNING) << "timed out suspending all daemon threads"; } + void ThreadList::Register(Thread* self) { DCHECK_EQ(self, Thread::Current()); diff --git a/runtime/trace.cc b/runtime/trace.cc index 5815f7a97c..99b2296b60 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -815,10 +815,10 @@ void Trace::ExceptionCaught(Thread* thread ATTRIBUTE_UNUSED, LOG(ERROR) << "Unexpected exception caught event in tracing"; } -void Trace::BackwardBranch(Thread* /*thread*/, ArtMethod* method, - int32_t /*dex_pc_offset*/) +void Trace::Branch(Thread* /*thread*/, ArtMethod* method, + uint32_t /*dex_pc*/, int32_t /*dex_pc_offset*/) SHARED_REQUIRES(Locks::mutator_lock_) { - LOG(ERROR) << "Unexpected backward branch event in tracing" << PrettyMethod(method); + LOG(ERROR) << "Unexpected branch event in tracing" << PrettyMethod(method); } void Trace::InvokeVirtualOrInterface(Thread*, diff --git a/runtime/trace.h b/runtime/trace.h index 356a81f282..80f1a4c1bc 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -164,7 +164,7 @@ class Trace FINAL : public instrumentation::InstrumentationListener { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; void ExceptionCaught(Thread* thread, mirror::Throwable* exception_object) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; - void BackwardBranch(Thread* thread, ArtMethod* method, int32_t dex_pc_offset) + void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE; void InvokeVirtualOrInterface(Thread* thread, mirror::Object* this_object, diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc index 0d40bb7be4..fc9faec5c1 100644 --- a/runtime/type_lookup_table.cc +++ b/runtime/type_lookup_table.cc @@ -16,6 +16,7 @@ #include "type_lookup_table.h" +#include "base/bit_utils.h" #include "dex_file-inl.h" #include "utf-inl.h" #include "utils.h" @@ -42,25 +43,39 @@ uint32_t TypeLookupTable::RawDataLength() const { } uint32_t TypeLookupTable::RawDataLength(const DexFile& dex_file) { - return RoundUpToPowerOfTwo(dex_file.NumClassDefs()) * sizeof(Entry); + return RawDataLength(dex_file.NumClassDefs()); } -TypeLookupTable* TypeLookupTable::Create(const DexFile& dex_file) { +uint32_t TypeLookupTable::RawDataLength(uint32_t num_class_defs) { + return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) * sizeof(Entry) : 0u; +} + +uint32_t TypeLookupTable::CalculateMask(uint32_t num_class_defs) { + return SupportedSize(num_class_defs) ? RoundUpToPowerOfTwo(num_class_defs) - 1u : 0u; +} + +bool TypeLookupTable::SupportedSize(uint32_t num_class_defs) { + return num_class_defs != 0u && num_class_defs <= std::numeric_limits<uint16_t>::max(); +} + +TypeLookupTable* TypeLookupTable::Create(const DexFile& dex_file, uint8_t* storage) { const uint32_t num_class_defs = dex_file.NumClassDefs(); - return (num_class_defs == 0 || num_class_defs > std::numeric_limits<uint16_t>::max()) - ? nullptr - : new TypeLookupTable(dex_file); + return SupportedSize(num_class_defs) + ? new TypeLookupTable(dex_file, storage) + : nullptr; } TypeLookupTable* TypeLookupTable::Open(const uint8_t* raw_data, const DexFile& dex_file) { return new TypeLookupTable(raw_data, dex_file); } -TypeLookupTable::TypeLookupTable(const DexFile& dex_file) +TypeLookupTable::TypeLookupTable(const DexFile& dex_file, uint8_t* storage) : dex_file_(dex_file), - mask_(RoundUpToPowerOfTwo(dex_file.NumClassDefs()) - 1), - entries_(new Entry[mask_ + 1]), - owns_entries_(true) { + mask_(CalculateMask(dex_file.NumClassDefs())), + entries_(storage != nullptr ? reinterpret_cast<Entry*>(storage) : new Entry[mask_ + 1]), + owns_entries_(storage == nullptr) { + static_assert(alignof(Entry) == 4u, "Expecting Entry to be 4-byte aligned."); + DCHECK_ALIGNED(storage, alignof(Entry)); std::vector<uint16_t> conflict_class_defs; // The first stage. Put elements on their initial positions. If an initial position is already // occupied then delay the insertion of the element to the second stage to reduce probing @@ -93,7 +108,7 @@ TypeLookupTable::TypeLookupTable(const DexFile& dex_file) TypeLookupTable::TypeLookupTable(const uint8_t* raw_data, const DexFile& dex_file) : dex_file_(dex_file), - mask_(RoundUpToPowerOfTwo(dex_file.NumClassDefs()) - 1), + mask_(CalculateMask(dex_file.NumClassDefs())), entries_(reinterpret_cast<Entry*>(const_cast<uint8_t*>(raw_data))), owns_entries_(false) {} diff --git a/runtime/type_lookup_table.h b/runtime/type_lookup_table.h index 3c2295c428..d74d01de4d 100644 --- a/runtime/type_lookup_table.h +++ b/runtime/type_lookup_table.h @@ -60,7 +60,7 @@ class TypeLookupTable { } // Method creates lookup table for dex file - static TypeLookupTable* Create(const DexFile& dex_file); + static TypeLookupTable* Create(const DexFile& dex_file, uint8_t* storage = nullptr); // Method opens lookup table from binary data. Lookup table does not owns binary data. static TypeLookupTable* Open(const uint8_t* raw_data, const DexFile& dex_file); @@ -76,6 +76,9 @@ class TypeLookupTable { // Method returns length of binary data for the specified dex file. static uint32_t RawDataLength(const DexFile& dex_file); + // Method returns length of binary data for the specified number of class definitions. + static uint32_t RawDataLength(uint32_t num_class_defs); + private: /** * To find element we need to compare strings. @@ -109,8 +112,11 @@ class TypeLookupTable { } }; + static uint32_t CalculateMask(uint32_t num_class_defs); + static bool SupportedSize(uint32_t num_class_defs); + // Construct from a dex file. - explicit TypeLookupTable(const DexFile& dex_file); + explicit TypeLookupTable(const DexFile& dex_file, uint8_t* storage); // Construct from a dex file with existing data. TypeLookupTable(const uint8_t* raw_data, const DexFile& dex_file); diff --git a/runtime/utils.cc b/runtime/utils.cc index 8e9f12b7a0..07f94c0766 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1115,7 +1115,7 @@ void DumpNativeStack(std::ostream& os, pid_t tid, BacktraceMap* existing_map, co BacktraceMap* map = existing_map; std::unique_ptr<BacktraceMap> tmp_map; if (map == nullptr) { - tmp_map.reset(BacktraceMap::Create(tid)); + tmp_map.reset(BacktraceMap::Create(getpid())); map = tmp_map.get(); } std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map)); diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index 90e24b9632..f6ee6a2b1a 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -60,7 +60,9 @@ inline size_t DexCacheArraysLayout::TypeOffset(uint32_t type_idx) const { } inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const { - return ArraySize(sizeof(GcRoot<mirror::Class>), num_elements); + // App image patching relies on having enough room for a forwarding pointer in the types array. + // See FixupArtMethodArrayVisitor and ClassLinker::AddImageSpace. + return std::max(ArraySize(sizeof(GcRoot<mirror::Class>), num_elements), pointer_size_); } inline size_t DexCacheArraysLayout::TypesAlignment() const { @@ -72,7 +74,8 @@ inline size_t DexCacheArraysLayout::MethodOffset(uint32_t method_idx) const { } inline size_t DexCacheArraysLayout::MethodsSize(size_t num_elements) const { - return ArraySize(pointer_size_, num_elements); + // App image patching relies on having enough room for a forwarding pointer in the methods array. + return std::max(ArraySize(pointer_size_, num_elements), pointer_size_); } inline size_t DexCacheArraysLayout::MethodsAlignment() const { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 1c95648cfe..2890a9826e 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -608,7 +608,6 @@ SafeMap<uint32_t, std::set<uint32_t>>& MethodVerifier::FindStringInitMap() { bool MethodVerifier::Verify() { // Some older code doesn't correctly mark constructors as such. Test for this case by looking at // the name. - Runtime* runtime = Runtime::Current(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const char* method_name = dex_file_->StringDataByIdx(method_id.name_idx_); bool instance_constructor_by_name = strcmp("<init>", method_name) == 0; @@ -678,12 +677,9 @@ bool MethodVerifier::Verify() { } if ((class_def_->GetJavaAccessFlags() & kAccInterface) != 0) { // Interface methods must be public and abstract (if default methods are disabled). - bool default_methods_supported = - runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods); - uint32_t kRequired = kAccPublic | (default_methods_supported ? 0 : kAccAbstract); + uint32_t kRequired = kAccPublic; if ((method_access_flags_ & kRequired) != kRequired) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be public" - << (default_methods_supported ? "" : " and abstract"); + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be public"; return false; } // In addition to the above, interface methods must not be protected. @@ -715,19 +711,14 @@ bool MethodVerifier::Verify() { // default methods enabled we also allow other public, static, non-final methods to have code. // Otherwise that is the only type of method allowed. if (!(IsConstructor() && IsStatic())) { - if (runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) { - if (IsInstanceConstructor()) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-static constructor"; - return false; - } else if (method_access_flags_ & kAccFinal) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods"; - return false; - } else if (!(method_access_flags_ & kAccPublic)) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members"; - return false; - } - } else { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract"; + if (IsInstanceConstructor()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-static constructor"; + return false; + } else if (method_access_flags_ & kAccFinal) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods"; + return false; + } else if (!(method_access_flags_ & kAccPublic)) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members"; return false; } } @@ -2735,7 +2726,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { inst->Opcode() == Instruction::INVOKE_SUPER_RANGE); bool is_super = (inst->Opcode() == Instruction::INVOKE_SUPER || inst->Opcode() == Instruction::INVOKE_SUPER_RANGE); - ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL, is_range, is_super); + MethodType type = is_super ? METHOD_SUPER : METHOD_VIRTUAL; + ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range); const RegType* return_type = nullptr; if (called_method != nullptr) { size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); @@ -2768,7 +2760,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); - ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_DIRECT, is_range, false); + ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_DIRECT, is_range); const char* return_type_descriptor; bool is_constructor; const RegType* return_type = nullptr; @@ -2848,7 +2840,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); - ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range, false); + ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range); const char* descriptor; if (called_method == nullptr) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); @@ -2870,7 +2862,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); - ArtMethod* abs_method = VerifyInvocationArgs(inst, METHOD_INTERFACE, is_range, false); + ArtMethod* abs_method = VerifyInvocationArgs(inst, METHOD_INTERFACE, is_range); if (abs_method != nullptr) { mirror::Class* called_interface = abs_method->GetDeclaringClass(); if (!called_interface->IsInterface() && !called_interface->IsObjectClass()) { @@ -3639,9 +3631,8 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { return *common_super; } -// TODO Maybe I should just add a METHOD_SUPER to MethodType? ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( - uint32_t dex_method_idx, MethodType method_type, bool is_super) { + uint32_t dex_method_idx, MethodType method_type) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx); const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_); if (klass_type.IsConflict()) { @@ -3668,9 +3659,10 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( res_method = klass->FindDirectMethod(name, signature, pointer_size); } else if (method_type == METHOD_INTERFACE) { res_method = klass->FindInterfaceMethod(name, signature, pointer_size); - } else if (is_super && klass->IsInterface()) { + } else if (method_type == METHOD_SUPER && klass->IsInterface()) { res_method = klass->FindInterfaceMethod(name, signature, pointer_size); } else { + DCHECK(method_type == METHOD_VIRTUAL || method_type == METHOD_SUPER); res_method = klass->FindVirtualMethod(name, signature, pointer_size); } if (res_method != nullptr) { @@ -3679,7 +3671,9 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( // If a virtual or interface method wasn't found with the expected type, look in // the direct methods. This can happen when the wrong invoke type is used or when // a class has changed, and will be flagged as an error in later checks. - if (method_type == METHOD_INTERFACE || method_type == METHOD_VIRTUAL) { + if (method_type == METHOD_INTERFACE || + method_type == METHOD_VIRTUAL || + method_type == METHOD_SUPER) { res_method = klass->FindDirectMethod(name, signature, pointer_size); } if (res_method == nullptr) { @@ -3710,12 +3704,10 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( // Note: this check must be after the initializer check, as those are required to fail a class, // while this check implies an IncompatibleClassChangeError. if (klass->IsInterface()) { - Runtime* runtime = Runtime::Current(); - const bool default_methods_supported = - runtime == nullptr || - runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods); + // methods called on interfaces should be invoke-interface, invoke-super, or invoke-static. if (method_type != METHOD_INTERFACE && - (!default_methods_supported || method_type != METHOD_STATIC)) { + method_type != METHOD_STATIC && + method_type != METHOD_SUPER) { Fail(VERIFY_ERROR_CLASS_CHANGE) << "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_) << " is in an interface class " << PrettyClass(klass); @@ -3742,7 +3734,7 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( return res_method; } // Check that invoke-virtual and invoke-super are not used on private methods of the same class. - if (res_method->IsPrivate() && method_type == METHOD_VIRTUAL) { + if (res_method->IsPrivate() && (method_type == METHOD_VIRTUAL || method_type == METHOD_SUPER)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invoke-super/virtual can't be used on private method " << PrettyMethod(res_method); return nullptr; @@ -3751,7 +3743,9 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( // target method. if ((method_type == METHOD_DIRECT && (!res_method->IsDirect() || res_method->IsStatic())) || (method_type == METHOD_STATIC && !res_method->IsStatic()) || - ((method_type == METHOD_VIRTUAL || method_type == METHOD_INTERFACE) && res_method->IsDirect()) + ((method_type == METHOD_SUPER || + method_type == METHOD_VIRTUAL || + method_type == METHOD_INTERFACE) && res_method->IsDirect()) ) { Fail(VERIFY_ERROR_CLASS_CHANGE) << "invoke type (" << method_type << ") does not match method " " type of " << PrettyMethod(res_method); @@ -3937,12 +3931,12 @@ class MethodParamListDescriptorIterator { }; ArtMethod* MethodVerifier::VerifyInvocationArgs( - const Instruction* inst, MethodType method_type, bool is_range, bool is_super) { + const Instruction* inst, MethodType method_type, bool is_range) { // Resolve the method. This could be an abstract or concrete method depending on what sort of call // we're making. const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - ArtMethod* res_method = ResolveMethodAndCheckAccess(method_idx, method_type, is_super); + ArtMethod* res_method = ResolveMethodAndCheckAccess(method_idx, method_type); if (res_method == nullptr) { // error or class is unresolved // Check what we can statically. if (!have_pending_hard_failure_) { @@ -3953,8 +3947,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgs( // If we're using invoke-super(method), make sure that the executing method's class' superclass // has a vtable entry for the target method. Or the target is on a interface. - if (is_super) { - DCHECK(method_type == METHOD_VIRTUAL); + if (method_type == METHOD_SUPER) { if (res_method->GetDeclaringClass()->IsInterface()) { // TODO Fill in this part. Verify what we can... if (Runtime::Current()->IsAotCompiler()) { diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index ec0a8f9262..a26e0fba13 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -57,7 +57,8 @@ enum MethodType { METHOD_UNKNOWN = 0, METHOD_DIRECT, // <init>, private METHOD_STATIC, // static - METHOD_VIRTUAL, // virtual, super + METHOD_VIRTUAL, // virtual + METHOD_SUPER, // super METHOD_INTERFACE // interface }; std::ostream& operator<<(std::ostream& os, const MethodType& rhs); @@ -654,7 +655,7 @@ class MethodVerifier { * the referrer can access the resolved method. * Does not throw exceptions. */ - ArtMethod* ResolveMethodAndCheckAccess(uint32_t method_idx, MethodType method_type, bool is_super) + ArtMethod* ResolveMethodAndCheckAccess(uint32_t method_idx, MethodType method_type) SHARED_REQUIRES(Locks::mutator_lock_); /* @@ -679,9 +680,7 @@ class MethodVerifier { * Returns the resolved method on success, null on failure (with *failure * set appropriately). */ - ArtMethod* VerifyInvocationArgs(const Instruction* inst, - MethodType method_type, - bool is_range, bool is_super) + ArtMethod* VerifyInvocationArgs(const Instruction* inst, MethodType method_type, bool is_range) SHARED_REQUIRES(Locks::mutator_lock_); // Similar checks to the above, but on the proto. Will be used when the method cannot be diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 83009212e8..cfa8329a36 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -129,6 +129,7 @@ jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch; jfieldID WellKnownClasses::dalvik_system_DexFile_cookie; +jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_PathClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; @@ -333,6 +334,7 @@ void WellKnownClasses::Init(JNIEnv* env) { true, "newStringFromStringBuilder", "(Ljava/lang/StringBuilder;)Ljava/lang/String;"); dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;"); + dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_PathClassLoader_pathList = CacheField(env, dalvik_system_PathClassLoader, false, "pathList", "Ldalvik/system/DexPathList;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 55158a77c6..482ff0a8f4 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -140,6 +140,7 @@ struct WellKnownClasses { static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch; static jfieldID dalvik_system_DexFile_cookie; + static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; static jfieldID dalvik_system_PathClassLoader_pathList; diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index 9daaf8e204..d96fb429e5 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -133,4 +133,8 @@ ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const { return new ZipEntry(handle_, zip_entry.release()); } +ZipArchive::~ZipArchive() { + CloseArchive(handle_); +} + } // namespace art diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index 717eb8c82e..42bf55cb3f 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -63,9 +63,7 @@ class ZipArchive { ZipEntry* Find(const char* name, std::string* error_msg) const; - ~ZipArchive() { - CloseArchive(handle_); - } + ~ZipArchive(); private: explicit ZipArchive(ZipArchiveHandle handle) : handle_(handle) {} diff --git a/test/004-StackWalk/src/Main.java b/test/004-StackWalk/src/Main.java index 883ce2c9fe..072b1d0c4d 100644 --- a/test/004-StackWalk/src/Main.java +++ b/test/004-StackWalk/src/Main.java @@ -2,14 +2,14 @@ public class Main { public Main() { } + boolean doThrow = false; + int $noinline$f() throws Exception { g(1); g(2); - // This loop currently defeats inlining of `f`. - for (int i = 0; i < 10; i++) { - Thread.sleep(0); - } + // This currently defeats inlining of `f`. + if (doThrow) { throw new Error(); } return 0; } diff --git a/test/964-default-iface-init-generated/run b/test/048-reflect-v8/build index 22f68006e7..4ea1838465 100755..100644 --- a/test/964-default-iface-init-generated/run +++ b/test/048-reflect-v8/build @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2015 The Android Open Source Project +# Copyright 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,4 +14,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -${RUN} --experimental default-methods "$@" +# Make us exit on a failure. +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/048-reflect-v8/expected.txt b/test/048-reflect-v8/expected.txt new file mode 100644 index 0000000000..2d0b4ccb6b --- /dev/null +++ b/test/048-reflect-v8/expected.txt @@ -0,0 +1,4 @@ +Main$DefaultInterface is default = yes +Main$RegularInterface is default = no +Main$ImplementsWithDefault is default = yes +Main$ImplementsWithRegular is default = no diff --git a/test/048-reflect-v8/info.txt b/test/048-reflect-v8/info.txt new file mode 100644 index 0000000000..a336d301d6 --- /dev/null +++ b/test/048-reflect-v8/info.txt @@ -0,0 +1 @@ +Test reflection for 1.8 APIs diff --git a/test/048-reflect-v8/src/Main.java b/test/048-reflect-v8/src/Main.java new file mode 100644 index 0000000000..7fa2a923d7 --- /dev/null +++ b/test/048-reflect-v8/src/Main.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + interface DefaultInterface { + default void sayHi() { + System.out.println("hi default"); + } + } + + interface RegularInterface { + void sayHi(); + } + + class ImplementsWithDefault implements DefaultInterface {} + class ImplementsWithRegular implements RegularInterface { + public void sayHi() { + System.out.println("hello specific"); + } + } + + private static void printIsDefault(Class<?> klass) { + Method m; + try { + m = klass.getMethod("sayHi"); + } catch (Throwable t) { + System.out.println(t); + return; + } + + boolean isDefault = m.isDefault(); + System.out.println(klass.getName() + " is default = " + (isDefault ? "yes" : "no")); + } + + public static void main(String[] args) { + printIsDefault(DefaultInterface.class); + printIsDefault(RegularInterface.class); + printIsDefault(ImplementsWithDefault.class); + printIsDefault(ImplementsWithRegular.class); + } +} diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 77301d20e8..87656bcf0e 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -101,7 +101,12 @@ static bool IsPicImage() { } #endif -extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( + JNIEnv*, + jobject, + jboolean full_signatrues, + jint, + jboolean) { #if __linux__ if (IsPicImage()) { LOG(INFO) << "Image is pic, in-process unwinding check bypassed."; @@ -122,14 +127,21 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject // We cannot really parse an exact stack, as the optimizing compiler may inline some functions. // This is also risky, as deduping might play a trick on us, so the test needs to make sure that // only unique functions are being expected. + // "mini-debug-info" does not include parameters to save space. std::vector<std::string> seq = { "Java_Main_unwindInProcess", // This function. - "boolean Main.unwindInProcess(int, boolean)", // The corresponding Java native method frame. + "Main.unwindInProcess", // The corresponding Java native method frame. + "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method. + "Main.main" // The Java entry method. + }; + std::vector<std::string> full_seq = { + "Java_Main_unwindInProcess", // This function. + "boolean Main.unwindInProcess(boolean, int, boolean)", // The corresponding Java native method frame. "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method. "void Main.main(java.lang.String[])" // The Java entry method. }; - bool result = CheckStack(bt.get(), seq); + bool result = CheckStack(bt.get(), full_signatrues ? full_seq : seq); if (!kCauseSegfault) { return result ? JNI_TRUE : JNI_FALSE; } else { @@ -178,7 +190,11 @@ int wait_for_sigstop(pid_t tid, int* total_sleep_time_usec, bool* detach_failed } #endif -extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess( + JNIEnv*, + jobject, + jboolean full_signatrues, + jint pid_int) { #if __linux__ // TODO: What to do on Valgrind? pid_t pid = static_cast<pid_t>(pid_int); @@ -214,17 +230,27 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobj if (result) { // See comment in unwindInProcess for non-exact stack matching. + // "mini-debug-info" does not include parameters to save space. std::vector<std::string> seq = { // "Java_Main_sleep", // The sleep function being executed in the // other runtime. // Note: For some reason, the name isn't // resolved, so don't look for it right now. + "Main.sleep", // The corresponding Java native method frame. + "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method. + "Main.main" // The Java entry method. + }; + std::vector<std::string> full_seq = { + // "Java_Main_sleep", // The sleep function being executed in the + // other runtime. + // Note: For some reason, the name isn't + // resolved, so don't look for it right now. "boolean Main.sleep(int, boolean, double)", // The corresponding Java native method frame. "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method. "void Main.main(java.lang.String[])" // The Java entry method. }; - result = CheckStack(bt.get(), seq); + result = CheckStack(bt.get(), full_signatrues ? full_seq : seq); } if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) { diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt index 6a5618ebc6..8db7853696 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 9c567b6813..6f4bcfe658 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -14,4 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -exec ${RUN} "$@" -Xcompiler-option --generate-debug-info +# Test with full DWARF debugging information. +# Check full signatures of methods. +${RUN} "$@" -Xcompiler-option --generate-debug-info --args --full-signatures + +# Test with minimal compressed debugging information. +# Check only method names (parameters are omitted to save space). +${RUN} "$@" -Xcompiler-option --generate-mini-debug-info diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java index 5474c9b7b6..77553380c8 100644 --- a/test/137-cfi/src/Main.java +++ b/test/137-cfi/src/Main.java @@ -34,19 +34,28 @@ public class Main implements Comparator<Main> { private boolean secondary; + private boolean full_signatures; + private boolean passed; - public Main(boolean secondary) { + public Main(boolean secondary, boolean full_signatures) { this.secondary = secondary; + this.full_signatures = full_signatures; } public static void main(String[] args) throws Exception { System.loadLibrary(args[0]); boolean secondary = false; - if (args.length > 0 && args[args.length - 1].equals("--secondary")) { + 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).run(); + new Main(secondary, full_signatures).run(); } private void run() { @@ -96,7 +105,7 @@ public class Main implements Comparator<Main> { throw new RuntimeException(e); } - if (!unwindOtherProcess(pid)) { + if (!unwindOtherProcess(full_signatures, pid)) { System.out.println("Unwinding other process failed."); } } finally { @@ -154,7 +163,7 @@ public class Main implements Comparator<Main> { if (b) { return sleep(2, b, 1.0); } else { - return unwindInProcess(1, b); + return unwindInProcess(full_signatures, 1, b); } } @@ -162,6 +171,6 @@ public class Main implements Comparator<Main> { public native boolean sleep(int i, boolean b, double dummy); - public native boolean unwindInProcess(int i, boolean b); - public native boolean unwindOtherProcess(int pid); + public native boolean unwindInProcess(boolean full_signatures, int i, boolean b); + public native boolean unwindOtherProcess(boolean full_signatures, int pid); } diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java index 3e6d1f4d94..06cfd0a606 100644 --- a/test/449-checker-bce/src/Main.java +++ b/test/449-checker-bce/src/Main.java @@ -622,28 +622,39 @@ public class Main { static int[][] mA; /// CHECK-START: void Main.dynamicBCEAndIntrinsic(int) BCE (before) - /// CHECK-DAG: NullCheck - /// CHECK-DAG: ArrayLength - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: NullCheck - /// CHECK-DAG: ArrayLength - /// CHECK-DAG: BoundsCheck - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: InvokeStaticOrDirect - /// CHECK-DAG: ArraySet - + // Array references mA[i] and ..[j] both in inner loop. + /// CHECK-DAG: <<Get1:l\d+>> ArrayGet [<<Array1:l\d+>>,<<Bounds1:i\d+>>] loop:<<InnerLoop:B\d+>> + /// CHECK-DAG: <<Array1>> NullCheck [<<Field1:l\d+>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Len1:i\d+>> ArrayLength [<<Array1>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Bounds1>> BoundsCheck [<<Index1:i\d+>>,<<Len1>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Get2:i\d+>> ArrayGet [<<Array2:l\d+>>,<<Bounds2:i\d+>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Array2>> NullCheck [<<Get1>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Len2:i\d+>> ArrayLength [<<Array2>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Bounds2>> BoundsCheck [<<Index2:i\d+>>,<<Len2>>] loop:<<InnerLoop>> + /// CHECK-DAG: InvokeStaticOrDirect [<<Get2>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Index2>> Phi loop:<<InnerLoop>> + /// CHECK-DAG: <<Index1>> Phi loop:<<OuterLoop:B\d+>> + /// CHECK-DAG: <<Field1>> StaticFieldGet loop:none + /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" + // + /// CHECK-START: void Main.dynamicBCEAndIntrinsic(int) BCE (after) + // Array reference mA[i] hoisted to same level as deopt. + /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>> + /// CHECK-DAG: ArrayLength loop:<<OuterLoop>> + /// CHECK-DAG: <<Get1:l\d+>> ArrayGet [<<Array1:l\d+>>,<<Index1:i\d+>>] loop:<<OuterLoop>> + // Array reference ..[j] still in inner loop, with a direct index. + /// CHECK-DAG: <<Get2:i\d+>> ArrayGet [<<Array2:l\d+>>,<<Index2:i\d+>>] loop:<<InnerLoop:B\d+>> + /// CHECK-DAG: InvokeStaticOrDirect [<<Get2>>] loop:<<InnerLoop>> + /// CHECK-DAG: <<Index2>> Phi loop:<<InnerLoop>> + /// CHECK-DAG: <<Index1>> Phi loop:<<OuterLoop>> + // Synthetic phi. + /// CHECK-DAG: <<Array2>> Phi loop:<<OuterLoop>> + /// CHECK-DAG: <<Array1>> StaticFieldGet loop:none + /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>" + // /// CHECK-START: void Main.dynamicBCEAndIntrinsic(int) BCE (after) /// CHECK-NOT: NullCheck - /// CHECK-NOT: ArrayLength /// CHECK-NOT: BoundsCheck - /// CHECK-DAG: ArrayGet - /// CHECK-NOT: ArrayGet - /// CHECK-DAG: InvokeStaticOrDirect - /// CHECK-DAG: ArraySet - /// CHECK-DAG: Exit - /// CHECK-DAG: Deoptimize - static void dynamicBCEAndIntrinsic(int n) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { diff --git a/test/482-checker-loop-back-edge-use/src/Main.java b/test/482-checker-loop-back-edge-use/src/Main.java index d0b33b9282..f8f0aa3f0a 100644 --- a/test/482-checker-loop-back-edge-use/src/Main.java +++ b/test/482-checker-loop-back-edge-use/src/Main.java @@ -40,6 +40,9 @@ public class Main { /// CHECK-EVAL: <<GotoLiv2>> + 2 == <<ArgLoopUse2>> public static void loop2(boolean incoming) { + // Add some code at entry to avoid having the entry block be a pre header. + // This avoids having to create a synthesized block. + System.out.println("Enter"); while (true) { System.out.println("foo"); while (incoming) {} @@ -170,6 +173,9 @@ public class Main { /// CHECK-EVAL: <<GotoLiv1>> + 2 == <<ArgLoopUse>> public static void loop9() { + // Add some code at entry to avoid having the entry block be a pre header. + // This avoids having to create a synthesized block. + System.out.println("Enter"); while (Runtime.getRuntime() != null) { // 'incoming' must only have a use in the inner loop. boolean incoming = field; diff --git a/test/496-checker-inlining-and-class-loader/src/Main.java b/test/496-checker-inlining-and-class-loader/src/Main.java index 39c031a6bc..ea6df623a1 100644 --- a/test/496-checker-inlining-and-class-loader/src/Main.java +++ b/test/496-checker-inlining-and-class-loader/src/Main.java @@ -16,6 +16,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.List; class MyClassLoader extends ClassLoader { @@ -30,18 +31,31 @@ class MyClassLoader extends ClassLoader { Object pathList = f.get(loader); // Some magic to get access to the dexField field of pathList. + // Need to make a copy of the dex elements since we don't want an app image with pre-resolved + // things. f = pathList.getClass().getDeclaredField("dexElements"); f.setAccessible(true); - dexElements = (Object[]) f.get(pathList); - dexFileField = dexElements[0].getClass().getDeclaredField("dexFile"); - dexFileField.setAccessible(true); + Object[] dexElements = (Object[]) f.get(pathList); + f = dexElements[0].getClass().getDeclaredField("dexFile"); + f.setAccessible(true); + for (Object element : dexElements) { + Object dexFile = f.get(element); + // Make copy. + Field fileNameField = dexFile.getClass().getDeclaredField("mFileName"); + fileNameField.setAccessible(true); + dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance( + fileNameField.get(dexFile))); + } } - Object[] dexElements; + ArrayList<Object> dexFiles = new ArrayList<Object>(); Field dexFileField; protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException { - System.out.println("Request for " + className); + // Other classes may also get loaded, ignore those. + if (className.equals("LoadedByMyClassLoader") || className.equals("FirstSeenByMyClassLoader")) { + System.out.println("Request for " + className); + } // We're only going to handle LoadedByMyClassLoader. if (className != "LoadedByMyClassLoader") { @@ -50,13 +64,12 @@ class MyClassLoader extends ClassLoader { // Mimic what DexPathList.findClass is doing. try { - for (Object element : dexElements) { - Object dex = dexFileField.get(element); - Method method = dex.getClass().getDeclaredMethod( + for (Object dexFile : dexFiles) { + Method method = dexFile.getClass().getDeclaredMethod( "loadClassBinaryName", String.class, ClassLoader.class, List.class); - if (dex != null) { - Class clazz = (Class)method.invoke(dex, className, this, null); + if (dexFile != null) { + Class clazz = (Class)method.invoke(dexFile, className, this, null); if (clazz != null) { return clazz; } diff --git a/test/506-verify-aput/src/Main.java b/test/506-verify-aput/src/Main.java index 8359f2c452..08368d4fa3 100644 --- a/test/506-verify-aput/src/Main.java +++ b/test/506-verify-aput/src/Main.java @@ -23,11 +23,12 @@ public class Main { try { Class.forName("VerifyAPut1"); throw new Error("expected verification error"); - } catch (VerifyError e) { /* ignore */ } - + } catch (VerifyError e) { /* ignore */ + } catch (Error e) { System.out.println(e.getClass() + " " + e.getClass().getClassLoader()); } try { Class.forName("VerifyAPut2"); throw new Error("expected verification error"); - } catch (VerifyError e) { /* ignore */ } + } catch (VerifyError e) { /* ignore */ + } catch (Error e) { System.out.println(e.getClass() + " " + e.getClass().getClassLoader()); } } } diff --git a/test/510-checker-try-catch/smali/SsaBuilder.smali b/test/510-checker-try-catch/smali/SsaBuilder.smali index 710e849c45..a6a5bfebee 100644 --- a/test/510-checker-try-catch/smali/SsaBuilder.smali +++ b/test/510-checker-try-catch/smali/SsaBuilder.smali @@ -21,7 +21,7 @@ ## CHECK-START: int SsaBuilder.testSimplifyCatchBlock(int, int, int) ssa_builder (after) -## CHECK: name "B0" +## CHECK: name "B1" ## CHECK-NEXT: from_bci ## CHECK-NEXT: to_bci ## CHECK-NEXT: predecessors @@ -39,12 +39,15 @@ ## CHECK: name "<<BExtracted>>" ## CHECK-NEXT: from_bci ## CHECK-NEXT: to_bci -## CHECK-NEXT: predecessors "B0" "<<BCatch>>" +## CHECK-NEXT: predecessors "B1" "<<BCatch>>" ## CHECK-NOT: flags "catch_block" ## CHECK: Add .method public static testSimplifyCatchBlock(III)I .registers 4 + # Avoid entry block be a pre header, which leads to + # the cfg simplifier to add a synthesized block. + goto :catch_all :catch_all add-int/2addr p0, p1 diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java index f1d9a37c88..deff279f77 100644 --- a/test/530-checker-loops/src/Main.java +++ b/test/530-checker-loops/src/Main.java @@ -26,7 +26,7 @@ public class Main { // /// CHECK-START: int Main.linear(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linear(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -40,7 +40,7 @@ public class Main { } /// CHECK-START: int Main.linearDown(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearDown(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -54,7 +54,7 @@ public class Main { } /// CHECK-START: int Main.linearObscure(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearObscure(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -69,7 +69,7 @@ public class Main { } /// CHECK-START: int Main.linearVeryObscure(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearVeryObscure(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -84,7 +84,7 @@ public class Main { } /// CHECK-START: int Main.hiddenStride(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.hiddenStride(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -101,7 +101,7 @@ public class Main { } /// CHECK-START: int Main.linearWhile(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearWhile(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -116,7 +116,7 @@ public class Main { } /// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearThreeWayPhi(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -134,7 +134,7 @@ public class Main { } /// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearFourWayPhi(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -156,7 +156,7 @@ public class Main { } /// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.wrapAroundThenLinear(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -173,7 +173,7 @@ public class Main { } /// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.wrapAroundThenLinearThreeWayPhi(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -194,7 +194,7 @@ public class Main { } /// CHECK-START: int[] Main.linearWithParameter(int) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int[] Main.linearWithParameter(int) BCE (after) /// CHECK-NOT: BoundsCheck @@ -208,7 +208,7 @@ public class Main { } /// CHECK-START: int[] Main.linearCopy(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int[] Main.linearCopy(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -223,8 +223,8 @@ public class Main { } /// CHECK-START: int Main.linearByTwo(int[]) BCE (before) - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearByTwo(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -241,7 +241,7 @@ public class Main { } /// CHECK-START: int Main.linearByTwoSkip1(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearByTwoSkip1(int[]) BCE (after) /// CHECK-NOT: BoundsCheck @@ -255,10 +255,12 @@ public class Main { } /// CHECK-START: int Main.linearByTwoSkip2(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearByTwoSkip2(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearByTwoSkip2(int[]) BCE (after) - /// CHECK: BoundsCheck /// CHECK-NOT: Deoptimize private static int linearByTwoSkip2(int x[]) { int result = 0; @@ -270,7 +272,7 @@ public class Main { } /// CHECK-START: int Main.linearWithCompoundStride() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearWithCompoundStride() BCE (after) /// CHECK-NOT: BoundsCheck @@ -287,7 +289,7 @@ public class Main { } /// CHECK-START: int Main.linearWithLargePositiveStride() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearWithLargePositiveStride() BCE (after) /// CHECK-NOT: BoundsCheck @@ -305,10 +307,12 @@ public class Main { } /// CHECK-START: int Main.linearWithVeryLargePositiveStride() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearWithVeryLargePositiveStride() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearWithVeryLargePositiveStride() BCE (after) - /// CHECK: BoundsCheck /// CHECK-NOT: Deoptimize private static int linearWithVeryLargePositiveStride() { int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; @@ -323,7 +327,7 @@ public class Main { } /// CHECK-START: int Main.linearWithLargeNegativeStride() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearWithLargeNegativeStride() BCE (after) /// CHECK-NOT: BoundsCheck @@ -341,10 +345,12 @@ public class Main { } /// CHECK-START: int Main.linearWithVeryLargeNegativeStride() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearWithVeryLargeNegativeStride() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearWithVeryLargeNegativeStride() BCE (after) - /// CHECK: BoundsCheck /// CHECK-NOT: Deoptimize private static int linearWithVeryLargeNegativeStride() { int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; @@ -359,7 +365,7 @@ public class Main { } /// CHECK-START: int Main.linearForNEUp() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearForNEUp() BCE (after) /// CHECK-NOT: BoundsCheck @@ -374,7 +380,7 @@ public class Main { } /// CHECK-START: int Main.linearForNEDown() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearForNEDown() BCE (after) /// CHECK-NOT: BoundsCheck @@ -389,7 +395,7 @@ public class Main { } /// CHECK-START: int Main.linearDoWhileUp() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearDoWhileUp() BCE (after) /// CHECK-NOT: BoundsCheck @@ -405,7 +411,7 @@ public class Main { } /// CHECK-START: int Main.linearDoWhileDown() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearDoWhileDown() BCE (after) /// CHECK-NOT: BoundsCheck @@ -421,10 +427,12 @@ public class Main { } /// CHECK-START: int Main.linearShort() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.linearShort() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.linearShort() BCE (after) - /// CHECK: BoundsCheck /// CHECK-NOT: Deoptimize private static int linearShort() { int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -437,7 +445,7 @@ public class Main { } /// CHECK-START: int Main.invariantFromPreLoop(int[], int) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.invariantFromPreLoop(int[], int) BCE (after) /// CHECK-NOT: BoundsCheck @@ -458,20 +466,11 @@ public class Main { } /// CHECK-START: void Main.linearTriangularOnTwoArrayLengths(int) BCE (before) - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.linearTriangularOnTwoArrayLengths(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet /// CHECK-NOT: Deoptimize private static void linearTriangularOnTwoArrayLengths(int n) { int[] a = new int[n]; @@ -488,20 +487,11 @@ public class Main { } /// CHECK-START: void Main.linearTriangularOnOneArrayLength(int) BCE (before) - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.linearTriangularOnOneArrayLength(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet /// CHECK-NOT: Deoptimize private static void linearTriangularOnOneArrayLength(int n) { int[] a = new int[n]; @@ -518,20 +508,11 @@ public class Main { } /// CHECK-START: void Main.linearTriangularOnParameter(int) BCE (before) - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.linearTriangularOnParameter(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet /// CHECK-NOT: Deoptimize private static void linearTriangularOnParameter(int n) { int[] a = new int[n]; @@ -548,32 +529,13 @@ public class Main { } /// CHECK-START: void Main.linearTriangularVariations(int) BCE (before) - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.linearTriangularVariations(int) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet /// CHECK-NOT: Deoptimize private static void linearTriangularVariations(int n) { int[] a = new int[n]; @@ -616,22 +578,11 @@ public class Main { } /// CHECK-START: void Main.bubble(int[]) BCE (before) - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: If - /// CHECK: ArraySet - /// CHECK: ArraySet + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.bubble(int[]) BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: If - /// CHECK: ArraySet - /// CHECK: ArraySet /// CHECK-NOT: Deoptimize private static void bubble(int[] a) { for (int i = a.length; --i >= 0;) { @@ -646,7 +597,7 @@ public class Main { } /// CHECK-START: int Main.periodicIdiom(int) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.periodicIdiom(int) BCE (after) /// CHECK-NOT: BoundsCheck @@ -664,7 +615,7 @@ public class Main { } /// CHECK-START: int Main.periodicSequence2(int) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.periodicSequence2(int) BCE (after) /// CHECK-NOT: BoundsCheck @@ -685,10 +636,10 @@ public class Main { } /// CHECK-START: int Main.periodicSequence4(int) BCE (before) - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.periodicSequence4(int) BCE (after) /// CHECK-NOT: BoundsCheck @@ -713,7 +664,7 @@ public class Main { } /// CHECK-START: int Main.justRightUp1() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justRightUp1() BCE (after) /// CHECK-NOT: BoundsCheck @@ -728,7 +679,7 @@ public class Main { } /// CHECK-START: int Main.justRightUp2() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justRightUp2() BCE (after) /// CHECK-NOT: BoundsCheck @@ -743,7 +694,7 @@ public class Main { } /// CHECK-START: int Main.justRightUp3() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justRightUp3() BCE (after) /// CHECK-NOT: BoundsCheck @@ -758,10 +709,12 @@ public class Main { } /// CHECK-START: int Main.justOOBUp() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBUp() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justOOBUp() BCE (after) - /// CHECK: BoundsCheck /// CHECK-NOT: Deoptimize private static int justOOBUp() { int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -774,7 +727,7 @@ public class Main { } /// CHECK-START: int Main.justRightDown1() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justRightDown1() BCE (after) /// CHECK-NOT: BoundsCheck @@ -789,7 +742,7 @@ public class Main { } /// CHECK-START: int Main.justRightDown2() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justRightDown2() BCE (after) /// CHECK-NOT: BoundsCheck @@ -804,7 +757,7 @@ public class Main { } /// CHECK-START: int Main.justRightDown3() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justRightDown3() BCE (after) /// CHECK-NOT: BoundsCheck @@ -819,10 +772,12 @@ public class Main { } /// CHECK-START: int Main.justOOBDown() BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int Main.justOOBDown() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int Main.justOOBDown() BCE (after) - /// CHECK: BoundsCheck /// CHECK-NOT: Deoptimize private static int justOOBDown() { int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -835,66 +790,74 @@ public class Main { } /// CHECK-START: void Main.lowerOOB(int[]) BCE (before) - /// CHECK: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.lowerOOB(int[]) BCE (after) - /// CHECK: BoundsCheck /// 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: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.upperOOB(int[]) BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.upperOOB(int[]) BCE (after) - /// CHECK: BoundsCheck /// 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: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileUpOOB() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.doWhileUpOOB() BCE (after) - /// CHECK: BoundsCheck /// 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: BoundsCheck + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: void Main.doWhileDownOOB() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: void Main.doWhileDownOOB() BCE (after) - /// CHECK: BoundsCheck /// 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: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int[] Main.multiply1() BCE (after) /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet /// CHECK-NOT: Deoptimize private static int[] multiply1() { int[] a = new int[10]; @@ -912,21 +875,20 @@ public class Main { } /// CHECK-START: int[] Main.multiply2() BCE (before) - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet + /// CHECK-DAG: BoundsCheck + // + /// CHECK-START: int[] Main.multiply2() BCE (after) + /// CHECK-DAG: BoundsCheck // /// CHECK-START: int[] Main.multiply2() BCE (after) - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: ArraySet + /// 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; + a[i * j] += 1; } } } catch (Exception e) { @@ -936,24 +898,19 @@ public class Main { } /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before) - /// CHECK: StaticFieldGet - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: StaticFieldSet + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> // /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after) - /// CHECK: StaticFieldGet - /// CHECK-NOT: NullCheck - /// CHECK-NOT: ArrayLength - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: StaticFieldSet - /// CHECK: Exit - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize + /// 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++) { @@ -963,24 +920,19 @@ public class Main { } /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before) - /// CHECK: StaticFieldGet - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: StaticFieldSet + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> // /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after) - /// CHECK: StaticFieldGet - /// CHECK-NOT: NullCheck - /// CHECK-NOT: ArrayLength - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: StaticFieldSet - /// CHECK: Exit - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize + /// 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++) { @@ -990,19 +942,19 @@ public class Main { } /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> // /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: NullCheck - /// CHECK-NOT: ArrayLength - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet + /// 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; @@ -1014,19 +966,19 @@ public class Main { } /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> // /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after) - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: NullCheck - /// CHECK-NOT: ArrayLength - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet + /// 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; @@ -1038,20 +990,19 @@ public class Main { } /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> // /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK-NOT: NullCheck - /// CHECK-NOT: ArrayLength - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: Exit - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize + /// 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. @@ -1063,16 +1014,14 @@ public class Main { } /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> // /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet /// CHECK-NOT: Deoptimize static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) { // As above, but now the index is not used as subscript, @@ -1085,16 +1034,14 @@ public class Main { } /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> // /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet /// CHECK-NOT: Deoptimize static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) { int result = 0; @@ -1107,42 +1054,21 @@ public class Main { } /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: NotEqual - /// CHECK: If - /// CHECK: If - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: If - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> // /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) - /// CHECK: NullCheck - /// CHECK: ArrayLength - /// CHECK: NotEqual - /// CHECK: If - /// CHECK: If - /// CHECK-NOT: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: If - /// CHECK: Deoptimize - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK: BoundsCheck - /// CHECK-NOT: BoundsCheck - /// CHECK: Exit - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK: Deoptimize - /// CHECK-NOT: ArrayGet + // Order matters: + /// CHECK: Deoptimize loop:<<Loop:B\d+>> + // CHECK-NOT: Goto loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>> + /// CHECK: Goto loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) { // Deliberately test array length on a before the loop so that only bounds checks // on constant subscripts remain, making them a viable candidate for hoisting. @@ -1166,80 +1092,73 @@ public class Main { return result; } - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], java.lang.Integer[], int, int) BCE (before) - /// CHECK: If - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - /// CHECK: BoundsCheck - /// CHECK: ArrayGet - // - /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], java.lang.Integer[], int, int) BCE (after) - /// CHECK-DAG: If - /// CHECK-NOT: BoundsCheck - /// CHECK-DAG: ArrayGet - /// CHECK-NOT: BoundsCheck - /// CHECK-NOT: ArrayGet - /// CHECK-DAG: Exit - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: Deoptimize - /// CHECK-DAG: ArrayGet - static int dynamicBCEAndConstantIndicesAllTypes(int[] q, - boolean[] r, - byte[] s, - char[] t, - short[] u, - int[] v, - long[] w, - float[] x, - double[] y, - Integer[] z, int lo, int hi) { + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + // For brevity, just test occurrence of at least one of each in the loop: + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-NOT: ArrayGet loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-NOT: NullCheck loop:{{B\d+}} + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after) + /// CHECK-DAG: Deoptimize loop:none + static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q, + boolean[] r, + byte[] s, + char[] t, + short[] u, + int[] v, + long[] w, + float[] x, + double[] y, int lo, int hi) { int result = 0; for (int i = lo; i < hi; i++) { + // All constant index array references can be hoisted out of the loop during BCE on q[i]. result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] + - (int) w[0] + (int) x[0] + (int) y[0] + (int) z[0]; + (int) w[0] + (int) x[0] + (int) y[0]; + } + return result; + } + + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + /// CHECK-DAG: ArrayGet loop:<<Loop>> + /// CHECK-DAG: NullCheck loop:<<Loop>> + /// CHECK-DAG: ArrayLength loop:<<Loop>> + /// CHECK-DAG: BoundsCheck loop:<<Loop>> + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>> + /// CHECK-DAG: Deoptimize loop:none + // + /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after) + /// CHECK-NOT: ArrayLength loop:{{B\d+}} + /// CHECK-NOT: BoundsCheck loop:{{B\d+}} + static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) { + int result = 0; + for (int i = lo; i < hi; i++) { + // Similar to above, but now implicit call to intValue() may prevent hoisting + // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i]. + result += q[i] + z[0]; } return result; } @@ -1501,9 +1420,10 @@ public class Main { 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(505, - dynamicBCEAndConstantIndicesAllTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, x9, 0, 10)); + expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10)); } private static void expectEquals(int expected, int result) { diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali index 4bd804da26..54312a43d0 100644 --- a/test/563-checker-fakestring/smali/TestCase.smali +++ b/test/563-checker-fakestring/smali/TestCase.smali @@ -124,3 +124,59 @@ return-object v0 .end method + +# Test that the compiler does not assume that the first argument of String.<init> +# is a NewInstance by inserting an irreducible loop between them (b/26676472). + +# We verify the type of the input instruction (Phi) in debuggable mode, because +# it is eliminated by later stages of SsaBuilder otherwise. + +## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance1(byte[], boolean) register (after) +## CHECK-DAG: InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}} +## CHECK-DAG: <<Phi>> Phi + +.method public static thisNotNewInstance1([BZ)Ljava/lang/String; + .registers 5 + + new-instance v0, Ljava/lang/String; + + # Irreducible loop + if-eqz p1, :loop_entry + :loop_header + const v1, 0x1 + xor-int p1, p1, v1 + :loop_entry + if-eqz p1, :string_init + goto :loop_header + + :string_init + const-string v1, "UTF8" + invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V + return-object v0 + +.end method + +## CHECK-START-DEBUGGABLE: java.lang.String TestCase.thisNotNewInstance2(byte[], boolean) register (after) +## CHECK-DAG: InvokeStaticOrDirect env:[[<<Phi:l\d+>>,{{.*]]}} +## CHECK-DAG: <<Phi>> Phi + +.method public static thisNotNewInstance2([BZ)Ljava/lang/String; + .registers 5 + + new-instance v0, Ljava/lang/String; + + # Irreducible loop + if-eqz p1, :loop_entry + :loop_header + if-eqz p1, :string_init + :loop_entry + const v1, 0x1 + xor-int p1, p1, v1 + goto :loop_header + + :string_init + const-string v1, "UTF8" + invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V + return-object v0 + +.end method diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java index 04df0f637c..1ac8a5bfcf 100644 --- a/test/563-checker-fakestring/src/Main.java +++ b/test/563-checker-fakestring/src/Main.java @@ -63,5 +63,20 @@ public class Main { String result = (String) m.invoke(null, new Object[] { testData }); assertEqual(testString, result); } + + { + Method m = c.getMethod("thisNotNewInstance1", byte[].class, boolean.class); + String result = (String) m.invoke(null, new Object[] { testData, true }); + assertEqual(testString, result); + result = (String) m.invoke(null, new Object[] { testData, false }); + assertEqual(testString, result); + } + { + Method m = c.getMethod("thisNotNewInstance2", byte[].class, boolean.class); + String result = (String) m.invoke(null, new Object[] { testData, true }); + assertEqual(testString, result); + result = (String) m.invoke(null, new Object[] { testData, false }); + assertEqual(testString, result); + } } } diff --git a/test/564-checker-bitcount/expected.txt b/test/564-checker-bitcount/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/564-checker-bitcount/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/564-checker-bitcount/info.txt b/test/564-checker-bitcount/info.txt new file mode 100644 index 0000000000..57db66b8ca --- /dev/null +++ b/test/564-checker-bitcount/info.txt @@ -0,0 +1 @@ +Unit test for 32-bit and 64-bit bit count operation. diff --git a/test/564-checker-bitcount/src/Main.java b/test/564-checker-bitcount/src/Main.java new file mode 100644 index 0000000000..b250145ef3 --- /dev/null +++ b/test/564-checker-bitcount/src/Main.java @@ -0,0 +1,90 @@ +/* + * 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 { + + // TODO: make this work when b/26700769 is done. + // + // CHECK-START-X86_64: int Main.bits32(int) disassembly (after) + // CHECK-DAG: popcnt + // + // CHECK-START-X86_64: int Main.bits32(int) disassembly (after) + // CHECK-NOT: call + private static int bits32(int x) { + return Integer.bitCount(x); + } + + // TODO: make this work when b/26700769 is done. + // + // CHECK-START-X86_64: int Main.bits64(long) disassembly (after) + // CHECK-DAG: popcnt + // + // CHECK-START-X86_64: int Main.bits64(long) disassembly (after) + // CHECK-NOT: call + private static int bits64(long x) { + return Long.bitCount(x); + } + + public static void main(String args[]) { + expectEquals32(bits32(0x00000000), 0); + expectEquals32(bits32(0x00000001), 1); + expectEquals32(bits32(0x10000000), 1); + expectEquals32(bits32(0x10000001), 2); + expectEquals32(bits32(0x00000003), 2); + expectEquals32(bits32(0x70000000), 3); + expectEquals32(bits32(0x000F0000), 4); + expectEquals32(bits32(0x00001111), 4); + expectEquals32(bits32(0x11110000), 4); + expectEquals32(bits32(0x11111111), 8); + expectEquals32(bits32(0x12345678), 13); + expectEquals32(bits32(0x9ABCDEF0), 19); + expectEquals32(bits32(0xFFFFFFFF), 32); + + for (int i = 0; i < 32; i++) { + expectEquals32(bits32(1 << i), 1); + } + + expectEquals64(bits64(0x0000000000000000L), 0); + expectEquals64(bits64(0x0000000000000001L), 1); + expectEquals64(bits64(0x1000000000000000L), 1); + expectEquals64(bits64(0x1000000000000001L), 2); + expectEquals64(bits64(0x0000000000000003L), 2); + expectEquals64(bits64(0x7000000000000000L), 3); + expectEquals64(bits64(0x000F000000000000L), 4); + expectEquals64(bits64(0x0000000011111111L), 8); + expectEquals64(bits64(0x1111111100000000L), 8); + expectEquals64(bits64(0x1111111111111111L), 16); + expectEquals64(bits64(0x123456789ABCDEF1L), 33); + expectEquals64(bits64(0xFFFFFFFFFFFFFFFFL), 64); + + for (int i = 0; i < 64; i++) { + expectEquals64(bits64(1L << i), 1); + } + + 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/564-checker-inline-loop/expected.txt b/test/564-checker-inline-loop/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/564-checker-inline-loop/expected.txt diff --git a/test/564-checker-inline-loop/info.txt b/test/564-checker-inline-loop/info.txt new file mode 100644 index 0000000000..a590bc6f23 --- /dev/null +++ b/test/564-checker-inline-loop/info.txt @@ -0,0 +1 @@ +Tests inlining of loops in the optimizing compiler. diff --git a/test/564-checker-inline-loop/src/Main.java b/test/564-checker-inline-loop/src/Main.java new file mode 100644 index 0000000000..6929913864 --- /dev/null +++ b/test/564-checker-inline-loop/src/Main.java @@ -0,0 +1,64 @@ +/* + * 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.inlineLoop() inliner (before) + /// CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect + /// CHECK-DAG: Return [<<Invoke>>] + + /// CHECK-START: int Main.inlineLoop() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + /// CHECK-START: int Main.inlineLoop() inliner (after) + /// CHECK-DAG: <<Constant:i\d+>> IntConstant 42 + /// CHECK-DAG: Return [<<Constant>>] + + /// CHECK-START: int Main.inlineLoop() licm (after) + /// CHECK: Goto loop:{{B\d+}} + + public static int inlineLoop() { + return loopMethod(); + } + + /// CHECK-START: void Main.inlineWithinLoop() inliner (before) + /// CHECK: InvokeStaticOrDirect + + /// CHECK-START: void Main.inlineWithinLoop() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + /// CHECK-START: void Main.inlineWithinLoop() licm (after) + /// CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none + /// CHECK-DAG: Goto outer_loop:<<OuterLoop>> + + public static void inlineWithinLoop() { + while (doLoop) { + loopMethod(); + } + } + + public static int loopMethod() { + while (doLoop) {} + return 42; + } + + public static boolean doLoop = false; + + public static void main(String[] args) { + inlineLoop(); + inlineWithinLoop(); + } +} diff --git a/test/564-checker-irreducible-loop/expected.txt b/test/564-checker-irreducible-loop/expected.txt new file mode 100644 index 0000000000..d81cc0710e --- /dev/null +++ b/test/564-checker-irreducible-loop/expected.txt @@ -0,0 +1 @@ +42 diff --git a/test/564-checker-irreducible-loop/info.txt b/test/564-checker-irreducible-loop/info.txt new file mode 100644 index 0000000000..1e0dd02284 --- /dev/null +++ b/test/564-checker-irreducible-loop/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing in the presence of +an irreducible loop. diff --git a/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali new file mode 100644 index 0000000000..b82ed921a5 --- /dev/null +++ b/test/564-checker-irreducible-loop/smali/IrreducibleLoop.smali @@ -0,0 +1,61 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LIrreducibleLoop; + +.super Ljava/lang/Object; + +## CHECK-START-X86: int IrreducibleLoop.simpleLoop(int) dead_code_elimination (before) +## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod +## CHECK-DAG: <<Constant:i\d+>> IntConstant 42 +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:{{B\d+}} irreducible:true +## CHECK-DAG: InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none +.method public static simpleLoop(I)I + .registers 3 + const/16 v0, 42 + if-eqz p0, :loop_entry + goto :other_loop_pre_entry + + # The then part: beginning of the irreducible loop. + :loop_entry + if-nez p0, :exit + invoke-static {v0},LIrreducibleLoop;->$noinline$m(I)V + :other_loop_entry + goto :loop_entry + + # The else part: a block uses the ArtMethod and branches to + # a block that doesn't. The register allocator used to trip there, as the + # ArtMethod was a live_in of the last block before the loop, but did not have + # a location due to our liveness analysis. + :other_loop_pre_entry + if-eqz p0, :other_loop_entry + invoke-static {v0},LIrreducibleLoop;->$noinline$m(I)V + goto :other_loop_entry + + :exit + return v0 +.end method + +.method public static $noinline$m(I)V + .registers 3 + const/16 v0, 0 + sget-boolean v1,LIrreducibleLoop;->doThrow:Z + if-eqz v1, :exit + # Prevent inlining. + throw v0 + :exit + return-void +.end method + +.field public static doThrow:Z diff --git a/test/564-checker-irreducible-loop/src/Main.java b/test/564-checker-irreducible-loop/src/Main.java new file mode 100644 index 0000000000..94e3357e5d --- /dev/null +++ b/test/564-checker-irreducible-loop/src/Main.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; + +public class Main { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("IrreducibleLoop"); + Method m = c.getMethod("simpleLoop", int.class); + Object[] arguments = { 42 }; + System.out.println(m.invoke(null, arguments)); + } +} diff --git a/test/960-default-smali/run b/test/960-default-smali/run deleted file mode 100755 index 22f68006e7..0000000000 --- a/test/960-default-smali/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} --experimental default-methods "$@" diff --git a/test/961-default-iface-resolution-generated/run b/test/961-default-iface-resolution-generated/run deleted file mode 100755 index 22f68006e7..0000000000 --- a/test/961-default-iface-resolution-generated/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} --experimental default-methods "$@" diff --git a/test/962-iface-static/run b/test/962-iface-static/run deleted file mode 100755 index d37737f3da..0000000000 --- a/test/962-iface-static/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} --experimental default-methods "$@" diff --git a/test/963-default-range-smali/run b/test/963-default-range-smali/run deleted file mode 100755 index d37737f3da..0000000000 --- a/test/963-default-range-smali/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} --experimental default-methods "$@" diff --git a/test/965-default-verify/run b/test/965-default-verify/run deleted file mode 100755 index 8944ea92d3..0000000000 --- a/test/965-default-verify/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} "$@" --experimental default-methods diff --git a/test/966-default-conflict/run b/test/966-default-conflict/run deleted file mode 100755 index 8944ea92d3..0000000000 --- a/test/966-default-conflict/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} "$@" --experimental default-methods diff --git a/test/967-default-ame/run b/test/967-default-ame/run deleted file mode 100755 index 8944ea92d3..0000000000 --- a/test/967-default-ame/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} "$@" --experimental default-methods diff --git a/test/968-default-partial-compile-generated/run b/test/968-default-partial-compile-generated/run deleted file mode 100755 index 6d2930d463..0000000000 --- a/test/968-default-partial-compile-generated/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} "$@" --experimental default-methods diff --git a/test/969-iface-super/run b/test/969-iface-super/run deleted file mode 100755 index 8944ea92d3..0000000000 --- a/test/969-iface-super/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} "$@" --experimental default-methods diff --git a/test/970-iface-super-resolution-generated/run b/test/970-iface-super-resolution-generated/run deleted file mode 100755 index 6d2930d463..0000000000 --- a/test/970-iface-super-resolution-generated/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} "$@" --experimental default-methods diff --git a/test/971-iface-super/run b/test/971-iface-super/run deleted file mode 100755 index 6d2930d463..0000000000 --- a/test/971-iface-super/run +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# -# 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. - -${RUN} "$@" --experimental default-methods diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 586c80528e..429af6a9e3 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -70,6 +70,7 @@ $$(dmart_target): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(TARGET_JACK_CLASSPATH_DEPE $(hide) DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \ SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \ DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ JACK_CLASSPATH=$(TARGET_JACK_CLASSPATH) \ JILL_JAR=$(abspath $(JILL_JAR)) \ @@ -524,16 +525,22 @@ endif TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := +# Tests that should fail in the read barrier configuration with the interpreter. +# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154). +# 141: Occasional failures: "Aborted" (b/25866001). +TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := \ + 004-ThreadStress \ + 141-class-unload -# Tests that should fail in the read barrier configuration with the default (Quick) compiler. -# 137: Quick has no support for read barriers and punts to the -# interpreter, but CFI unwinding expects managed frames. -# 554: Quick does not support JIT profiling. +# 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 +# tests expected to fail with the interpreter, both on the concurrent collector and in general. TEST_ART_BROKEN_DEFAULT_READ_BARRIER_RUN_TESTS := \ - 137-cfi \ - 554-jit-profile-file + $(TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS) \ + $(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS) -# Tests that should fail in the read barrier configuration with the Optimizing compiler. +# Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). +# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154). # 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 @@ -541,16 +548,28 @@ 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 := \ + 004-ThreadStress \ 484-checker-register-hints \ 527-checker-array-access-split \ 537-checker-arraycopy # Tests that should fail in the read barrier configuration with JIT. +# 004: Occasional timeout: "TEST TIMED OUT!" (b/26786154). # 141: Disabled because of intermittent failures on the ART Builtbot (b/25866001). +# 496: Occasional timeout: "Fault message: timeout: the monitored command dumped core" (b/26786304). TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \ - 141-class-unload + 004-ThreadStress \ + 141-class-unload \ + 496-checker-inlining-and-class-loader ifeq ($(ART_USE_READ_BARRIER),true) + ifneq (,$(filter interpreter,$(COMPILER_TYPES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ + $(PREBUILD_TYPES),interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ + $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ + $(TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) + endif + ifneq (,$(filter default,$(COMPILER_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ $(PREBUILD_TYPES),default,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ @@ -967,6 +986,7 @@ $$(run_test_rule_name): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(HOST_OUT_EXECUTABLES JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \ SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \ DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ + JACK_VERSION=$(JACK_DEFAULT_VERSION) \ JACK=$(abspath $(JACK)) \ JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \ JILL_JAR=$(abspath $(JILL_JAR)) \ diff --git a/test/ProfileTestMultiDex/Main.java b/test/ProfileTestMultiDex/Main.java new file mode 100644 index 0000000000..41532ea8f7 --- /dev/null +++ b/test/ProfileTestMultiDex/Main.java @@ -0,0 +1,27 @@ +/* + * 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 Main { + public String getA() { + return "A"; + } + public String getB() { + return "B"; + } + public String getC() { + return "C"; + } +} diff --git a/test/ProfileTestMultiDex/Second.java b/test/ProfileTestMultiDex/Second.java new file mode 100644 index 0000000000..4ac5abc300 --- /dev/null +++ b/test/ProfileTestMultiDex/Second.java @@ -0,0 +1,27 @@ +/* + * 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 Second { + public String getX() { + return "X"; + } + public String getY() { + return "Y"; + } + public String getZ() { + return "Z"; + } +} diff --git a/test/ProfileTestMultiDex/main.jpp b/test/ProfileTestMultiDex/main.jpp new file mode 100644 index 0000000000..f2e3b4e14c --- /dev/null +++ b/test/ProfileTestMultiDex/main.jpp @@ -0,0 +1,3 @@ +main: + @@com.android.jack.annotations.ForceInMainDex + class Second diff --git a/test/ProfileTestMultiDex/main.list b/test/ProfileTestMultiDex/main.list new file mode 100644 index 0000000000..44ba78ead5 --- /dev/null +++ b/test/ProfileTestMultiDex/main.list @@ -0,0 +1 @@ +Main.class diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index e004b6cff4..63ec2a03db 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -74,6 +74,14 @@ while true; do fi ARGS="${ARGS} $1" shift + elif [ "x$1" = "x--args" ]; then + shift + if [ "x$1" = "x" ]; then + echo "$0 missing argument to --args" 1>&2 + exit 1 + fi + ARGS="${ARGS} $1" + shift elif [ "x$1" = "x-Xcompiler-option" ]; then shift option="$1" diff --git a/test/run-test b/test/run-test index 033e2c6467..faa597e1ca 100755 --- a/test/run-test +++ b/test/run-test @@ -733,7 +733,10 @@ fi # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB. build_file_size_limit=2048 run_file_size_limit=2048 -if echo "$test_dir" | grep -Eq "(083|089|964|971)" > /dev/null; then + +# Add tests requiring a higher ulimit to this list. Ulimits might need to be raised to deal with +# large amounts of expected output or large generated files. +if echo "$test_dir" | grep -Eq "(083|089|961|964|971)" > /dev/null; then build_file_size_limit=5120 run_file_size_limit=5120 fi diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 351e99e8d4..6d67f8477d 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -260,6 +260,7 @@ "dalvik.system.JniTest#testPassingShorts", "dalvik.system.JniTest#testPassingThis", "libcore.util.NativeAllocationRegistryTest#testBadSize", + "libcore.util.NativeAllocationRegistryTest#testEarlyFree", "libcore.util.NativeAllocationRegistryTest#testNativeAllocationAllocatorAndNoSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNativeAllocationAllocatorAndSharedRegistry", "libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndNoSharedRegistry", diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt index 6ea83d2777..95d12928f1 100644 --- a/tools/libcore_failures_concurrent_collector.txt +++ b/tools/libcore_failures_concurrent_collector.txt @@ -34,5 +34,13 @@ "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"], + bug: 26711853 } ] |