diff options
68 files changed, 4462 insertions, 2767 deletions
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 294072d7e7..792f508199 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -99,25 +99,6 @@ inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField( return std::make_pair(fast_get, fast_put); } -inline ArtMethod* CompilerDriver::ResolveMethod( - ScopedObjectAccess& soa, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, - const DexCompilationUnit* mUnit, - uint32_t method_idx, - InvokeType invoke_type) { - DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); - ArtMethod* resolved_method = - mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>( - method_idx, dex_cache, class_loader, /* referrer */ nullptr, invoke_type); - if (UNLIKELY(resolved_method == nullptr)) { - DCHECK(soa.Self()->IsExceptionPending()); - // Clean up any exception left by type resolution. - soa.Self()->ClearException(); - } - return resolved_method; -} - inline VerificationResults* CompilerDriver::GetVerificationResults() const { DCHECK(Runtime::Current()->IsAotCompiler()); return verification_results_; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index f8e2dff25a..f7bff4d336 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -258,7 +258,6 @@ CompilerDriver::CompilerDriver( had_hard_verifier_failure_(false), parallel_thread_count_(thread_count), stats_(new AOTCompilationStats), - compiler_context_(nullptr), compiled_method_storage_(swap_fd), max_arena_alloc_(0), dex_to_dex_compiler_(this) { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index b0f2dac683..94efdd8304 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -145,7 +145,6 @@ class CompilerDriver { bool GetCompiledClass(const ClassReference& ref, ClassStatus* status) const; CompiledMethod* GetCompiledMethod(MethodReference ref) const; - size_t GetNonRelativeLinkerPatchCount() const; // Add a compiled method. void AddCompiledMethod(const MethodReference& method_ref, CompiledMethod* const compiled_method); CompiledMethod* RemoveCompiledMethod(const MethodReference& method_ref); @@ -180,16 +179,6 @@ class CompilerDriver { uint16_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a method. Returns null on failure, including incompatible class change. - ArtMethod* ResolveMethod( - ScopedObjectAccess& soa, - Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, - const DexCompilationUnit* mUnit, - uint32_t method_idx, - InvokeType invoke_type) - REQUIRES_SHARED(Locks::mutator_lock_); - void ProcessedInstanceField(bool resolved); void ProcessedStaticField(bool resolved, bool local); @@ -208,14 +197,6 @@ class CompilerDriver { const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const; bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc); - void SetCompilerContext(void* compiler_context) { - compiler_context_ = compiler_context; - } - - void* GetCompilerContext() const { - return compiler_context_; - } - size_t GetThreadCount() const { return parallel_thread_count_; } @@ -358,6 +339,12 @@ class CompilerDriver { void FreeThreadPools(); void CheckThreadPools(); + // Resolve const string literals that are loaded from dex code. If only_startup_strings is + // specified, only methods that are marked startup in the profile are resolved. + void ResolveConstStrings(const std::vector<const DexFile*>& dex_files, + bool only_startup_strings, + /*inout*/ TimingLogger* timings); + const CompilerOptions* const compiler_options_; VerificationResults* const verification_results_; @@ -372,13 +359,6 @@ class CompilerDriver { typedef AtomicDexRefMap<MethodReference, CompiledMethod*> MethodTable; - private: - // Resolve const string literals that are loaded from dex code. If only_startup_strings is - // specified, only methods that are marked startup in the profile are resolved. - void ResolveConstStrings(const std::vector<const DexFile*>& dex_files, - bool only_startup_strings, - /*inout*/ TimingLogger* timings); - // All method references that this compiler has compiled. MethodTable compiled_methods_; @@ -407,11 +387,6 @@ class CompilerDriver { class AOTCompilationStats; std::unique_ptr<AOTCompilationStats> stats_; - typedef void (*CompilerCallbackFn)(CompilerDriver& driver); - typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver); - - void* compiler_context_; - CompiledMethodStorage compiled_method_storage_; size_t max_arena_alloc_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a9acf90762..48b50ea5d6 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2319,9 +2319,10 @@ void LocationsBuilderARM64::VisitArrayGet(HArrayGet* instruction) { if (offset >= kReferenceLoadMinFarOffset) { locations->AddTemp(FixedTempLocation()); } - } else { + } else if (!instruction->GetArray()->IsIntermediateAddress()) { // We need a non-scratch temporary for the array data pointer in - // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(). + // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier() for the case with no + // intermediate address. locations->AddTemp(Location::RequiresRegister()); } } @@ -2351,11 +2352,12 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(masm); - // The read barrier instrumentation of object ArrayGet instructions + // The non-Baker read barrier instrumentation of object ArrayGet instructions // does not support the HIntermediateAddress instruction. DCHECK(!((type == DataType::Type::kReference) && instruction->GetArray()->IsIntermediateAddress() && - kEmitCompilerReadBarrier)); + kEmitCompilerReadBarrier && + !kUseBakerReadBarrier)); if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // Object ArrayGet with Baker's read barrier case. @@ -2363,6 +2365,7 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call. DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0))); if (index.IsConstant()) { + DCHECK(!instruction->GetArray()->IsIntermediateAddress()); // Array load with a constant index can be treated as a field load. offset += Int64FromLocation(index) << DataType::SizeShift(type); Location maybe_temp = @@ -2375,9 +2378,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { /* needs_null_check */ false, /* use_load_acquire */ false); } else { - Register temp = WRegisterFrom(locations->GetTemp(0)); codegen_->GenerateArrayLoadWithBakerReadBarrier( - out, obj.W(), offset, index, temp, /* needs_null_check */ false); + instruction, out, obj.W(), offset, index, /* needs_null_check */ false); } } else { // General case. @@ -2426,8 +2428,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { // input instruction has done it already. See the comment in // `TryExtractArrayAccessAddress()`. if (kIsDebugBuild) { - HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress(); - DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset); + HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress(); + DCHECK_EQ(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset); } temp = obj; } else { @@ -2539,8 +2541,8 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { // input instruction has done it already. See the comment in // `TryExtractArrayAccessAddress()`. if (kIsDebugBuild) { - HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress(); - DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset); + HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress(); + DCHECK(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset); } temp = array; } else { @@ -5957,11 +5959,11 @@ void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* ins instruction, ref, obj, src, needs_null_check, use_load_acquire); } -void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(Location ref, +void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HArrayGet* instruction, + Location ref, Register obj, uint32_t data_offset, Location index, - Register temp, bool needs_null_check) { DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); @@ -6000,9 +6002,24 @@ void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(Location ref, DCHECK(temps.IsAvailable(ip0)); DCHECK(temps.IsAvailable(ip1)); temps.Exclude(ip0, ip1); + + Register temp; + if (instruction->GetArray()->IsIntermediateAddress()) { + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress(); + DCHECK_EQ(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + } + temp = obj; + } else { + temp = WRegisterFrom(instruction->GetLocations()->GetTemp(0)); + __ Add(temp.X(), obj.X(), Operand(data_offset)); + } + uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode()); - __ Add(temp.X(), obj.X(), Operand(data_offset)); { ExactAssemblyScope guard(GetVIXLAssembler(), (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize); diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 1ba58b1a9f..ada5742fc0 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -694,11 +694,11 @@ class CodeGeneratorARM64 : public CodeGenerator { 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(Location ref, + void GenerateArrayLoadWithBakerReadBarrier(HArrayGet* instruction, + Location ref, vixl::aarch64::Register obj, uint32_t data_offset, Location index, - vixl::aarch64::Register temp, bool needs_null_check); // Emit code checking the status of the Marking Register, and diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index 24fbb6cb4c..f968c19cf9 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -202,6 +202,11 @@ void InstructionSimplifierArmVisitor::VisitArrayGet(HArrayGet* instruction) { return; } + // TODO: Support intermediate address for object arrays on arm. + if (type == DataType::Type::kReference) { + return; + } + if (type == DataType::Type::kInt64 || type == DataType::Type::kFloat32 || type == DataType::Type::kFloat64) { diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc index ccdcb3532d..0f30f662cd 100644 --- a/compiler/optimizing/instruction_simplifier_shared.cc +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -245,11 +245,11 @@ bool TryExtractArrayAccessAddress(HInstruction* access, return false; } if (kEmitCompilerReadBarrier && + !kUseBakerReadBarrier && access->IsArrayGet() && access->GetType() == DataType::Type::kReference) { - // For object arrays, the read barrier instrumentation requires + // For object arrays, the non-Baker read barrier instrumentation requires // the original array pointer. - // TODO: This can be relaxed for Baker CC. return false; } diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 7684dc79f2..6d04b0e9d9 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -2916,6 +2916,40 @@ void IntrinsicLocationsBuilderARM64::VisitReachabilityFence(HInvoke* invoke) { void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { } +void IntrinsicLocationsBuilderARM64::VisitCRC32Update(HInvoke* invoke) { + if (!codegen_->GetInstructionSetFeatures().HasCRC()) { + return; + } + + LocationSummary* locations = new (allocator_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +// Lower the invoke of CRC32.update(int crc, int b). +void IntrinsicCodeGeneratorARM64::VisitCRC32Update(HInvoke* invoke) { + DCHECK(codegen_->GetInstructionSetFeatures().HasCRC()); + + MacroAssembler* masm = GetVIXLAssembler(); + + Register crc = InputRegisterAt(invoke, 0); + Register val = InputRegisterAt(invoke, 1); + Register out = OutputRegister(invoke); + + // The general algorithm of the CRC32 calculation is: + // crc = ~crc + // result = crc32_for_byte(crc, b) + // crc = ~result + // It is directly lowered to three instructions. + __ Mvn(out, crc); + __ Crc32b(out, out, val); + __ Mvn(out, out); +} + UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf); diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 38e4c8968a..0c463a280e 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -3059,6 +3059,7 @@ UNIMPLEMENTED_INTRINSIC(ARMVIXL, MathRoundDouble) // Could be done by changing UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar) UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update) UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 6f7f5e49c1..21fb7d7f1c 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2696,6 +2696,8 @@ UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong) UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy) +UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update) + UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter); UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 2eb252908c..4b86f5d423 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -2346,6 +2346,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitReachabilityFence(HInvoke* invoke ATTRIB UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy) +UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update) UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 3504d7a6f8..6dd4681847 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -2967,6 +2967,7 @@ UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit) UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit) UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit) UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit) +UNIMPLEMENTED_INTRINSIC(X86, CRC32Update) UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 96f6eaaf33..7db26dc9be 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -2732,6 +2732,7 @@ void IntrinsicCodeGeneratorX86_64::VisitReachabilityFence(HInvoke* invoke ATTRIB UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite) UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite) +UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update) UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf); UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter); diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index 053e202523..3d262969a4 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -125,7 +125,7 @@ void DumpAndCheck(std::vector<uint8_t>& code, const char* testname, const char* // Assemble the .S snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename); int cmd_result = system(cmd); - ASSERT_EQ(cmd_result, 0) << strerror(errno); + ASSERT_EQ(cmd_result, 0) << cmd << strerror(errno); // Disassemble. snprintf(cmd, sizeof(cmd), "%sobjdump -D -M force-thumb --section=.text %s.o | grep '^ *[0-9a-f][0-9a-f]*:'", diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index db610f1f4f..50785c5fce 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1045,8 +1045,8 @@ class Dex2Oat final { } void InsertCompileOptions(int argc, char** argv) { - std::ostringstream oss; if (!avoid_storing_invocation_) { + std::ostringstream oss; for (int i = 0; i < argc; ++i) { if (i > 0) { oss << ' '; @@ -1055,9 +1055,9 @@ class Dex2Oat final { } key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str()); oss.str(""); // Reset. + oss << kRuntimeISA; + key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str()); } - oss << kRuntimeISA; - key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str()); key_value_store_->Put( OatHeader::kDebuggableKey, compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); @@ -1513,15 +1513,6 @@ class Dex2Oat final { std::vector<gc::space::ImageSpace*> image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum(); - // 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 = android::base::Join(image_filenames, ':'); - if (!image_file_location.empty()) { - key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location); - } } else { image_file_location_oat_checksum_ = 0u; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index d22b301585..baeebd9371 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -23,6 +23,7 @@ #include <unistd.h> #include <android-base/logging.h> +#include <android-base/macros.h> #include <android-base/stringprintf.h> #include "common_runtime_test.h" @@ -90,6 +91,10 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { args.push_back("--runtime-arg"); args.push_back("-Xnorelocate"); + // Unless otherwise stated, use a small amount of threads, so that potential aborts are + // shorter. This can be overridden with extra_args. + args.push_back("-j4"); + args.insert(args.end(), extra_args.begin(), extra_args.end()); int status = Dex2Oat(args, error_msg); @@ -99,33 +104,33 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { return status; } - void GenerateOdexForTest( + ::testing::AssertionResult GenerateOdexForTest( const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter, const std::vector<std::string>& extra_args = {}, bool expect_success = true, - bool use_fd = false) { - GenerateOdexForTest(dex_location, - odex_location, - filter, - extra_args, - expect_success, - use_fd, - [](const OatFile&) {}); + bool use_fd = false) WARN_UNUSED { + return GenerateOdexForTest(dex_location, + odex_location, + filter, + extra_args, + expect_success, + use_fd, + [](const OatFile&) {}); } bool test_accepts_odex_file_on_failure = false; template <typename T> - void GenerateOdexForTest( + ::testing::AssertionResult GenerateOdexForTest( const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter, const std::vector<std::string>& extra_args, bool expect_success, bool use_fd, - T check_oat) { + T check_oat) WARN_UNUSED { std::string error_msg; int status = GenerateOdexForTestWithStatus({dex_location}, odex_location, @@ -135,7 +140,10 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { use_fd); bool success = (WIFEXITED(status) && WEXITSTATUS(status) == 0); if (expect_success) { - ASSERT_TRUE(success) << error_msg << std::endl << output_; + if (!success) { + return ::testing::AssertionFailure() + << "Failed to compile odex: " << error_msg << std::endl << output_; + } // Verify the odex file was generated as expected. std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, @@ -146,12 +154,16 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { dex_location.c_str(), /*reservation=*/ nullptr, &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + if (odex_file == nullptr) { + return ::testing::AssertionFailure() << "Could not open odex file: " << error_msg; + } CheckFilter(filter, odex_file->GetCompilerFilter()); check_oat(*(odex_file.get())); } else { - ASSERT_FALSE(success) << output_; + if (success) { + return ::testing::AssertionFailure() << "Succeeded to compile odex: " << output_; + } error_msg_ = error_msg; @@ -165,9 +177,12 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { dex_location.c_str(), /*reservation=*/ nullptr, &error_msg)); - ASSERT_TRUE(odex_file.get() == nullptr); + if (odex_file != nullptr) { + return ::testing::AssertionFailure() << "Could open odex file: " << error_msg; + } } } + return ::testing::AssertionSuccess(); } // Check the input compiler filter against the generated oat file's filter. May be overridden @@ -265,7 +280,7 @@ class Dex2oatSwapTest : public Dex2oatTest { std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap"; copy.push_back("--swap-file=" + swap_location); } - GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy); + ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy)); CheckValidity(); ASSERT_TRUE(success_); @@ -490,7 +505,7 @@ class Dex2oatVeryLargeTest : public Dex2oatTest { std::vector<std::string> new_args(extra_args); new_args.push_back("--app-image-file=" + app_image_file); - GenerateOdexForTest(dex_location, odex_location, filter, new_args); + ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, filter, new_args)); CheckValidity(); ASSERT_TRUE(success_); @@ -681,12 +696,12 @@ class Dex2oatLayoutTest : public Dex2oatTest { copy.push_back("--app-image-file=" + app_image_file_name); } } - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kSpeedProfile, - copy, - expect_success, - use_fd); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kSpeedProfile, + copy, + expect_success, + use_fd)); if (app_image_file != nullptr) { ASSERT_EQ(app_image_file->FlushCloseOrErase(), 0) << "Could not flush and close art file"; } @@ -877,24 +892,24 @@ class Dex2oatUnquickenTest : public Dex2oatTest { { std::string input_vdex = "--input-vdex-fd=-1"; std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd()); - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kQuicken, - { input_vdex, output_vdex }, - /*expect_success=*/ true, - /*use_fd=*/ true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kQuicken, + { input_vdex, output_vdex }, + /* expect_success= */ true, + /* use_fd= */ true)); EXPECT_GT(vdex_file1->GetLength(), 0u); } // Unquicken by running the verify compiler filter on the vdex file. { std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd()); std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd()); - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kVerify, - { input_vdex, output_vdex, kDisableCompactDex }, - /*expect_success=*/ true, - /*use_fd=*/ true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kVerify, + { input_vdex, output_vdex, kDisableCompactDex }, + /* expect_success= */ true, + /* use_fd= */ true)); } ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; CheckResult(dex_location, odex_location); @@ -918,12 +933,12 @@ class Dex2oatUnquickenTest : public Dex2oatTest { { std::string input_vdex = "--input-vdex-fd=-1"; std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd()); - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kQuicken, - { input_vdex, output_vdex, "--compact-dex-level=fast"}, - /*expect_success=*/ true, - /*use_fd=*/ true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kQuicken, + { input_vdex, output_vdex, "--compact-dex-level=fast"}, + /* expect_success= */ true, + /* use_fd= */ true)); EXPECT_GT(vdex_file1->GetLength(), 0u); } @@ -931,12 +946,12 @@ class Dex2oatUnquickenTest : public Dex2oatTest { { std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd()); std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file2->Fd()); - GenerateOdexForTest(dex_location, - odex_location2, - CompilerFilter::kVerify, - { input_vdex, output_vdex, "--compact-dex-level=none"}, - /*expect_success=*/ true, - /*use_fd=*/ true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location2, + CompilerFilter::kVerify, + { input_vdex, output_vdex, "--compact-dex-level=none"}, + /* expect_success= */ true, + /* use_fd= */ true)); } ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; ASSERT_EQ(vdex_file2->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; @@ -992,11 +1007,11 @@ class Dex2oatWatchdogTest : public Dex2oatTest { std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap"; copy.push_back("--swap-file=" + swap_location); copy.push_back("-j512"); // Excessive idle threads just slow down dex2oat. - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kSpeed, - copy, - expect_success); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kSpeed, + copy, + expect_success)); } std::string GetTestDexFileName() { @@ -1072,13 +1087,13 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { ASSERT_STREQ(expected_classpath_key, classpath); }; - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kQuicken, - extra_args, - expected_success, - /*use_fd*/ false, - check_oat); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kQuicken, + extra_args, + expected_success, + /*use_fd*/ false, + check_oat)); } std::string GetUsedDexLocation() { @@ -1135,11 +1150,11 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFilesBackedByOdex) { Copy(GetDexSrc1(), stripped_classpath); - GenerateOdexForTest(stripped_classpath, - odex_for_classpath, - CompilerFilter::kQuicken, - {}, - true); + ASSERT_TRUE(GenerateOdexForTest(stripped_classpath, + odex_for_classpath, + CompilerFilter::kQuicken, + {}, + true)); // Strip the dex file Copy(GetStrippedDexSrc1(), stripped_classpath); @@ -1216,7 +1231,7 @@ TEST_F(Dex2oatDeterminism, UnloadCompile) { CompilerFilter::Filter::kQuicken, &error_msg, {"--force-determinism", "--avoid-storing-invocation"}); - EXPECT_EQ(res, 0); + ASSERT_EQ(res, 0); Copy(base_oat_name, unload_oat_name); Copy(base_vdex_name, unload_vdex_name); std::unique_ptr<File> unload_oat(OS::OpenFileForReading(unload_oat_name.c_str())); @@ -1233,7 +1248,7 @@ TEST_F(Dex2oatDeterminism, UnloadCompile) { CompilerFilter::Filter::kQuicken, &error_msg, {"--force-determinism", "--avoid-storing-invocation", "--app-image-file=" + app_image_name}); - EXPECT_EQ(res2, 0); + ASSERT_EQ(res2, 0); Copy(base_oat_name, no_unload_oat_name); Copy(base_vdex_name, no_unload_vdex_name); std::unique_ptr<File> no_unload_oat(OS::OpenFileForReading(no_unload_oat_name.c_str())); @@ -1535,26 +1550,26 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; size_t no_dedupe_size = 0; - GenerateOdexForTest(dex->GetLocation(), - base_oat_name, - CompilerFilter::Filter::kSpeed, - { "--deduplicate-code=false" }, - true, // expect_success - false, // use_fd - [&no_dedupe_size](const OatFile& o) { - no_dedupe_size = o.Size(); - }); + ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), + base_oat_name, + CompilerFilter::Filter::kSpeed, + { "--deduplicate-code=false" }, + true, // expect_success + false, // use_fd + [&no_dedupe_size](const OatFile& o) { + no_dedupe_size = o.Size(); + })); size_t dedupe_size = 0; - GenerateOdexForTest(dex->GetLocation(), - base_oat_name, - CompilerFilter::Filter::kSpeed, - { "--deduplicate-code=true" }, - true, // expect_success - false, // use_fd - [&dedupe_size](const OatFile& o) { - dedupe_size = o.Size(); - }); + ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), + base_oat_name, + CompilerFilter::Filter::kSpeed, + { "--deduplicate-code=true" }, + true, // expect_success + false, // use_fd + [&dedupe_size](const OatFile& o) { + dedupe_size = o.Size(); + })); EXPECT_LT(dedupe_size, no_dedupe_size); } @@ -1563,15 +1578,15 @@ TEST_F(Dex2oatTest, UncompressedTest) { std::unique_ptr<const DexFile> dex(OpenTestDexFile("MainUncompressed")); std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; - GenerateOdexForTest(dex->GetLocation(), - base_oat_name, - CompilerFilter::Filter::kQuicken, - { }, - true, // expect_success - false, // use_fd - [](const OatFile& o) { - CHECK(!o.ContainsDexCode()); - }); + ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(), + base_oat_name, + CompilerFilter::Filter::kQuicken, + { }, + true, // expect_success + false, // use_fd + [](const OatFile& o) { + CHECK(!o.ContainsDexCode()); + })); } TEST_F(Dex2oatTest, EmptyUncompressedDexTest) { @@ -1667,15 +1682,15 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailure) { std::string out_dir = GetScratchDir(); const std::string oat_filename = out_dir + "/base.oat"; // The dex won't pass the method verifier, only use the verify filter. - GenerateOdexForTest(temp_dex.GetFilename(), - oat_filename, - CompilerFilter::Filter::kVerify, - { }, - true, // expect_success - false, // use_fd - [](const OatFile& o) { - CHECK(o.ContainsDexCode()); - }); + ASSERT_TRUE(GenerateOdexForTest(temp_dex.GetFilename(), + oat_filename, + CompilerFilter::Filter::kVerify, + { }, + true, // expect_success + false, // use_fd + [](const OatFile& o) { + CHECK(o.ContainsDexCode()); + })); // Open our generated oat file. std::string error_msg; std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, @@ -1718,11 +1733,11 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailureMultiDex) { } const std::string& dex_location = apk_file.GetFilename(); const std::string odex_location = GetOdexDir() + "/output.odex"; - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kQuicken, - { "--compact-dex-level=fast" }, - true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kQuicken, + { "--compact-dex-level=fast" }, + true)); } TEST_F(Dex2oatTest, StderrLoggerOutput) { @@ -1732,11 +1747,11 @@ TEST_F(Dex2oatTest, StderrLoggerOutput) { // Test file doesn't matter. Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kQuicken, - { "--runtime-arg", "-Xuse-stderr-logger" }, - true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kQuicken, + { "--runtime-arg", "-Xuse-stderr-logger" }, + true)); // Look for some random part of dex2oat logging. With the stderr logger this should be captured, // even on device. EXPECT_NE(std::string::npos, output_.find("dex2oat took")); @@ -1749,11 +1764,11 @@ TEST_F(Dex2oatTest, VerifyCompilationReason) { // Test file doesn't matter. Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kVerify, - { "--compilation-reason=install" }, - true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kVerify, + { "--compilation-reason=install" }, + true)); std::string error_msg; std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, odex_location.c_str(), @@ -1774,11 +1789,11 @@ TEST_F(Dex2oatTest, VerifyNoCompilationReason) { // Test file doesn't matter. Copy(GetDexSrc1(), dex_location); - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kVerify, - {}, - true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kVerify, + {}, + true)); std::string error_msg; std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, odex_location.c_str(), @@ -1799,14 +1814,13 @@ TEST_F(Dex2oatTest, DontExtract) { const std::string dex_location = dex->GetLocation(); const std::string odex_location = out_dir + "/base.oat"; const std::string vdex_location = out_dir + "/base.vdex"; - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::Filter::kVerify, - { "--copy-dex-files=false" }, - true, // expect_success - false, // use_fd - [](const OatFile&) { - }); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::Filter::kVerify, + { "--copy-dex-files=false" }, + true, // expect_success + false, // use_fd + [](const OatFile&) {})); { // Check the vdex doesn't have dex. std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location.c_str(), @@ -1856,19 +1870,21 @@ TEST_F(Dex2oatTest, DontExtract) { } auto generate_and_check = [&](CompilerFilter::Filter filter) { - GenerateOdexForTest(dex_location, - odex_location, - filter, - { "--dump-timings", - "--dm-file=" + dm_file.GetFilename(), - // Pass -Xuse-stderr-logger have dex2oat output in output_ on target. - "--runtime-arg", - "-Xuse-stderr-logger" }, - true, // expect_success - false, // use_fd - [](const OatFile& o) { - CHECK(o.ContainsDexCode()); - }); + output_.clear(); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + filter, + { "--dump-timings", + "--dm-file=" + dm_file.GetFilename(), + // Pass -Xuse-stderr-logger have dex2oat output in output_ on + // target. + "--runtime-arg", + "-Xuse-stderr-logger" }, + true, // expect_success + false, // use_fd + [](const OatFile& o) { + CHECK(o.ContainsDexCode()); + })); // Check the output for "Fast verify", this is printed from --dump-timings. std::istringstream iss(output_); std::string line; @@ -1916,14 +1932,14 @@ TEST_F(Dex2oatTest, QuickenedInput) { { std::string input_vdex = "--input-vdex-fd=-1"; std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_output->Fd()); - GenerateOdexForTest(dex_location, - odex_location, - CompilerFilter::kQuicken, - // Disable cdex since we want to compare against the original dex file - // after unquickening. - { input_vdex, output_vdex, kDisableCompactDex }, - /*expect_success=*/ true, - /*use_fd=*/ true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kQuicken, + // Disable cdex since we want to compare against the original + // dex file after unquickening. + { input_vdex, output_vdex, kDisableCompactDex }, + /* expect_success= */ true, + /* use_fd= */ true)); } // Unquicken by running the verify compiler filter on the vdex file and verify it matches. std::string odex_location2 = GetOdexDir() + "/unquickened.odex"; @@ -1932,13 +1948,14 @@ TEST_F(Dex2oatTest, QuickenedInput) { { std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_output->Fd()); std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_unquickened->Fd()); - GenerateOdexForTest(dex_location, - odex_location2, - CompilerFilter::kVerify, - // Disable cdex to avoid needing to write out the shared section. - { input_vdex, output_vdex, kDisableCompactDex }, - /*expect_success=*/ true, - /*use_fd=*/ true); + ASSERT_TRUE(GenerateOdexForTest(dex_location, + odex_location2, + CompilerFilter::kVerify, + // Disable cdex to avoid needing to write out the shared + // section. + { input_vdex, output_vdex, kDisableCompactDex }, + /* expect_success= */ true, + /* use_fd= */ true)); } ASSERT_EQ(vdex_unquickened->Flush(), 0) << "Could not flush and close vdex file"; ASSERT_TRUE(success_); @@ -2046,13 +2063,13 @@ TEST_F(Dex2oatTest, AppImageNoProfile) { ScratchFile app_image_file; const std::string out_dir = GetScratchDir(); const std::string odex_location = out_dir + "/base.odex"; - GenerateOdexForTest(GetTestDexFileName("ManyMethods"), - odex_location, - CompilerFilter::Filter::kSpeedProfile, - { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) }, - true, // expect_success - false, // use_fd - [](const OatFile&) {}); + ASSERT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"), + odex_location, + CompilerFilter::Filter::kSpeedProfile, + { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) }, + true, // expect_success + false, // use_fd + [](const OatFile&) {})); // Open our generated oat file. std::string error_msg; std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, @@ -2105,15 +2122,15 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) { const std::string out_dir = GetScratchDir(); const std::string odex_location = out_dir + "/base.odex"; const std::string app_image_location = out_dir + "/base.art"; - GenerateOdexForTest(GetTestDexFileName("StringLiterals"), - odex_location, - CompilerFilter::Filter::kSpeedProfile, - { "--app-image-file=" + app_image_location, - "--resolve-startup-const-strings=true", - "--profile-file=" + profile_file.GetFilename()}, - /*expect_success=*/ true, - /*use_fd=*/ false, - [](const OatFile&) {}); + ASSERT_TRUE(GenerateOdexForTest(GetTestDexFileName("StringLiterals"), + odex_location, + CompilerFilter::Filter::kSpeedProfile, + { "--app-image-file=" + app_image_location, + "--resolve-startup-const-strings=true", + "--profile-file=" + profile_file.GetFilename()}, + /* expect_success= */ true, + /* use_fd= */ false, + [](const OatFile&) {})); // Open our generated oat file. std::string error_msg; std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1, @@ -2220,27 +2237,27 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) { } expected_stored_context += + "]"; // The class path should not be valid and should fail being stored. - GenerateOdexForTest(GetTestDexFileName("ManyMethods"), - odex_location, - CompilerFilter::Filter::kQuicken, - { "--class-loader-context=" + stored_context }, - true, // expect_success - false, // use_fd - [&](const OatFile& oat_file) { + EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"), + odex_location, + CompilerFilter::Filter::kQuicken, + { "--class-loader-context=" + stored_context }, + true, // expect_success + false, // use_fd + [&](const OatFile& oat_file) { EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) << output_; EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) << output_; - }); + })); // The stored context should match what we expect even though it's invalid. - GenerateOdexForTest(GetTestDexFileName("ManyMethods"), - odex_location, - CompilerFilter::Filter::kQuicken, - { "--class-loader-context=" + valid_context, - "--stored-class-loader-context=" + stored_context }, - true, // expect_success - false, // use_fd - [&](const OatFile& oat_file) { + EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"), + odex_location, + CompilerFilter::Filter::kQuicken, + { "--class-loader-context=" + valid_context, + "--stored-class-loader-context=" + stored_context }, + true, // expect_success + false, // use_fd + [&](const OatFile& oat_file) { EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_; - }); + })); } } // namespace art diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 01c24fc197..28287bd083 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -766,10 +766,6 @@ void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) { bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u; CHECK_EQ(dex_files_->size(), oat_dex_files_.size()); - if (GetCompilerOptions().IsBootImage()) { - CHECK_EQ(image_writer_ != nullptr, - oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr); - } write_state_ = WriteState::kWriteRoData; } diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 83fb17cf05..af02bfc8bd 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -394,7 +394,6 @@ TEST_F(OatTest, WriteRead) { ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "lue.art"); bool success = WriteElf(tmp_vdex.GetFile(), tmp_oat.GetFile(), class_linker->GetBootClassPath(), @@ -418,7 +417,6 @@ TEST_F(OatTest, WriteRead) { ASSERT_TRUE(oat_header.IsValid()); ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount()); // core ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum()); - ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey))); ASSERT_TRUE(java_lang_dex_file_ != nullptr); const DexFile& dex_file = *java_lang_dex_file_; @@ -517,7 +515,6 @@ TEST_F(OatTest, EmptyTextSection) { ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); bool success = WriteElf(tmp_vdex.GetFile(), tmp_oat.GetFile(), dex_files, @@ -584,7 +581,6 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex"); SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr); success = WriteElf(tmp_vdex.GetFile(), @@ -714,7 +710,6 @@ void OatTest::TestZipFileInput(bool verify) { 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() }; @@ -831,7 +826,6 @@ void OatTest::TestZipFileInputWithEmptyDex() { ASSERT_TRUE(success) << strerror(errno); SafeMap<std::string, std::string> key_value_store; - key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() }; ScratchFile oat_file, vdex_file(oat_file, ".vdex"); std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(new ProfileCompilationInfo()); diff --git a/disassembler/Android.bp b/disassembler/Android.bp index 241b191641..a7c1802515 100644 --- a/disassembler/Android.bp +++ b/disassembler/Android.bp @@ -20,11 +20,39 @@ art_cc_defaults { host_supported: true, srcs: [ "disassembler.cc", - "disassembler_arm.cc", - "disassembler_arm64.cc", "disassembler_mips.cc", "disassembler_x86.cc", ], + codegen: { + arm: { + srcs: ["disassembler_arm.cc"] + }, + arm64: { + srcs: ["disassembler_arm64.cc"] + }, + // TODO: We should also conditionally include the MIPS32/MIPS64 and the + // x86/x86-64 disassembler definitions (b/119090273). However, using the + // following syntax here: + // + // mips: { + // srcs: ["disassembler_mips.cc"] + // }, + // mips64: { + // srcs: ["disassembler_mips.cc"] + // }, + // x86: { + // srcs: ["disassembler_x86.cc"] + // }, + // x86_64: { + // srcs: ["disassembler_x86.cc"] + // }, + // + // does not work, as it generates a file rejected by ninja with this + // error message (e.g. on host, where we include all the back ends by + // default): + // + // FAILED: ninja: out/soong/build.ninja:320768: multiple rules generate out/soong/.intermediates/art/disassembler/libart-disassembler/linux_glibc_x86_64_static/obj/art/disassembler/disassembler_mips.o [-w dupbuild=err] + }, include_dirs: ["art/runtime"], shared_libs: [ diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc index 262e8152fb..aee690e036 100644 --- a/disassembler/disassembler.cc +++ b/disassembler/disassembler.cc @@ -21,10 +21,21 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "disassembler_arm.h" -#include "disassembler_arm64.h" -#include "disassembler_mips.h" -#include "disassembler_x86.h" +#ifdef ART_ENABLE_CODEGEN_arm +# include "disassembler_arm.h" +#endif + +#ifdef ART_ENABLE_CODEGEN_arm64 +# include "disassembler_arm64.h" +#endif + +#if defined(ART_ENABLE_CODEGEN_mips) || defined(ART_ENABLE_CODEGEN_mips64) +# include "disassembler_mips.h" +#endif + +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) +# include "disassembler_x86.h" +#endif using android::base::StringPrintf; @@ -36,21 +47,35 @@ Disassembler::Disassembler(DisassemblerOptions* disassembler_options) } Disassembler* Disassembler::Create(InstructionSet instruction_set, DisassemblerOptions* options) { - if (instruction_set == InstructionSet::kArm || instruction_set == InstructionSet::kThumb2) { - return new arm::DisassemblerArm(options); - } else if (instruction_set == InstructionSet::kArm64) { - return new arm64::DisassemblerArm64(options); - } else if (instruction_set == InstructionSet::kMips) { - return new mips::DisassemblerMips(options, /* is_o32_abi= */ true); - } else if (instruction_set == InstructionSet::kMips64) { - return new mips::DisassemblerMips(options, /* is_o32_abi= */ false); - } else if (instruction_set == InstructionSet::kX86) { - return new x86::DisassemblerX86(options, false); - } else if (instruction_set == InstructionSet::kX86_64) { - return new x86::DisassemblerX86(options, true); - } else { - UNIMPLEMENTED(FATAL) << static_cast<uint32_t>(instruction_set); - return nullptr; + switch (instruction_set) { +#ifdef ART_ENABLE_CODEGEN_arm + case InstructionSet::kArm: + case InstructionSet::kThumb2: + return new arm::DisassemblerArm(options); +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + case InstructionSet::kArm64: + return new arm64::DisassemblerArm64(options); +#endif +#ifdef ART_ENABLE_CODEGEN_mips + case InstructionSet::kMips: + return new mips::DisassemblerMips(options, /* is_o32_abi= */ true); +#endif +#ifdef ART_ENABLE_CODEGEN_mips64 + case InstructionSet::kMips64: + return new mips::DisassemblerMips(options, /* is_o32_abi= */ false); +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + case InstructionSet::kX86: + return new x86::DisassemblerX86(options, /* supports_rex= */ false); +#endif +#ifdef ART_ENABLE_CODEGEN_x86_64 + case InstructionSet::kX86_64: + return new x86::DisassemblerX86(options, /* supports_rex= */ true); +#endif + default: + UNIMPLEMENTED(FATAL) << static_cast<uint32_t>(instruction_set); + return nullptr; } } diff --git a/libartbase/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc index f7c9c5e264..c9173076d1 100644 --- a/libartbase/base/file_utils_test.cc +++ b/libartbase/base/file_utils_test.cc @@ -83,11 +83,15 @@ TEST_F(FileUtilsTest, GetAndroidRootSafe) { ASSERT_EQ(0, unsetenv("ANDROID_ROOT")); std::string android_root3 = GetAndroidRootSafe(&error_msg); // This should be the same as the other root (modulo realpath), otherwise the test setup is - // broken. - UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr)); + // broken. On non-bionic. On bionic we can be running with a different libart that lives outside + // of ANDROID_ROOT UniqueCPtr<char> real_root3(realpath(android_root3.c_str(), nullptr)); +#if !defined(__BIONIC__ ) || defined(__ANDROID__) + UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr)); EXPECT_STREQ(real_root.get(), real_root3.get()); - +#else + EXPECT_STRNE(real_root3.get(), ""); +#endif // Reset ANDROID_ROOT, as other things may depend on it. ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), /* overwrite */ 1)); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index e9c17eeab5..d8da9129ed 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -402,6 +402,7 @@ inline hiddenapi::ApiList ArtMethod::GetHiddenApiAccessFlags() case Intrinsics::kUnsafeLoadFence: case Intrinsics::kUnsafeStoreFence: case Intrinsics::kUnsafeFullFence: + case Intrinsics::kCRC32Update: // These intrinsics are on the light greylist and will fail a DCHECK in // SetIntrinsic() if their flags change on the respective dex methods. // Note that the DCHECK currently won't fail if the dex methods are diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 9ba52c429d..67513f9ce6 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -965,9 +965,6 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { runtime->GetOatFileManager().RegisterImageOatFiles(spaces); DCHECK(!oat_files.empty()); const OatHeader& default_oat_header = oat_files[0]->GetOatHeader(); - const char* image_file_location = oat_files[0]->GetOatHeader(). - GetStoreValueByKey(OatHeader::kImageLocationKey); - CHECK(image_file_location == nullptr || *image_file_location == 0); quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline(); quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline(); quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline(); @@ -2034,11 +2031,15 @@ bool ClassLinker::AddImageSpace( } if (app_image) { AppImageLoadingHelper::Update(this, space, class_loader, dex_caches, &temp_set); - // Update class loader and resolved strings. If added_class_table is false, the resolved - // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. - UpdateClassLoaderVisitor visitor(space, class_loader.Get()); - for (const ClassTable::TableSlot& root : temp_set) { - visitor(root.Read()); + + { + ScopedTrace trace("AppImage:UpdateClassLoaders"); + // Update class loader and resolved strings. If added_class_table is false, the resolved + // strings were forwarded UpdateAppImageClassLoadersAndDexCaches. + UpdateClassLoaderVisitor visitor(space, class_loader.Get()); + for (const ClassTable::TableSlot& root : temp_set) { + visitor(root.Read()); + } } if (kBitstringSubtypeCheckEnabled) { @@ -4011,33 +4012,31 @@ ObjPtr<mirror::Class> ClassLinker::CreateArrayClass(Thread* self, return existing; } -ObjPtr<mirror::Class> ClassLinker::FindPrimitiveClass(char type) { - ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots = GetClassRoots(); +ObjPtr<mirror::Class> ClassLinker::LookupPrimitiveClass(char type) { + ClassRoot class_root; switch (type) { - case 'B': - return GetClassRoot(ClassRoot::kPrimitiveByte, class_roots); - case 'C': - return GetClassRoot(ClassRoot::kPrimitiveChar, class_roots); - case 'D': - return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots); - case 'F': - return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots); - case 'I': - return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots); - case 'J': - return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots); - case 'S': - return GetClassRoot(ClassRoot::kPrimitiveShort, class_roots); - case 'Z': - return GetClassRoot(ClassRoot::kPrimitiveBoolean, class_roots); - case 'V': - return GetClassRoot(ClassRoot::kPrimitiveVoid, class_roots); + case 'B': class_root = ClassRoot::kPrimitiveByte; break; + case 'C': class_root = ClassRoot::kPrimitiveChar; break; + case 'D': class_root = ClassRoot::kPrimitiveDouble; break; + case 'F': class_root = ClassRoot::kPrimitiveFloat; break; + case 'I': class_root = ClassRoot::kPrimitiveInt; break; + case 'J': class_root = ClassRoot::kPrimitiveLong; break; + case 'S': class_root = ClassRoot::kPrimitiveShort; break; + case 'Z': class_root = ClassRoot::kPrimitiveBoolean; break; + case 'V': class_root = ClassRoot::kPrimitiveVoid; break; default: - break; + return nullptr; } - std::string printable_type(PrintableChar(type)); - ThrowNoClassDefFoundError("Not a primitive type: %s", printable_type.c_str()); - return nullptr; + return GetClassRoot(class_root, this); +} + +ObjPtr<mirror::Class> ClassLinker::FindPrimitiveClass(char type) { + ObjPtr<mirror::Class> result = LookupPrimitiveClass(type); + if (UNLIKELY(result == nullptr)) { + std::string printable_type(PrintableChar(type)); + ThrowNoClassDefFoundError("Not a primitive type: %s", printable_type.c_str()); + } + return result; } ObjPtr<mirror::Class> ClassLinker::InsertClass(const char* descriptor, @@ -8033,7 +8032,7 @@ ObjPtr<mirror::Class> ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, if (descriptor[1] == '\0') { // only the descriptors of primitive types should be 1 character long, also avoid class lookup // for primitive classes that aren't backed by dex files. - type = FindPrimitiveClass(descriptor[0]); + type = LookupPrimitiveClass(descriptor[0]); } else { Thread* const self = Thread::Current(); DCHECK(self != nullptr); @@ -8562,7 +8561,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( switch (handle_type) { case DexFile::MethodHandleType::kStaticPut: { method_params->Set(0, target_field->ResolveType()); - return_type = hs.NewHandle(FindPrimitiveClass('V')); + return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this)); break; } case DexFile::MethodHandleType::kStaticGet: { @@ -8572,7 +8571,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( case DexFile::MethodHandleType::kInstancePut: { method_params->Set(0, target_field->GetDeclaringClass()); method_params->Set(1, target_field->ResolveType()); - return_type = hs.NewHandle(FindPrimitiveClass('V')); + return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this)); break; } case DexFile::MethodHandleType::kInstanceGet: { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 7afd575607..60e68d5f78 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -196,6 +196,7 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr<mirror::Class> LookupPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr<mirror::Class> FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_); void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::classlinker_classes_lock_); diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index e0bbf43622..26a8d1310b 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -1658,7 +1658,9 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { << " type=" << to_ref->PrettyTypeOf() << " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha << " space=" << heap_->DumpSpaceNameFromAddress(to_ref) - << " region_type=" << rtype; + << " region_type=" << rtype + // TODO: Temporary; remove this when this is no longer needed (b/116087961). + << " runtime->sentinel=" << Runtime::Current()->GetSentinel().Read<kWithoutReadBarrier>(); } bool add_to_live_bytes = false; // Invariant: There should be no object from a newly-allocated @@ -1702,7 +1704,9 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { << " type=" << to_ref->PrettyTypeOf() << " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha << " space=" << heap_->DumpSpaceNameFromAddress(to_ref) - << " region_type=" << rtype; + << " region_type=" << rtype + // TODO: Temporary; remove this when this is no longer needed (b/116087961). + << " runtime->sentinel=" << Runtime::Current()->GetSentinel().Read<kWithoutReadBarrier>(); } #ifdef USE_BAKER_OR_BROOKS_READ_BARRIER mirror::Object* referent = nullptr; diff --git a/runtime/image.cc b/runtime/image.cc index 376742afbc..59ac283779 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '6', '\0' }; // Add metadata section. +const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '7', '\0' }; // Added CRC32 intrinsic ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc index 17b3cd45aa..24a026a92e 100644 --- a/runtime/interpreter/interpreter_intrinsics.cc +++ b/runtime/interpreter/interpreter_intrinsics.cc @@ -558,6 +558,7 @@ bool MterpHandleIntrinsic(ShadowFrame* shadow_frame, UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */) UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */) UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */) + UNIMPLEMENTED_CASE(CRC32Update /* (II)I */) INTRINSIC_CASE(VarHandleFullFence) INTRINSIC_CASE(VarHandleAcquireFence) INTRINSIC_CASE(VarHandleReleaseFence) diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h index 48e1728c8b..71b2336cb0 100644 --- a/runtime/interpreter/interpreter_switch_impl-inl.h +++ b/runtime/interpreter/interpreter_switch_impl-inl.h @@ -23,6 +23,7 @@ #include "base/memory_tool.h" #include "base/quasi_atomic.h" #include "dex/dex_file_types.h" +#include "dex/dex_instruction_list.h" #include "experimental_flags.h" #include "interpreter_common.h" #include "jit/jit.h" @@ -36,6 +37,7 @@ namespace art { namespace interpreter { +// TODO: Replace macros with member functions. #define CHECK_FORCE_RETURN() \ { \ if (UNLIKELY(shadow_frame.GetForcePopFrame())) { \ @@ -46,12 +48,13 @@ namespace interpreter { SendMethodExitEvents(self, \ instrumentation, \ shadow_frame, \ - shadow_frame.GetThisObject(accessor.InsSize()), \ + shadow_frame.GetThisObject(Accessor().InsSize()), \ shadow_frame.GetMethod(), \ - inst->GetDexPc(insns), \ + inst->GetDexPc(Insns()), \ JValue()); \ } \ ctx->result = JValue(); /* Handled in caller. */ \ + exit_interpreter_loop = true; \ return; \ } \ } \ @@ -65,18 +68,19 @@ namespace interpreter { if (!MoveToExceptionHandler(self, shadow_frame, instr)) { \ /* Structured locking is to be enforced for abnormal termination, too. */ \ DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame); \ - if (interpret_one_instruction) { \ + if (ctx->interpret_one_instruction) { \ /* Signal mterp to return to caller */ \ shadow_frame.SetDexPC(dex::kDexNoIndex); \ } \ ctx->result = JValue(); /* Handled in caller. */ \ + exit_interpreter_loop = true; \ return; \ } else { \ CHECK_FORCE_RETURN(); \ int32_t displacement = \ static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc); \ inst = inst->RelativeAt(displacement); \ - break; /* Stop executing this opcode and continue in the exception handler. */ \ + return; /* Stop executing this opcode and continue in the exception handler. */ \ } \ } \ do {} while (false) @@ -138,13 +142,12 @@ namespace interpreter { CHECK_FORCE_RETURN(); \ if (UNLIKELY(instrumentation->HasDexPcListeners())) { \ if (UNLIKELY(!DoDexPcMoveEvent(self, \ - accessor, \ + Accessor(), \ shadow_frame, \ dex_pc, \ instrumentation, \ save_ref))) { \ HANDLE_PENDING_EXCEPTION(); \ - break; \ } \ CHECK_FORCE_RETURN(); \ } \ @@ -164,11 +167,12 @@ namespace interpreter { dex_pc, \ offset, \ &result)) { \ - if (interpret_one_instruction) { \ + if (ctx->interpret_one_instruction) { \ /* OSR has completed execution of the method. Signal mterp to return to caller */ \ shadow_frame.SetDexPC(dex::kDexNoIndex); \ } \ ctx->result = result; \ + exit_interpreter_loop = true; \ return; \ } \ } \ @@ -176,6 +180,7 @@ namespace interpreter { #define HOTNESS_UPDATE() \ { \ + jit::Jit* jit = Runtime::Current()->GetJit(); \ if (jit != nullptr) { \ jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges=*/ true); \ } \ @@ -193,7 +198,7 @@ namespace interpreter { if (IsBackwardBranch(offset)) { \ HOTNESS_UPDATE(); \ /* Record new dex pc early to have consistent suspend point at loop header. */ \ - shadow_frame.SetDexPC(inst->GetDexPc(insns)); \ + shadow_frame.SetDexPC(inst->GetDexPc(Insns())); \ self->AllowThreadSuspension(); \ } \ } \ @@ -268,6 +273,2460 @@ NO_INLINE static bool SendMethodExitEvents(Thread* self, } } +// Short-lived helper class which executes single DEX bytecode. It is inlined by compiler. +// +// The function names must match the names from dex_instruction_list.h and have no arguments. +// +// Any relevant execution information is stored in the fields - it should be kept to minimum. +// +template<bool do_access_check, bool transaction_active> +class InstructionHandler { + public: + ALWAYS_INLINE void NOP() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MOVE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MOVE_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22x(inst_data), + shadow_frame.GetVReg(inst->VRegB_22x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MOVE_16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_32x(), + shadow_frame.GetVReg(inst->VRegB_32x())); + inst = inst->Next_3xx(); + } + + ALWAYS_INLINE void MOVE_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MOVE_WIDE_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_22x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MOVE_WIDE_16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_32x(), + shadow_frame.GetVRegLong(inst->VRegB_32x())); + inst = inst->Next_3xx(); + } + + ALWAYS_INLINE void MOVE_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MOVE_OBJECT_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data), + shadow_frame.GetVRegReference(inst->VRegB_22x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MOVE_OBJECT_16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegReference(inst->VRegA_32x(), + shadow_frame.GetVRegReference(inst->VRegB_32x())); + inst = inst->Next_3xx(); + } + + ALWAYS_INLINE void MOVE_RESULT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_11x(inst_data), ResultRegister()->GetI()); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MOVE_RESULT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), ResultRegister()->GetJ()); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MOVE_RESULT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE_SAVE(ResultRegister()); + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), ResultRegister()->GetL()); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MOVE_EXCEPTION() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Throwable> exception = self->GetException(); + DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction"; + shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception); + self->ClearException(); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void RETURN_VOID_NO_BARRIER() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + JValue result; + self->AllowThreadSuspension(); + HANDLE_MONITOR_CHECKS(); + if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && + !SendMethodExitEvents(self, + instrumentation, + shadow_frame, + shadow_frame.GetThisObject(Accessor().InsSize()), + shadow_frame.GetMethod(), + inst->GetDexPc(Insns()), + result))) { + HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); + } + if (ctx->interpret_one_instruction) { + /* Signal mterp to return to caller */ + shadow_frame.SetDexPC(dex::kDexNoIndex); + } + ctx->result = result; + exit_interpreter_loop = true; + } + + ALWAYS_INLINE void RETURN_VOID() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + QuasiAtomic::ThreadFenceForConstructor(); + JValue result; + self->AllowThreadSuspension(); + HANDLE_MONITOR_CHECKS(); + if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && + !SendMethodExitEvents(self, + instrumentation, + shadow_frame, + shadow_frame.GetThisObject(Accessor().InsSize()), + shadow_frame.GetMethod(), + inst->GetDexPc(Insns()), + result))) { + HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); + } + if (ctx->interpret_one_instruction) { + /* Signal mterp to return to caller */ + shadow_frame.SetDexPC(dex::kDexNoIndex); + } + ctx->result = result; + exit_interpreter_loop = true; + } + + ALWAYS_INLINE void RETURN() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + JValue result; + result.SetJ(0); + result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); + self->AllowThreadSuspension(); + HANDLE_MONITOR_CHECKS(); + if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && + !SendMethodExitEvents(self, + instrumentation, + shadow_frame, + shadow_frame.GetThisObject(Accessor().InsSize()), + shadow_frame.GetMethod(), + inst->GetDexPc(Insns()), + result))) { + HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); + } + if (ctx->interpret_one_instruction) { + /* Signal mterp to return to caller */ + shadow_frame.SetDexPC(dex::kDexNoIndex); + } + ctx->result = result; + exit_interpreter_loop = true; + } + + ALWAYS_INLINE void RETURN_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + JValue result; + result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data))); + self->AllowThreadSuspension(); + HANDLE_MONITOR_CHECKS(); + if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && + !SendMethodExitEvents(self, + instrumentation, + shadow_frame, + shadow_frame.GetThisObject(Accessor().InsSize()), + shadow_frame.GetMethod(), + inst->GetDexPc(Insns()), + result))) { + HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); + } + if (ctx->interpret_one_instruction) { + /* Signal mterp to return to caller */ + shadow_frame.SetDexPC(dex::kDexNoIndex); + } + ctx->result = result; + exit_interpreter_loop = true; + } + + ALWAYS_INLINE void RETURN_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + JValue result; + self->AllowThreadSuspension(); + HANDLE_MONITOR_CHECKS(); + const size_t ref_idx = inst->VRegA_11x(inst_data); + ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx); + if (do_assignability_check && obj_result != nullptr) { + ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType(); + // Re-load since it might have moved. + obj_result = shadow_frame.GetVRegReference(ref_idx); + if (return_type == nullptr) { + // Return the pending exception. + HANDLE_PENDING_EXCEPTION(); + } + if (!obj_result->VerifierInstanceOf(return_type)) { + // This should never happen. + std::string temp1, temp2; + self->ThrowNewExceptionF("Ljava/lang/InternalError;", + "Returning '%s' that is not instance of return type '%s'", + obj_result->GetClass()->GetDescriptor(&temp1), + return_type->GetDescriptor(&temp2)); + HANDLE_PENDING_EXCEPTION(); + } + } + result.SetL(obj_result); + if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && + !SendMethodExitEvents(self, + instrumentation, + shadow_frame, + shadow_frame.GetThisObject(Accessor().InsSize()), + shadow_frame.GetMethod(), + inst->GetDexPc(Insns()), + result))) { + HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); + } + // Re-load since it might have moved during the MethodExitEvent. + result.SetL(shadow_frame.GetVRegReference(ref_idx)); + if (ctx->interpret_one_instruction) { + /* Signal mterp to return to caller */ + shadow_frame.SetDexPC(dex::kDexNoIndex); + } + ctx->result = result; + exit_interpreter_loop = true; + } + + ALWAYS_INLINE void CONST_4() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t dst = inst->VRegA_11n(inst_data); + int4_t val = inst->VRegB_11n(inst_data); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, nullptr); + } + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void CONST_16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint8_t dst = inst->VRegA_21s(inst_data); + int16_t val = inst->VRegB_21s(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, nullptr); + } + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void CONST() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint8_t dst = inst->VRegA_31i(inst_data); + int32_t val = inst->VRegB_31i(); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, nullptr); + } + inst = inst->Next_3xx(); + } + + ALWAYS_INLINE void CONST_HIGH16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint8_t dst = inst->VRegA_21h(inst_data); + int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16); + shadow_frame.SetVReg(dst, val); + if (val == 0) { + shadow_frame.SetVRegReference(dst, nullptr); + } + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void CONST_WIDE_16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s()); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void CONST_WIDE_32() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i()); + inst = inst->Next_3xx(); + } + + ALWAYS_INLINE void CONST_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l()); + inst = inst->Next_51l(); + } + + ALWAYS_INLINE void CONST_WIDE_HIGH16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data), + static_cast<uint64_t>(inst->VRegB_21h()) << 48); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void CONST_STRING() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::String> s = ResolveString(self, + shadow_frame, + dex::StringIndex(inst->VRegB_21c())); + if (UNLIKELY(s == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void CONST_STRING_JUMBO() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::String> s = ResolveString(self, + shadow_frame, + dex::StringIndex(inst->VRegB_31c())); + if (UNLIKELY(s == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s); + inst = inst->Next_3xx(); + } + } + + ALWAYS_INLINE void CONST_CLASS() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), + shadow_frame.GetMethod(), + self, + false, + do_access_check); + if (UNLIKELY(c == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void CONST_METHOD_HANDLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self, + inst->VRegB_21c(), + shadow_frame.GetMethod()); + if (UNLIKELY(mh == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void CONST_METHOD_TYPE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self, + dex::ProtoIndex(inst->VRegB_21c()), + shadow_frame.GetMethod()); + if (UNLIKELY(mt == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void MONITOR_ENTER() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + HANDLE_ASYNC_EXCEPTION(); + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); + if (UNLIKELY(obj == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } else { + DoMonitorEnter<do_assignability_check>(self, &shadow_frame, obj); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + } + } + + ALWAYS_INLINE void MONITOR_EXIT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + HANDLE_ASYNC_EXCEPTION(); + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); + if (UNLIKELY(obj == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } else { + DoMonitorExit<do_assignability_check>(self, &shadow_frame, obj); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + } + } + + ALWAYS_INLINE void CHECK_CAST() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), + shadow_frame.GetMethod(), + self, + false, + do_access_check); + if (UNLIKELY(c == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data)); + if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) { + ThrowClassCastException(c, obj->GetClass()); + HANDLE_PENDING_EXCEPTION(); + } else { + inst = inst->Next_2xx(); + } + } + } + + ALWAYS_INLINE void INSTANCE_OF() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()), + shadow_frame.GetMethod(), + self, + false, + do_access_check); + if (UNLIKELY(c == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); + shadow_frame.SetVReg(inst->VRegA_22c(inst_data), + (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void ARRAY_LENGTH() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)); + if (UNLIKELY(array == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength()); + inst = inst->Next_1xx(); + } + } + + ALWAYS_INLINE void NEW_INSTANCE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> obj = nullptr; + ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), + shadow_frame.GetMethod(), + self, + false, + do_access_check); + if (LIKELY(c != nullptr)) { + if (UNLIKELY(c->IsStringClass())) { + gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); + obj = mirror::String::AllocEmptyString<true>(self, allocator_type); + } else { + obj = AllocObjectFromCode<true>( + c.Ptr(), + self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); + } + } + if (UNLIKELY(obj == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + obj->GetClass()->AssertInitializedOrInitializingInThread(self); + // Don't allow finalizable objects to be allocated during a transaction since these can't + // be finalized without a started runtime. + if (transaction_active && obj->GetClass()->IsFinalizable()) { + AbortTransactionF(self, "Allocating finalizable object in transaction: %s", + obj->PrettyTypeOf().c_str()); + HANDLE_PENDING_EXCEPTION(); + } + shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void NEW_ARRAY() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); + ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>( + dex::TypeIndex(inst->VRegC_22c()), + length, + shadow_frame.GetMethod(), + self, + Runtime::Current()->GetHeap()->GetCurrentAllocator()); + if (UNLIKELY(obj == nullptr)) { + HANDLE_PENDING_EXCEPTION(); + } else { + shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void FILLED_NEW_ARRAY() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = + DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self, + ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + } + + ALWAYS_INLINE void FILLED_NEW_ARRAY_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = + DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame, + self, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + } + + ALWAYS_INLINE void FILL_ARRAY_DATA() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t(); + const Instruction::ArrayDataPayload* payload = + reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr); + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data)); + bool success = FillArrayData(obj, payload); + if (!success) { + HANDLE_PENDING_EXCEPTION(); + } + if (transaction_active) { + RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count); + } + inst = inst->Next_3xx(); + } + + ALWAYS_INLINE void THROW() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + HANDLE_ASYNC_EXCEPTION(); + ObjPtr<mirror::Object> exception = + shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); + if (UNLIKELY(exception == nullptr)) { + ThrowNullPointerException("throw with null exception"); + } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) { + // This should never happen. + std::string temp; + self->ThrowNewExceptionF("Ljava/lang/InternalError;", + "Throwing '%s' that is not instance of Throwable", + exception->GetClass()->GetDescriptor(&temp)); + } else { + self->SetException(exception->AsThrowable()); + } + HANDLE_PENDING_EXCEPTION(); + } + + ALWAYS_INLINE void GOTO() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + HANDLE_ASYNC_EXCEPTION(); + int8_t offset = inst->VRegA_10t(inst_data); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } + + ALWAYS_INLINE void GOTO_16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + HANDLE_ASYNC_EXCEPTION(); + int16_t offset = inst->VRegA_20t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } + + ALWAYS_INLINE void GOTO_32() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + HANDLE_ASYNC_EXCEPTION(); + int32_t offset = inst->VRegA_30t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } + + ALWAYS_INLINE void PACKED_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } + + ALWAYS_INLINE void SPARSE_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" + + + ALWAYS_INLINE void CMPL_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x()); + float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x()); + int32_t result; + if (val1 > val2) { + result = 1; + } else if (val1 == val2) { + result = 0; + } else { + result = -1; + } + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void CMPG_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x()); + float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x()); + int32_t result; + if (val1 < val2) { + result = -1; + } else if (val1 == val2) { + result = 0; + } else { + result = 1; + } + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void CMPL_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x()); + double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x()); + int32_t result; + if (val1 > val2) { + result = 1; + } else if (val1 == val2) { + result = 0; + } else { + result = -1; + } + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); + inst = inst->Next_2xx(); + } + + + ALWAYS_INLINE void CMPG_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x()); + double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x()); + int32_t result; + if (val1 < val2) { + result = -1; + } else if (val1 == val2) { + result = 0; + } else { + result = 1; + } + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); + inst = inst->Next_2xx(); + } + +#pragma clang diagnostic pop + + + ALWAYS_INLINE void CMP_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x()); + int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x()); + int32_t result; + if (val1 > val2) { + result = 1; + } else if (val1 == val2) { + result = 0; + } else { + result = -1; + } + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void IF_EQ() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + 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); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_NE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + 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); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_LT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + 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); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_GE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + 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); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_GT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + 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); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_LE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + 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); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_EQZ() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) { + int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_NEZ() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) { + int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_LTZ() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) { + int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_GEZ() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) { + int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_GTZ() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) { + int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void IF_LEZ() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) { + int16_t offset = inst->VRegB_21t(); + BRANCH_INSTRUMENTATION(offset); + inst = inst->RelativeAt(offset); + HANDLE_BACKWARD_BRANCH(offset); + } else { + BRANCH_INSTRUMENTATION(2); + inst = inst->Next_2xx(); + } + } + + ALWAYS_INLINE void AGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray(); + if (array->CheckIsValidIndex(index)) { + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void AGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::ByteArray> array = a->AsByteArray(); + if (array->CheckIsValidIndex(index)) { + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void AGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::CharArray> array = a->AsCharArray(); + if (array->CheckIsValidIndex(index)) { + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void AGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::ShortArray> array = a->AsShortArray(); + if (array->CheckIsValidIndex(index)) { + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void AGET() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf(); + ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a); + if (array->CheckIsValidIndex(index)) { + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void AGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf(); + ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a); + if (array->CheckIsValidIndex(index)) { + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void AGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>(); + if (array->CheckIsValidIndex(index)) { + shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void APUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray(); + if (array->CheckIsValidIndex(index)) { + array->SetWithoutChecks<transaction_active>(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void APUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::ByteArray> array = a->AsByteArray(); + if (array->CheckIsValidIndex(index)) { + array->SetWithoutChecks<transaction_active>(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void APUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::CharArray> array = a->AsCharArray(); + if (array->CheckIsValidIndex(index)) { + array->SetWithoutChecks<transaction_active>(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void APUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::ShortArray> array = a->AsShortArray(); + if (array->CheckIsValidIndex(index)) { + array->SetWithoutChecks<transaction_active>(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void APUT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf(); + ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a); + if (array->CheckIsValidIndex(index)) { + array->SetWithoutChecks<transaction_active>(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void APUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data)); + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf(); + ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a); + if (array->CheckIsValidIndex(index)) { + array->SetWithoutChecks<transaction_active>(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void APUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); + if (UNLIKELY(a == nullptr)) { + ThrowNullPointerExceptionFromInterpreter(); + HANDLE_PENDING_EXCEPTION(); + } + int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); + ObjPtr<mirror::Object> val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data)); + ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>(); + if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) { + array->SetWithoutChecks<transaction_active>(index, val); + inst = inst->Next_2xx(); + } else { + HANDLE_PENDING_EXCEPTION(); + } + } + + ALWAYS_INLINE void IGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>( + self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>( + self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>( + self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>( + self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>( + self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>( + self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>( + self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_WIDE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_OBJECT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_BOOLEAN_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_BYTE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_CHAR_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IGET_SHORT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SGET() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>( + shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_BOOLEAN_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>( + shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_BYTE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>( + shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_CHAR_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>( + shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_SHORT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>( + shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_WIDE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>( + shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void IPUT_OBJECT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>( + shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SPUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SPUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SPUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SPUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SPUT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SPUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SPUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, + transaction_active>(self, shadow_frame, inst, inst_data); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void INVOKE_VIRTUAL() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_VIRTUAL_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_SUPER() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kSuper, false, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_SUPER_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kSuper, true, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_DIRECT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kDirect, false, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_DIRECT_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kDirect, true, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_INTERFACE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kInterface, false, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_INTERFACE_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kInterface, true, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_STATIC() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kStatic, false, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_STATIC_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kStatic, true, do_access_check, /*is_mterp=*/ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_VIRTUAL_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false, + /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_VIRTUAL_RANGE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false, + /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_POLYMORPHIC() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + bool success = DoInvokePolymorphic</* is_range= */ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success); + } + + ALWAYS_INLINE void INVOKE_POLYMORPHIC_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + bool success = DoInvokePolymorphic</* is_range= */ true>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success); + } + + ALWAYS_INLINE void INVOKE_CUSTOM() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + bool success = DoInvokeCustom</* is_range= */ false>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void INVOKE_CUSTOM_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + bool success = DoInvokeCustom</* is_range= */ true>( + self, shadow_frame, inst, inst_data, ResultRegister()); + POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); + } + + ALWAYS_INLINE void NEG_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg( + inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void NOT_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg( + inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void NEG_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong( + inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void NOT_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong( + inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void NEG_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat( + inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void NEG_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble( + inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void INT_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void INT_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void INT_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void LONG_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void LONG_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void LONG_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void FLOAT_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); + int32_t result = art_float_to_integral<int32_t, float>(val); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void FLOAT_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); + int64_t result = art_float_to_integral<int64_t, float>(val); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void FLOAT_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void DOUBLE_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); + int32_t result = art_float_to_integral<int32_t, double>(val); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void DOUBLE_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); + int64_t result = art_float_to_integral<int64_t, double>(val); + shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void DOUBLE_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void INT_TO_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int8_t>( + shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void INT_TO_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<uint16_t>( + shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void INT_TO_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int16_t>( + shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void ADD_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()), + shadow_frame.GetVReg(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SUB_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()), + shadow_frame.GetVReg(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MUL_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()), + shadow_frame.GetVReg(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void DIV_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data), + shadow_frame.GetVReg(inst->VRegB_23x()), + shadow_frame.GetVReg(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void REM_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data), + shadow_frame.GetVReg(inst->VRegB_23x()), + shadow_frame.GetVReg(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void SHL_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + shadow_frame.GetVReg(inst->VRegB_23x()) << + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SHR_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + shadow_frame.GetVReg(inst->VRegB_23x()) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void USHR_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void AND_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + shadow_frame.GetVReg(inst->VRegB_23x()) & + shadow_frame.GetVReg(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void OR_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + shadow_frame.GetVReg(inst->VRegB_23x()) | + shadow_frame.GetVReg(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void XOR_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_23x(inst_data), + shadow_frame.GetVReg(inst->VRegB_23x()) ^ + shadow_frame.GetVReg(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void ADD_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SUB_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MUL_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void DIV_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx); + } + + ALWAYS_INLINE void REM_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()), + shadow_frame.GetVRegLong(inst->VRegC_23x())); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx); + } + + ALWAYS_INLINE void AND_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()) & + shadow_frame.GetVRegLong(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void OR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()) | + shadow_frame.GetVRegLong(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void XOR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()) ^ + shadow_frame.GetVRegLong(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SHL_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()) << + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SHR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegLong(inst->VRegB_23x()) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void USHR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), + static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >> + (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void ADD_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) + + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SUB_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) - + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MUL_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) * + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void DIV_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegFloat(inst->VRegB_23x()) / + shadow_frame.GetVRegFloat(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void REM_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), + fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()), + shadow_frame.GetVRegFloat(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void ADD_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) + + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SUB_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) - + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MUL_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) * + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void DIV_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), + shadow_frame.GetVRegDouble(inst->VRegB_23x()) / + shadow_frame.GetVRegDouble(inst->VRegC_23x())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void REM_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), + fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()), + shadow_frame.GetVRegDouble(inst->VRegC_23x()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void ADD_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, SafeAdd(shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void SUB_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + SafeSub(shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MUL_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + SafeMul(shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void DIV_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); + } + + ALWAYS_INLINE void REM_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); + } + + ALWAYS_INLINE void SHL_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) << + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void SHR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) >> + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void USHR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >> + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void AND_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) & + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void OR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) | + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void XOR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVReg(vregA, + shadow_frame.GetVReg(vregA) ^ + shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void ADD_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + SafeAdd(shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void SUB_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + SafeSub(shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MUL_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + SafeMul(shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void DIV_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + } + + ALWAYS_INLINE void REM_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); + } + + ALWAYS_INLINE void AND_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) & + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void OR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) | + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void XOR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) ^ + shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void SHL_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) << + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void SHR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + shadow_frame.GetVRegLong(vregA) >> + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void USHR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegLong(vregA, + static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >> + (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void ADD_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) + + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void SUB_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) - + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MUL_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) * + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void DIV_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegFloat(vregA, + shadow_frame.GetVRegFloat(vregA) / + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void REM_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegFloat(vregA, + fmodf(shadow_frame.GetVRegFloat(vregA), + shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void ADD_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) + + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void SUB_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) - + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void MUL_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) * + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void DIV_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegDouble(vregA, + shadow_frame.GetVRegDouble(vregA) / + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void REM_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + uint4_t vregA = inst->VRegA_12x(inst_data); + shadow_frame.SetVRegDouble(vregA, + fmod(shadow_frame.GetVRegDouble(vregA), + shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)))); + inst = inst->Next_1xx(); + } + + ALWAYS_INLINE void ADD_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), + inst->VRegC_22s())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void RSUB_INT() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + SafeSub(inst->VRegC_22s(), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MUL_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), + inst->VRegC_22s())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void DIV_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), + inst->VRegC_22s()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void REM_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), + inst->VRegC_22s()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void AND_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) & + inst->VRegC_22s()); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void OR_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) | + inst->VRegC_22s()); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void XOR_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22s(inst_data), + shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^ + inst->VRegC_22s()); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void ADD_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void RSUB_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + SafeSub(inst->VRegC_22b(), shadow_frame.GetVReg(inst->VRegB_22b()))); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void MUL_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b())); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void DIV_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void REM_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); + } + + ALWAYS_INLINE void AND_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()) & + inst->VRegC_22b()); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void OR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()) | + inst->VRegC_22b()); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void XOR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()) ^ + inst->VRegC_22b()); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SHL_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()) << + (inst->VRegC_22b() & 0x1f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void SHR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + shadow_frame.GetVReg(inst->VRegB_22b()) >> + (inst->VRegC_22b() & 0x1f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void USHR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) { + PREAMBLE(); + shadow_frame.SetVReg(inst->VRegA_22b(inst_data), + static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >> + (inst->VRegC_22b() & 0x1f)); + inst = inst->Next_2xx(); + } + + ALWAYS_INLINE void UNUSED_3E() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_3F() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_40() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_41() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_42() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_43() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_79() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_7A() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_F3() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_F4() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_F5() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_F6() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_F7() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_F8() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE void UNUSED_F9() REQUIRES_SHARED(Locks::mutator_lock_) { + UnexpectedOpcode(inst, shadow_frame); + } + + ALWAYS_INLINE InstructionHandler(SwitchImplContext* ctx, + const instrumentation::Instrumentation* instrumentation, + Thread* self, + ShadowFrame& shadow_frame, + uint16_t dex_pc, + const Instruction*& inst, + uint16_t inst_data, + bool& exit_interpreter_loop) + : ctx(ctx), + instrumentation(instrumentation), + self(self), + shadow_frame(shadow_frame), + dex_pc(dex_pc), + inst(inst), + inst_data(inst_data), + exit_interpreter_loop(exit_interpreter_loop) { + } + + private: + static constexpr bool do_assignability_check = do_access_check; + + const CodeItemDataAccessor& Accessor() { return ctx->accessor; } + const uint16_t* Insns() { return ctx->accessor.Insns(); } + JValue* ResultRegister() { return &ctx->result_register; } + + SwitchImplContext* const ctx; + const instrumentation::Instrumentation* const instrumentation; + Thread* const self; + ShadowFrame& shadow_frame; + uint32_t const dex_pc; + const Instruction*& inst; + uint16_t const inst_data; + bool& exit_interpreter_loop; +}; + +#undef HANDLE_BACKWARD_BRANCH +#undef HANDLE_ASYNC_EXCEPTION +#undef HOTNESS_UPDATE +#undef BRANCH_INSTRUMENTATION +#undef PREAMBLE +#undef PREAMBLE_SAVE +#undef HANDLE_MONITOR_CHECKS +#undef POSSIBLY_HANDLE_PENDING_EXCEPTION +#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE +#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC +#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL +#undef HANDLE_PENDING_EXCEPTION +#undef HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION +#undef CHECK_FORCE_RETURN + // TODO On ASAN builds this function gets a huge stack frame. Since normally we run in the mterp // this shouldn't cause any problems for stack overflow detection. Remove this once b/117341496 is // fixed. @@ -276,9 +2735,6 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx) Thread* self = ctx->self; const CodeItemDataAccessor& accessor = ctx->accessor; ShadowFrame& shadow_frame = ctx->shadow_frame; - JValue result_register = ctx->result_register; - bool interpret_one_instruction = ctx->interpret_one_instruction; - constexpr bool do_assignability_check = do_access_check; if (UNLIKELY(!shadow_frame.HasReferenceArray())) { LOG(FATAL) << "Invalid shadow frame for interpreter use"; ctx->result = JValue(); @@ -291,2295 +2747,40 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx) const uint16_t* const insns = accessor.Insns(); const Instruction* inst = Instruction::At(insns + dex_pc); uint16_t inst_data; - jit::Jit* jit = Runtime::Current()->GetJit(); DCHECK(!shadow_frame.GetForceRetryInstruction()) << "Entered interpreter from invoke without retry instruction being handled!"; - do { + bool const interpret_one_instruction = ctx->interpret_one_instruction; + while (true) { dex_pc = inst->GetDexPc(insns); shadow_frame.SetDexPC(dex_pc); TraceExecution(shadow_frame, inst, dex_pc); inst_data = inst->Fetch16(0); switch (inst->Opcode(inst_data)) { - case Instruction::NOP: - PREAMBLE(); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_FROM16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22x(inst_data), - shadow_frame.GetVReg(inst->VRegB_22x())); - inst = inst->Next_2xx(); - break; - case Instruction::MOVE_16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_32x(), - shadow_frame.GetVReg(inst->VRegB_32x())); - inst = inst->Next_3xx(); - break; - case Instruction::MOVE_WIDE: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_WIDE_FROM16: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_22x())); - inst = inst->Next_2xx(); - break; - case Instruction::MOVE_WIDE_16: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_32x(), - shadow_frame.GetVRegLong(inst->VRegB_32x())); - inst = inst->Next_3xx(); - break; - case Instruction::MOVE_OBJECT: - PREAMBLE(); - shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data), - shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_OBJECT_FROM16: - PREAMBLE(); - shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data), - shadow_frame.GetVRegReference(inst->VRegB_22x())); - inst = inst->Next_2xx(); - break; - case Instruction::MOVE_OBJECT_16: - PREAMBLE(); - shadow_frame.SetVRegReference(inst->VRegA_32x(), - shadow_frame.GetVRegReference(inst->VRegB_32x())); - inst = inst->Next_3xx(); - break; - case Instruction::MOVE_RESULT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI()); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_RESULT_WIDE: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ()); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_RESULT_OBJECT: - PREAMBLE_SAVE(&result_register); - shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL()); - inst = inst->Next_1xx(); - break; - case Instruction::MOVE_EXCEPTION: { - PREAMBLE(); - ObjPtr<mirror::Throwable> exception = self->GetException(); - DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction"; - shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception); - self->ClearException(); - inst = inst->Next_1xx(); - break; - } - case Instruction::RETURN_VOID_NO_BARRIER: { - PREAMBLE(); - JValue result; - self->AllowThreadSuspension(); - HANDLE_MONITOR_CHECKS(); - if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && - !SendMethodExitEvents(self, - instrumentation, - shadow_frame, - shadow_frame.GetThisObject(accessor.InsSize()), - shadow_frame.GetMethod(), - inst->GetDexPc(insns), - result))) { - HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); - } - if (interpret_one_instruction) { - /* Signal mterp to return to caller */ - shadow_frame.SetDexPC(dex::kDexNoIndex); - } - ctx->result = result; - return; - } - case Instruction::RETURN_VOID: { - PREAMBLE(); - QuasiAtomic::ThreadFenceForConstructor(); - JValue result; - self->AllowThreadSuspension(); - HANDLE_MONITOR_CHECKS(); - if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && - !SendMethodExitEvents(self, - instrumentation, - shadow_frame, - shadow_frame.GetThisObject(accessor.InsSize()), - shadow_frame.GetMethod(), - inst->GetDexPc(insns), - result))) { - HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); - } - if (interpret_one_instruction) { - /* Signal mterp to return to caller */ - shadow_frame.SetDexPC(dex::kDexNoIndex); - } - ctx->result = result; - return; - } - case Instruction::RETURN: { - PREAMBLE(); - JValue result; - result.SetJ(0); - result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data))); - self->AllowThreadSuspension(); - HANDLE_MONITOR_CHECKS(); - if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && - !SendMethodExitEvents(self, - instrumentation, - shadow_frame, - shadow_frame.GetThisObject(accessor.InsSize()), - shadow_frame.GetMethod(), - inst->GetDexPc(insns), - result))) { - HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); - } - if (interpret_one_instruction) { - /* Signal mterp to return to caller */ - shadow_frame.SetDexPC(dex::kDexNoIndex); - } - ctx->result = result; - return; - } - case Instruction::RETURN_WIDE: { - PREAMBLE(); - JValue result; - result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data))); - self->AllowThreadSuspension(); - HANDLE_MONITOR_CHECKS(); - if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && - !SendMethodExitEvents(self, - instrumentation, - shadow_frame, - shadow_frame.GetThisObject(accessor.InsSize()), - shadow_frame.GetMethod(), - inst->GetDexPc(insns), - result))) { - HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); - } - if (interpret_one_instruction) { - /* Signal mterp to return to caller */ - shadow_frame.SetDexPC(dex::kDexNoIndex); - } - ctx->result = result; - return; - } - case Instruction::RETURN_OBJECT: { - PREAMBLE(); - JValue result; - self->AllowThreadSuspension(); - HANDLE_MONITOR_CHECKS(); - const size_t ref_idx = inst->VRegA_11x(inst_data); - ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx); - if (do_assignability_check && obj_result != nullptr) { - ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType(); - // Re-load since it might have moved. - obj_result = shadow_frame.GetVRegReference(ref_idx); - if (return_type == nullptr) { - // Return the pending exception. - HANDLE_PENDING_EXCEPTION(); - } - if (!obj_result->VerifierInstanceOf(return_type)) { - // This should never happen. - std::string temp1, temp2; - self->ThrowNewExceptionF("Ljava/lang/InternalError;", - "Returning '%s' that is not instance of return type '%s'", - obj_result->GetClass()->GetDescriptor(&temp1), - return_type->GetDescriptor(&temp2)); - HANDLE_PENDING_EXCEPTION(); - } - } - result.SetL(obj_result); - if (UNLIKELY(NeedsMethodExitEvent(instrumentation) && - !SendMethodExitEvents(self, - instrumentation, - shadow_frame, - shadow_frame.GetThisObject(accessor.InsSize()), - shadow_frame.GetMethod(), - inst->GetDexPc(insns), - result))) { - HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr); - } - // Re-load since it might have moved during the MethodExitEvent. - result.SetL(shadow_frame.GetVRegReference(ref_idx)); - if (interpret_one_instruction) { - /* Signal mterp to return to caller */ - shadow_frame.SetDexPC(dex::kDexNoIndex); - } - ctx->result = result; - return; - } - case Instruction::CONST_4: { - PREAMBLE(); - uint4_t dst = inst->VRegA_11n(inst_data); - int4_t val = inst->VRegB_11n(inst_data); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, nullptr); - } - inst = inst->Next_1xx(); - break; - } - case Instruction::CONST_16: { - PREAMBLE(); - uint8_t dst = inst->VRegA_21s(inst_data); - int16_t val = inst->VRegB_21s(); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, nullptr); - } - inst = inst->Next_2xx(); - break; - } - case Instruction::CONST: { - PREAMBLE(); - uint8_t dst = inst->VRegA_31i(inst_data); - int32_t val = inst->VRegB_31i(); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, nullptr); - } - inst = inst->Next_3xx(); - break; - } - case Instruction::CONST_HIGH16: { - PREAMBLE(); - uint8_t dst = inst->VRegA_21h(inst_data); - int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16); - shadow_frame.SetVReg(dst, val); - if (val == 0) { - shadow_frame.SetVRegReference(dst, nullptr); - } - inst = inst->Next_2xx(); - break; - } - case Instruction::CONST_WIDE_16: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s()); - inst = inst->Next_2xx(); - break; - case Instruction::CONST_WIDE_32: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i()); - inst = inst->Next_3xx(); - break; - case Instruction::CONST_WIDE: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l()); - inst = inst->Next_51l(); - break; - case Instruction::CONST_WIDE_HIGH16: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data), - static_cast<uint64_t>(inst->VRegB_21h()) << 48); - inst = inst->Next_2xx(); - break; - case Instruction::CONST_STRING: { - PREAMBLE(); - ObjPtr<mirror::String> s = ResolveString(self, - shadow_frame, - dex::StringIndex(inst->VRegB_21c())); - if (UNLIKELY(s == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::CONST_STRING_JUMBO: { - PREAMBLE(); - ObjPtr<mirror::String> s = ResolveString(self, - shadow_frame, - dex::StringIndex(inst->VRegB_31c())); - if (UNLIKELY(s == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s); - inst = inst->Next_3xx(); - } - break; - } - case Instruction::CONST_CLASS: { - PREAMBLE(); - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), - shadow_frame.GetMethod(), - self, - false, - do_access_check); - if (UNLIKELY(c == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::CONST_METHOD_HANDLE: { - PREAMBLE(); - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self, - inst->VRegB_21c(), - shadow_frame.GetMethod()); - if (UNLIKELY(mh == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::CONST_METHOD_TYPE: { - PREAMBLE(); - ClassLinker* cl = Runtime::Current()->GetClassLinker(); - ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self, - dex::ProtoIndex(inst->VRegB_21c()), - shadow_frame.GetMethod()); - if (UNLIKELY(mt == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::MONITOR_ENTER: { - PREAMBLE(); - HANDLE_ASYNC_EXCEPTION(); - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); - if (UNLIKELY(obj == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - } else { - DoMonitorEnter<do_assignability_check>(self, &shadow_frame, obj); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - } - break; - } - case Instruction::MONITOR_EXIT: { - PREAMBLE(); - HANDLE_ASYNC_EXCEPTION(); - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); - if (UNLIKELY(obj == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - } else { - DoMonitorExit<do_assignability_check>(self, &shadow_frame, obj); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - } - break; - } - case Instruction::CHECK_CAST: { - PREAMBLE(); - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), - shadow_frame.GetMethod(), - self, - false, - do_access_check); - if (UNLIKELY(c == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data)); - if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) { - ThrowClassCastException(c, obj->GetClass()); - HANDLE_PENDING_EXCEPTION(); - } else { - inst = inst->Next_2xx(); - } - } - break; - } - case Instruction::INSTANCE_OF: { - PREAMBLE(); - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()), - shadow_frame.GetMethod(), - self, - false, - do_access_check); - if (UNLIKELY(c == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); - shadow_frame.SetVReg(inst->VRegA_22c(inst_data), - (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::ARRAY_LENGTH: { - PREAMBLE(); - ObjPtr<mirror::Object> array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)); - if (UNLIKELY(array == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength()); - inst = inst->Next_1xx(); - } - break; - } - case Instruction::NEW_INSTANCE: { - PREAMBLE(); - ObjPtr<mirror::Object> obj = nullptr; - ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), - shadow_frame.GetMethod(), - self, - false, - do_access_check); - if (LIKELY(c != nullptr)) { - if (UNLIKELY(c->IsStringClass())) { - gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); - obj = mirror::String::AllocEmptyString<true>(self, allocator_type); - } else { - obj = AllocObjectFromCode<true>( - c.Ptr(), - self, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - } - } - if (UNLIKELY(obj == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - obj->GetClass()->AssertInitializedOrInitializingInThread(self); - // Don't allow finalizable objects to be allocated during a transaction since these can't - // be finalized without a started runtime. - if (transaction_active && obj->GetClass()->IsFinalizable()) { - AbortTransactionF(self, "Allocating finalizable object in transaction: %s", - obj->PrettyTypeOf().c_str()); - HANDLE_PENDING_EXCEPTION(); - break; - } - shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::NEW_ARRAY: { - PREAMBLE(); - int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data)); - ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>( - dex::TypeIndex(inst->VRegC_22c()), - length, - shadow_frame.GetMethod(), - self, - Runtime::Current()->GetHeap()->GetCurrentAllocator()); - if (UNLIKELY(obj == nullptr)) { - HANDLE_PENDING_EXCEPTION(); - } else { - shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::FILLED_NEW_ARRAY: { - PREAMBLE(); - bool success = - DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self, - &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::FILLED_NEW_ARRAY_RANGE: { - PREAMBLE(); - bool success = - DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame, - self, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); - break; - } - case Instruction::FILL_ARRAY_DATA: { - PREAMBLE(); - const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t(); - const Instruction::ArrayDataPayload* payload = - reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr); - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data)); - bool success = FillArrayData(obj, payload); - if (!success) { - HANDLE_PENDING_EXCEPTION(); - break; - } - if (transaction_active) { - RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count); - } - inst = inst->Next_3xx(); - break; - } - case Instruction::THROW: { - PREAMBLE(); - HANDLE_ASYNC_EXCEPTION(); - ObjPtr<mirror::Object> exception = - shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data)); - if (UNLIKELY(exception == nullptr)) { - ThrowNullPointerException("throw with null exception"); - } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) { - // This should never happen. - std::string temp; - self->ThrowNewExceptionF("Ljava/lang/InternalError;", - "Throwing '%s' that is not instance of Throwable", - exception->GetClass()->GetDescriptor(&temp)); - } else { - self->SetException(exception->AsThrowable()); - } - HANDLE_PENDING_EXCEPTION(); - break; - } - case Instruction::GOTO: { - PREAMBLE(); - HANDLE_ASYNC_EXCEPTION(); - int8_t offset = inst->VRegA_10t(inst_data); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - break; - } - case Instruction::GOTO_16: { - PREAMBLE(); - HANDLE_ASYNC_EXCEPTION(); - int16_t offset = inst->VRegA_20t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - break; - } - case Instruction::GOTO_32: { - PREAMBLE(); - HANDLE_ASYNC_EXCEPTION(); - int32_t offset = inst->VRegA_30t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - break; - } - case Instruction::PACKED_SWITCH: { - PREAMBLE(); - int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - break; - } - case Instruction::SPARSE_SWITCH: { - PREAMBLE(); - int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - break; - } - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wfloat-equal" - - case Instruction::CMPL_FLOAT: { - PREAMBLE(); - float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x()); - float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x()); - int32_t result; - if (val1 > val2) { - result = 1; - } else if (val1 == val2) { - result = 0; - } else { - result = -1; - } - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); - inst = inst->Next_2xx(); - break; - } - case Instruction::CMPG_FLOAT: { - PREAMBLE(); - float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x()); - float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x()); - int32_t result; - if (val1 < val2) { - result = -1; - } else if (val1 == val2) { - result = 0; - } else { - result = 1; - } - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); - inst = inst->Next_2xx(); - break; - } - case Instruction::CMPL_DOUBLE: { - PREAMBLE(); - double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x()); - double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x()); - int32_t result; - if (val1 > val2) { - result = 1; - } else if (val1 == val2) { - result = 0; - } else { - result = -1; - } - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); - inst = inst->Next_2xx(); - break; - } - - case Instruction::CMPG_DOUBLE: { - PREAMBLE(); - double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x()); - double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x()); - int32_t result; - if (val1 < val2) { - result = -1; - } else if (val1 == val2) { - result = 0; - } else { - result = 1; - } - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); - inst = inst->Next_2xx(); - break; - } - -#pragma clang diagnostic pop - - case Instruction::CMP_LONG: { - PREAMBLE(); - int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x()); - int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x()); - int32_t result; - if (val1 > val2) { - result = 1; - } else if (val1 == val2) { - result = 0; - } else { - result = -1; - } - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result); - inst = inst->Next_2xx(); - break; - } - case Instruction::IF_EQ: { - PREAMBLE(); - 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); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_NE: { - PREAMBLE(); - 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); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LT: { - PREAMBLE(); - 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); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GE: { - PREAMBLE(); - 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); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GT: { - PREAMBLE(); - 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); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LE: { - PREAMBLE(); - 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); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_EQZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) { - int16_t offset = inst->VRegB_21t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_NEZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) { - int16_t offset = inst->VRegB_21t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LTZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) { - int16_t offset = inst->VRegB_21t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GEZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) { - int16_t offset = inst->VRegB_21t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_GTZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) { - int16_t offset = inst->VRegB_21t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::IF_LEZ: { - PREAMBLE(); - if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) { - int16_t offset = inst->VRegB_21t(); - BRANCH_INSTRUMENTATION(offset); - inst = inst->RelativeAt(offset); - HANDLE_BACKWARD_BRANCH(offset); - } else { - BRANCH_INSTRUMENTATION(2); - inst = inst->Next_2xx(); - } - break; - } - case Instruction::AGET_BOOLEAN: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray(); - if (array->CheckIsValidIndex(index)) { - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_BYTE: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::ByteArray> array = a->AsByteArray(); - if (array->CheckIsValidIndex(index)) { - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_CHAR: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::CharArray> array = a->AsCharArray(); - if (array->CheckIsValidIndex(index)) { - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_SHORT: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::ShortArray> array = a->AsShortArray(); - if (array->CheckIsValidIndex(index)) { - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf(); - ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a); - if (array->CheckIsValidIndex(index)) { - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_WIDE: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf(); - ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a); - if (array->CheckIsValidIndex(index)) { - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::AGET_OBJECT: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>(); - if (array->CheckIsValidIndex(index)) { - shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index)); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_BOOLEAN: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray(); - if (array->CheckIsValidIndex(index)) { - array->SetWithoutChecks<transaction_active>(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_BYTE: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::ByteArray> array = a->AsByteArray(); - if (array->CheckIsValidIndex(index)) { - array->SetWithoutChecks<transaction_active>(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_CHAR: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::CharArray> array = a->AsCharArray(); - if (array->CheckIsValidIndex(index)) { - array->SetWithoutChecks<transaction_active>(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_SHORT: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::ShortArray> array = a->AsShortArray(); - if (array->CheckIsValidIndex(index)) { - array->SetWithoutChecks<transaction_active>(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data)); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf(); - ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a); - if (array->CheckIsValidIndex(index)) { - array->SetWithoutChecks<transaction_active>(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_WIDE: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data)); - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf(); - ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a); - if (array->CheckIsValidIndex(index)) { - array->SetWithoutChecks<transaction_active>(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::APUT_OBJECT: { - PREAMBLE(); - ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x()); - if (UNLIKELY(a == nullptr)) { - ThrowNullPointerExceptionFromInterpreter(); - HANDLE_PENDING_EXCEPTION(); - break; - } - int32_t index = shadow_frame.GetVReg(inst->VRegC_23x()); - ObjPtr<mirror::Object> val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data)); - ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>(); - if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) { - array->SetWithoutChecks<transaction_active>(index, val); - inst = inst->Next_2xx(); - } else { - HANDLE_PENDING_EXCEPTION(); - } - break; - } - case Instruction::IGET_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>( - self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_BYTE: { - PREAMBLE(); - bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>( - self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_CHAR: { - PREAMBLE(); - bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>( - self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_SHORT: { - PREAMBLE(); - bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>( - self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET: { - PREAMBLE(); - bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>( - self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_WIDE: { - PREAMBLE(); - bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>( - self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_OBJECT: { - PREAMBLE(); - bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>( - self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_WIDE_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_OBJECT_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_BOOLEAN_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_BYTE_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_CHAR_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IGET_SHORT_QUICK: { - PREAMBLE(); - bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_BYTE: { - PREAMBLE(); - bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_CHAR: { - PREAMBLE(); - bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_SHORT: { - PREAMBLE(); - bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET: { - PREAMBLE(); - bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_WIDE: { - PREAMBLE(); - bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SGET_OBJECT: { - PREAMBLE(); - bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_BYTE: { - PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_CHAR: { - PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_SHORT: { - PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT: { - PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_WIDE: { - PREAMBLE(); - bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_OBJECT: { - PREAMBLE(); - bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>( - shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_BOOLEAN_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>( - shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_BYTE_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>( - shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_CHAR_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>( - shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_SHORT_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>( - shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_WIDE_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>( - shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::IPUT_OBJECT_QUICK: { - PREAMBLE(); - bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>( - shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_BOOLEAN: { - PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_BYTE: { - PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_CHAR: { - PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_SHORT: { - PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT: { - PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_WIDE: { - PREAMBLE(); - bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SPUT_OBJECT: { - PREAMBLE(); - bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check, - transaction_active>(self, shadow_frame, inst, inst_data); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::INVOKE_VIRTUAL: { - PREAMBLE(); - bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_VIRTUAL_RANGE: { - PREAMBLE(); - bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_SUPER: { - PREAMBLE(); - bool success = DoInvoke<kSuper, false, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_SUPER_RANGE: { - PREAMBLE(); - bool success = DoInvoke<kSuper, true, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_DIRECT: { - PREAMBLE(); - bool success = DoInvoke<kDirect, false, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_DIRECT_RANGE: { - PREAMBLE(); - bool success = DoInvoke<kDirect, true, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_INTERFACE: { - PREAMBLE(); - bool success = DoInvoke<kInterface, false, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_INTERFACE_RANGE: { - PREAMBLE(); - bool success = DoInvoke<kInterface, true, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_STATIC: { - PREAMBLE(); - bool success = DoInvoke<kStatic, false, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_STATIC_RANGE: { - PREAMBLE(); - bool success = DoInvoke<kStatic, true, do_access_check, /*is_mterp=*/ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_VIRTUAL_QUICK: { - PREAMBLE(); - bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false, - /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { - PREAMBLE(); - bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false, - /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_POLYMORPHIC: { - PREAMBLE(); - DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - bool success = DoInvokePolymorphic</* is_range= */ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success); - break; - } - case Instruction::INVOKE_POLYMORPHIC_RANGE: { - PREAMBLE(); - DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - bool success = DoInvokePolymorphic</* is_range= */ true>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success); - break; - } - case Instruction::INVOKE_CUSTOM: { - PREAMBLE(); - DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - bool success = DoInvokeCustom</* is_range= */ false>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::INVOKE_CUSTOM_RANGE: { - PREAMBLE(); - DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - bool success = DoInvokeCustom</* is_range= */ true>( - self, shadow_frame, inst, inst_data, &result_register); - POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success); - break; - } - case Instruction::NEG_INT: - PREAMBLE(); - shadow_frame.SetVReg( - inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::NOT_INT: - PREAMBLE(); - shadow_frame.SetVReg( - inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::NEG_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong( - inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::NOT_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong( - inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::NEG_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat( - inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::NEG_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble( - inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::LONG_TO_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::LONG_TO_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::LONG_TO_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::FLOAT_TO_INT: { - PREAMBLE(); - float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); - int32_t result = art_float_to_integral<int32_t, float>(val); - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::FLOAT_TO_LONG: { - PREAMBLE(); - float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)); - int64_t result = art_float_to_integral<int64_t, float>(val); - shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::FLOAT_TO_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), - shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::DOUBLE_TO_INT: { - PREAMBLE(); - double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); - int32_t result = art_float_to_integral<int32_t, double>(val); - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::DOUBLE_TO_LONG: { - PREAMBLE(); - double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)); - int64_t result = art_float_to_integral<int64_t, double>(val); - shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result); - inst = inst->Next_1xx(); - break; - } - case Instruction::DOUBLE_TO_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), - shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_BYTE: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int8_t>( - shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_CHAR: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<uint16_t>( - shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - case Instruction::INT_TO_SHORT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int16_t>( - shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - case Instruction::ADD_INT: { - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()), - shadow_frame.GetVReg(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - } - case Instruction::SUB_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()), - shadow_frame.GetVReg(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - case Instruction::MUL_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()), - shadow_frame.GetVReg(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - case Instruction::DIV_INT: { - PREAMBLE(); - bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data), - shadow_frame.GetVReg(inst->VRegB_23x()), - shadow_frame.GetVReg(inst->VRegC_23x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::REM_INT: { - PREAMBLE(); - bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data), - shadow_frame.GetVReg(inst->VRegB_23x()), - shadow_frame.GetVReg(inst->VRegC_23x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::SHL_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - shadow_frame.GetVReg(inst->VRegB_23x()) << - (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); - inst = inst->Next_2xx(); - break; - case Instruction::SHR_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - shadow_frame.GetVReg(inst->VRegB_23x()) >> - (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); - inst = inst->Next_2xx(); - break; - case Instruction::USHR_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >> - (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f)); - inst = inst->Next_2xx(); - break; - case Instruction::AND_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - shadow_frame.GetVReg(inst->VRegB_23x()) & - shadow_frame.GetVReg(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::OR_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - shadow_frame.GetVReg(inst->VRegB_23x()) | - shadow_frame.GetVReg(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::XOR_INT: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_23x(inst_data), - shadow_frame.GetVReg(inst->VRegB_23x()) ^ - shadow_frame.GetVReg(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::ADD_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()), - shadow_frame.GetVRegLong(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - case Instruction::SUB_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()), - shadow_frame.GetVRegLong(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - case Instruction::MUL_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()), - shadow_frame.GetVRegLong(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - case Instruction::DIV_LONG: - PREAMBLE(); - DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_23x()), - shadow_frame.GetVRegLong(inst->VRegC_23x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx); - break; - case Instruction::REM_LONG: - PREAMBLE(); - DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_23x()), - shadow_frame.GetVRegLong(inst->VRegC_23x())); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx); - break; - case Instruction::AND_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_23x()) & - shadow_frame.GetVRegLong(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::OR_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_23x()) | - shadow_frame.GetVRegLong(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::XOR_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_23x()) ^ - shadow_frame.GetVRegLong(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::SHL_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_23x()) << - (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); - inst = inst->Next_2xx(); - break; - case Instruction::SHR_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegLong(inst->VRegB_23x()) >> - (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); - inst = inst->Next_2xx(); - break; - case Instruction::USHR_LONG: - PREAMBLE(); - shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), - static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >> - (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f)); - inst = inst->Next_2xx(); - break; - case Instruction::ADD_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegFloat(inst->VRegB_23x()) + - shadow_frame.GetVRegFloat(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::SUB_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegFloat(inst->VRegB_23x()) - - shadow_frame.GetVRegFloat(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::MUL_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegFloat(inst->VRegB_23x()) * - shadow_frame.GetVRegFloat(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::DIV_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegFloat(inst->VRegB_23x()) / - shadow_frame.GetVRegFloat(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::REM_FLOAT: - PREAMBLE(); - shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data), - fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()), - shadow_frame.GetVRegFloat(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - case Instruction::ADD_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegDouble(inst->VRegB_23x()) + - shadow_frame.GetVRegDouble(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::SUB_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegDouble(inst->VRegB_23x()) - - shadow_frame.GetVRegDouble(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::MUL_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegDouble(inst->VRegB_23x()) * - shadow_frame.GetVRegDouble(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::DIV_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), - shadow_frame.GetVRegDouble(inst->VRegB_23x()) / - shadow_frame.GetVRegDouble(inst->VRegC_23x())); - inst = inst->Next_2xx(); - break; - case Instruction::REM_DOUBLE: - PREAMBLE(); - shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data), - fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()), - shadow_frame.GetVRegDouble(inst->VRegC_23x()))); - inst = inst->Next_2xx(); - break; - case Instruction::ADD_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, SafeAdd(shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - SafeSub(shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - SafeMul(shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); - break; - } - case Instruction::REM_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA), - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx); - break; - } - case Instruction::SHL_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::SHR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::USHR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::AND_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) & - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::OR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) | - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::XOR_INT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVReg(vregA, - shadow_frame.GetVReg(vregA) ^ - shadow_frame.GetVReg(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - SafeAdd(shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - SafeSub(shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - SafeMul(shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - break; - } - case Instruction::REM_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA), - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx); - break; - } - case Instruction::AND_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) & - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::OR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) | - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::XOR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) ^ - shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::SHL_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) << - (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::SHR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - shadow_frame.GetVRegLong(vregA) >> - (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::USHR_LONG_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegLong(vregA, - static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >> - (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f)); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) + - shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) - - shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) * - shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegFloat(vregA, - shadow_frame.GetVRegFloat(vregA) / - shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::REM_FLOAT_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegFloat(vregA, - fmodf(shadow_frame.GetVRegFloat(vregA), - shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) + - shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::SUB_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) - - shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::MUL_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) * - shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::DIV_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegDouble(vregA, - shadow_frame.GetVRegDouble(vregA) / - shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))); - inst = inst->Next_1xx(); - break; - } - case Instruction::REM_DOUBLE_2ADDR: { - PREAMBLE(); - uint4_t vregA = inst->VRegA_12x(inst_data); - shadow_frame.SetVRegDouble(vregA, - fmod(shadow_frame.GetVRegDouble(vregA), - shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)))); - inst = inst->Next_1xx(); - break; - } - case Instruction::ADD_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(inst_data), - SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), - inst->VRegC_22s())); - inst = inst->Next_2xx(); - break; - case Instruction::RSUB_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(inst_data), - SafeSub(inst->VRegC_22s(), - shadow_frame.GetVReg(inst->VRegB_22s(inst_data)))); - inst = inst->Next_2xx(); - break; - case Instruction::MUL_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(inst_data), - SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), - inst->VRegC_22s())); - inst = inst->Next_2xx(); - break; - case Instruction::DIV_INT_LIT16: { - PREAMBLE(); - bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data), - shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), - inst->VRegC_22s()); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::REM_INT_LIT16: { - PREAMBLE(); - bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data), - shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), - inst->VRegC_22s()); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::AND_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(inst_data), - shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) & - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::OR_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(inst_data), - shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) | - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::XOR_INT_LIT16: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22s(inst_data), - shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^ - inst->VRegC_22s()); - inst = inst->Next_2xx(); - break; - case Instruction::ADD_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b())); - inst = inst->Next_2xx(); - break; - case Instruction::RSUB_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - SafeSub(inst->VRegC_22b(), shadow_frame.GetVReg(inst->VRegB_22b()))); - inst = inst->Next_2xx(); - break; - case Instruction::MUL_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b())); - inst = inst->Next_2xx(); - break; - case Instruction::DIV_INT_LIT8: { - PREAMBLE(); - bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data), - shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::REM_INT_LIT8: { - PREAMBLE(); - bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data), - shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()); - POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx); - break; - } - case Instruction::AND_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - shadow_frame.GetVReg(inst->VRegB_22b()) & - inst->VRegC_22b()); - inst = inst->Next_2xx(); - break; - case Instruction::OR_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - shadow_frame.GetVReg(inst->VRegB_22b()) | - inst->VRegC_22b()); - inst = inst->Next_2xx(); - break; - case Instruction::XOR_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - shadow_frame.GetVReg(inst->VRegB_22b()) ^ - inst->VRegC_22b()); - inst = inst->Next_2xx(); - break; - case Instruction::SHL_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - shadow_frame.GetVReg(inst->VRegB_22b()) << - (inst->VRegC_22b() & 0x1f)); - inst = inst->Next_2xx(); - break; - case Instruction::SHR_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - shadow_frame.GetVReg(inst->VRegB_22b()) >> - (inst->VRegC_22b() & 0x1f)); - inst = inst->Next_2xx(); - break; - case Instruction::USHR_INT_LIT8: - PREAMBLE(); - shadow_frame.SetVReg(inst->VRegA_22b(inst_data), - static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >> - (inst->VRegC_22b() & 0x1f)); - inst = inst->Next_2xx(); - break; - case Instruction::UNUSED_3E ... Instruction::UNUSED_43: - case Instruction::UNUSED_79 ... Instruction::UNUSED_7A: - case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9: - UnexpectedOpcode(inst, shadow_frame); - } - } while (!interpret_one_instruction); - // Record where we stopped. - shadow_frame.SetDexPC(inst->GetDexPc(insns)); - ctx->result = result_register; - return; +#define OPCODE_CASE(OPCODE, OPCODE_NAME, pname, f, i, a, e, v) \ + case OPCODE: { \ + bool exit_loop = false; \ + InstructionHandler<do_access_check, transaction_active> handler( \ + ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop); \ + /* TODO: Call PREAMBLE here, instead of explicitly in each handler */ \ + handler.OPCODE_NAME(); \ + /* TODO: Advance 'inst' here, instead of explicitly in each handler */ \ + if (UNLIKELY(exit_loop)) { \ + return; \ + } \ + break; \ + } +DEX_INSTRUCTION_LIST(OPCODE_CASE) +#undef OPCODE_CASE + } + if (UNLIKELY(interpret_one_instruction)) { + // Record where we stopped. + shadow_frame.SetDexPC(inst->GetDexPc(insns)); + ctx->result = ctx->result_register; + return; + } + } } // NOLINT(readability/fn_size) } // namespace interpreter diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h index 2f91f5dfe0..093dd7f400 100644 --- a/runtime/intrinsics_list.h +++ b/runtime/intrinsics_list.h @@ -219,6 +219,7 @@ V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \ V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \ V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \ + V(CRC32Update, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "update", "(II)I") \ SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V) #endif // ART_RUNTIME_INTRINSICS_LIST_H_ diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index e876a1bc89..d67d9dced8 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -60,7 +60,6 @@ void* (*Jit::jit_load_)(bool*) = nullptr; void (*Jit::jit_unload_)(void*) = nullptr; bool (*Jit::jit_compile_method_)(void*, ArtMethod*, Thread*, bool) = nullptr; void (*Jit::jit_types_loaded_)(void*, mirror::Class**, size_t count) = nullptr; -bool Jit::generate_debug_info_ = false; struct StressModeHelper { DECLARE_RUNTIME_DEBUG_FLAG(kSlowMode); @@ -176,11 +175,24 @@ Jit::Jit(JitCodeCache* code_cache, JitOptions* options) lock_("JIT memory use lock") {} Jit* Jit::Create(JitCodeCache* code_cache, JitOptions* options) { - CHECK(jit_compiler_handle_ != nullptr) << "Jit::LoadLibrary() needs to be called first"; - std::unique_ptr<Jit> jit(new Jit(code_cache, options)); + if (jit_load_ == nullptr) { + LOG(WARNING) << "Not creating JIT: library not loaded"; + return nullptr; + } + bool will_generate_debug_symbols = false; + jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols); if (jit_compiler_handle_ == nullptr) { + LOG(WARNING) << "Not creating JIT: failed to allocate a compiler"; return nullptr; } + std::unique_ptr<Jit> jit(new Jit(code_cache, options)); + jit->generate_debug_info_ = will_generate_debug_symbols; + + // With 'perf', we want a 1-1 mapping between an address and a method. + // We aren't able to keep method pointers live during the instrumentation method entry trampoline + // so we will just disable jit-gc if we are doing that. + code_cache->SetGarbageCollectCode(!jit->generate_debug_info_ && + !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()); VLOG(jit) << "JIT created with initial_capacity=" << PrettySize(options->GetCodeCacheInitialCapacity()) @@ -195,7 +207,7 @@ Jit* Jit::Create(JitCodeCache* code_cache, JitOptions* options) { return jit.release(); } -bool Jit::BindCompilerMethods(std::string* error_msg) { +bool Jit::LoadCompilerLibrary(std::string* error_msg) { jit_library_handle_ = dlopen( kIsDebugBuild ? "libartd-compiler.so" : "libart-compiler.so", RTLD_NOW); if (jit_library_handle_ == nullptr) { @@ -234,23 +246,6 @@ bool Jit::BindCompilerMethods(std::string* error_msg) { return true; } -bool Jit::LoadCompiler(std::string* error_msg) { - if (jit_library_handle_ == nullptr && !BindCompilerMethods(error_msg)) { - return false; - } - bool will_generate_debug_symbols = false; - VLOG(jit) << "Calling JitLoad interpreter_only=" - << Runtime::Current()->GetInstrumentation()->InterpretOnly(); - jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols); - if (jit_compiler_handle_ == nullptr) { - dlclose(jit_library_handle_); - *error_msg = "JIT couldn't load compiler"; - return false; - } - generate_debug_info_ = will_generate_debug_symbols; - return true; -} - bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { DCHECK(Runtime::Current()->UseJitCompilation()); DCHECK(!method->IsRuntimeMethod()); @@ -300,11 +295,6 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { return success; } -bool Jit::ShouldGenerateDebugInfo() { - CHECK(CompilerIsLoaded()); - return generate_debug_info_; -} - void Jit::CreateThreadPool() { // There is a DCHECK in the 'AddSamples' method to ensure the tread pool // is not null when we instrument. @@ -385,7 +375,7 @@ void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) { return; } jit::Jit* jit = Runtime::Current()->GetJit(); - if (generate_debug_info_) { + if (jit->generate_debug_info_) { DCHECK(jit->jit_types_loaded_ != nullptr); jit->jit_types_loaded_(jit->jit_compiler_handle_, &type, 1); } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index b0ea19b303..46b0762629 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -282,13 +282,8 @@ class Jit { JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); - // Load and initialize compiler. - static bool LoadCompiler(std::string* error_msg); - - static bool CompilerIsLoaded() { return jit_compiler_handle_ != nullptr; } - - // Return whether debug info should be generated. Requires LoadCompiler() to have been called. - static bool ShouldGenerateDebugInfo(); + // Load the compiler library. + static bool LoadCompilerLibrary(std::string* error_msg); ThreadPool* GetThreadPool() const { return thread_pool_.get(); @@ -313,8 +308,8 @@ class Jit { static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool); static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count); - // We make this static to simplify the interaction with libart-compiler.so. - static bool generate_debug_info_; + // Whether we should generate debug info when compiling. + bool generate_debug_info_; // JIT resources owned by runtime. jit::JitCodeCache* const code_cache_; diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index a15a9be6f5..359f97e705 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -436,12 +436,6 @@ JitCodeCache::JitCodeCache(MemMap&& data_pages, SetFootprintLimit(current_capacity_); } - // With 'perf', we want a 1-1 mapping between an address and a method. - // We aren't able to keep method pointers live during the instrumentation method entry trampoline - // so we will just disable jit-gc if we are doing that. - garbage_collect_code_ = !Jit::ShouldGenerateDebugInfo() && - !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled(); - VLOG(jit) << "Created jit code cache: initial data size=" << PrettySize(initial_data_capacity) << ", initial code size=" diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 32b88e6fa1..49b71cd801 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -371,7 +371,7 @@ static void PreloadDexCachesResolveType(Thread* self, const char* class_name = dex_file->StringByTypeIdx(type_idx); ClassLinker* linker = Runtime::Current()->GetClassLinker(); ObjPtr<mirror::Class> klass = (class_name[1] == '\0') - ? linker->FindPrimitiveClass(class_name[0]) + ? linker->LookupPrimitiveClass(class_name[0]) : linker->LookupClass(self, class_name, nullptr); if (klass == nullptr) { return; diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index b9ac88d999..f21ded9c23 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -334,14 +334,15 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j return; } ObjPtr<mirror::Class> field_type; - const char* field_type_desciptor = f->GetArtField()->GetTypeDescriptor(); - Primitive::Type field_prim_type = Primitive::GetType(field_type_desciptor[0]); + const char* field_type_descriptor = f->GetArtField()->GetTypeDescriptor(); + Primitive::Type field_prim_type = Primitive::GetType(field_type_descriptor[0]); if (field_prim_type == Primitive::kPrimNot) { field_type = f->GetType(); - DCHECK(field_type != nullptr); } else { - field_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(field_type_desciptor[0]); + field_type = + Runtime::Current()->GetClassLinker()->LookupPrimitiveClass(field_type_descriptor[0]); } + DCHECK(field_type != nullptr) << field_type_descriptor; // We now don't expect suspension unless an exception is thrown. // Unbox the value, if necessary. ObjPtr<mirror::Object> boxed_value = soa.Decode<mirror::Object>(javaValue); diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc index e021b77dae..a739c2d16e 100644 --- a/runtime/native/sun_misc_Unsafe.cc +++ b/runtime/native/sun_misc_Unsafe.cc @@ -31,8 +31,10 @@ #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/object-inl.h" +#include "art_field-inl.h" #include "native_util.h" #include "scoped_fast_native_object_access-inl.h" +#include "well_known_classes.h" namespace art { @@ -504,6 +506,33 @@ static void Unsafe_fullFence(JNIEnv*, jobject) { std::atomic_thread_fence(std::memory_order_seq_cst); } +static void Unsafe_park(JNIEnv* env, jobject, jboolean isAbsolute, jlong time) { + ScopedObjectAccess soa(env); + Thread::Current()->Park(isAbsolute, time); +} + +static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) { + art::ScopedFastNativeObjectAccess soa(env); + if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) { + ThrowIllegalArgumentException("Argument to unpark() was not a Thread"); + return; + } + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + art::Thread* thread = art::Thread::FromManagedThread(soa, jthread); + if (thread != nullptr) { + thread->Unpark(); + } else { + // If thread is null, that means that either the thread is not started yet, + // or the thread has already terminated. Setting the field to true will be + // respected when the thread does start, and is harmless if the thread has + // already terminated. + ArtField* unparked = + jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart); + // JNI must use non transactional mode. + unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE); + } +} + static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"), FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"), @@ -546,6 +575,8 @@ static JNINativeMethod gMethods[] = { FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"), FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"), FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"), + FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"), + NATIVE_METHOD(Unsafe, park, "(ZJ)V"), // Each of the getFoo variants are overloaded with a call that operates // directively on a native pointer. diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index 10f589831f..88adad0f91 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -373,7 +373,7 @@ void DumpNativeStack(std::ostream& os, } os << std::endl; if (try_addr2line && use_addr2line) { - Addr2line(it->map.name, it->pc - it->map.start, os, prefix, &addr2line_state); + Addr2line(it->map.name, it->rel_pc, os, prefix, &addr2line_state); } } diff --git a/runtime/oat.h b/runtime/oat.h index 2a6d738178..b07294adeb 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -34,7 +34,6 @@ class PACKED(4) OatHeader { // Last oat version changed reason: Remove interpreter alt tables. static constexpr uint8_t kOatVersion[] = { '1', '6', '3', '\0' }; - static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kDebuggableKey = "debuggable"; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 646de757e0..80118365f0 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -886,32 +886,31 @@ static bool UnboxPrimitive(ObjPtr<mirror::Object> o, JValue boxed_value; ObjPtr<mirror::Class> klass = o->GetClass(); - ObjPtr<mirror::Class> src_class = nullptr; - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + Primitive::Type primitive_type; ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0); if (klass->DescriptorEquals("Ljava/lang/Boolean;")) { - src_class = class_linker->FindPrimitiveClass('Z'); + primitive_type = Primitive::kPrimBoolean; boxed_value.SetZ(primitive_field->GetBoolean(o)); } else if (klass->DescriptorEquals("Ljava/lang/Byte;")) { - src_class = class_linker->FindPrimitiveClass('B'); + primitive_type = Primitive::kPrimByte; boxed_value.SetB(primitive_field->GetByte(o)); } else if (klass->DescriptorEquals("Ljava/lang/Character;")) { - src_class = class_linker->FindPrimitiveClass('C'); + primitive_type = Primitive::kPrimChar; boxed_value.SetC(primitive_field->GetChar(o)); } else if (klass->DescriptorEquals("Ljava/lang/Float;")) { - src_class = class_linker->FindPrimitiveClass('F'); + primitive_type = Primitive::kPrimFloat; boxed_value.SetF(primitive_field->GetFloat(o)); } else if (klass->DescriptorEquals("Ljava/lang/Double;")) { - src_class = class_linker->FindPrimitiveClass('D'); + primitive_type = Primitive::kPrimDouble; boxed_value.SetD(primitive_field->GetDouble(o)); } else if (klass->DescriptorEquals("Ljava/lang/Integer;")) { - src_class = class_linker->FindPrimitiveClass('I'); + primitive_type = Primitive::kPrimInt; boxed_value.SetI(primitive_field->GetInt(o)); } else if (klass->DescriptorEquals("Ljava/lang/Long;")) { - src_class = class_linker->FindPrimitiveClass('J'); + primitive_type = Primitive::kPrimLong; boxed_value.SetJ(primitive_field->GetLong(o)); } else if (klass->DescriptorEquals("Ljava/lang/Short;")) { - src_class = class_linker->FindPrimitiveClass('S'); + primitive_type = Primitive::kPrimShort; boxed_value.SetS(primitive_field->GetShort(o)); } else { std::string temp; @@ -923,7 +922,8 @@ static bool UnboxPrimitive(ObjPtr<mirror::Object> o, } return ConvertPrimitiveValue(unbox_for_result, - src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(), + primitive_type, + dst_class->GetPrimitiveType(), boxed_value, unboxed_value); } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 34b84f52c6..e5772666d1 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -789,12 +789,8 @@ bool Runtime::Start() { if (!safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo())) { // Try to load compiler pre zygote to reduce PSS. b/27744947 std::string error_msg; - if (!jit::Jit::LoadCompiler(&error_msg)) { + if (!jit::Jit::LoadCompilerLibrary(&error_msg)) { LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg; - } else if (!IsZygote()) { - // If we are the zygote then we need to wait until after forking to create the code cache - // due to SELinux restrictions on r/w/x memory regions. - CreateJitCodeCache(/*rwx_memory_allowed=*/true); } } @@ -900,6 +896,11 @@ void Runtime::InitNonZygoteOrPostFork( } if (jit_ == nullptr) { + // The system server's code cache was initialized specially. For other zygote forks or + // processes create it now. + if (!is_system_server) { + CreateJitCodeCache(/*rwx_memory_allowed=*/true); + } // Note that when running ART standalone (not zygote, nor zygote fork), // the jit may have already been created. CreateJit(); @@ -1101,6 +1102,10 @@ void Runtime::SetSentinel(mirror::Object* sentinel) { sentinel_ = GcRoot<mirror::Object>(sentinel); } +GcRoot<mirror::Object> Runtime::GetSentinel() { + return sentinel_; +} + static inline void CreatePreAllocatedException(Thread* self, Runtime* runtime, GcRoot<mirror::Throwable>* exception, diff --git a/runtime/runtime.h b/runtime/runtime.h index 4fb0d2ede0..be5b3c119c 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -691,6 +691,9 @@ class Runtime { // Called from class linker. void SetSentinel(mirror::Object* sentinel) REQUIRES_SHARED(Locks::mutator_lock_); + // For testing purpose only. + // TODO: Remove this when this is no longer needed (b/116087961). + GcRoot<mirror::Object> GetSentinel() REQUIRES_SHARED(Locks::mutator_lock_); // Create a normal LinearAlloc or low 4gb version if we are 64 bit AOT compiler. LinearAlloc* CreateLinearAlloc(); diff --git a/runtime/thread.cc b/runtime/thread.cc index dda3b82612..66e852a216 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -44,6 +44,7 @@ #include "arch/context.h" #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/atomic.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/file_utils.h" @@ -285,6 +286,116 @@ void Thread::AssertHasDeoptimizationContext() { << "No deoptimization context for thread " << *this; } +enum { + kPermitAvailable = 0, // Incrementing consumes the permit + kNoPermit = 1, // Incrementing marks as waiter waiting + kNoPermitWaiterWaiting = 2 +}; + +void Thread::Park(bool is_absolute, int64_t time) { + DCHECK(this == Thread::Current()); +#if ART_USE_FUTEXES + // Consume the permit, or mark as waiting. This cannot cause park_state to go + // outside of its valid range (0, 1, 2), because in all cases where 2 is + // assigned it is set back to 1 before returning, and this method cannot run + // concurrently with itself since it operates on the current thread. + int old_state = tls32_.park_state_.fetch_add(1, std::memory_order_relaxed); + if (old_state == kNoPermit) { + // no permit was available. block thread until later. + // TODO: Call to signal jvmti here + int result = 0; + if (!is_absolute && time == 0) { + // Thread.getState() is documented to return waiting for untimed parks. + ScopedThreadSuspension sts(this, ThreadState::kWaiting); + DCHECK_EQ(NumberOfHeldMutexes(), 0u); + result = futex(tls32_.park_state_.Address(), + FUTEX_WAIT_PRIVATE, + /* sleep if val = */ kNoPermitWaiterWaiting, + /* timeout */ nullptr, + nullptr, + 0); + } else if (time > 0) { + // Only actually suspend and futex_wait if we're going to wait for some + // positive amount of time - the kernel will reject negative times with + // EINVAL, and a zero time will just noop. + + // Thread.getState() is documented to return timed wait for timed parks. + ScopedThreadSuspension sts(this, ThreadState::kTimedWaiting); + DCHECK_EQ(NumberOfHeldMutexes(), 0u); + timespec timespec; + if (is_absolute) { + // Time is millis when scheduled for an absolute time + timespec.tv_nsec = (time % 1000) * 1000000; + timespec.tv_sec = time / 1000; + // This odd looking pattern is recommended by futex documentation to + // wait until an absolute deadline, with otherwise identical behavior to + // FUTEX_WAIT_PRIVATE. This also allows parkUntil() to return at the + // correct time when the system clock changes. + result = futex(tls32_.park_state_.Address(), + FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME, + /* sleep if val = */ kNoPermitWaiterWaiting, + ×pec, + nullptr, + FUTEX_BITSET_MATCH_ANY); + } else { + // Time is nanos when scheduled for a relative time + timespec.tv_sec = time / 1000000000; + timespec.tv_nsec = time % 1000000000; + result = futex(tls32_.park_state_.Address(), + FUTEX_WAIT_PRIVATE, + /* sleep if val = */ kNoPermitWaiterWaiting, + ×pec, + nullptr, + 0); + } + } + if (result == -1) { + switch (errno) { + case EAGAIN: + case ETIMEDOUT: + case EINTR: break; // park() is allowed to spuriously return + default: PLOG(FATAL) << "Failed to park"; + } + } + // Mark as no longer waiting, and consume permit if there is one. + tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed); + // TODO: Call to signal jvmti here + } else { + // the fetch_add has consumed the permit. immediately return. + DCHECK_EQ(old_state, kPermitAvailable); + } +#else + #pragma clang diagnostic push + #pragma clang diagnostic warning "-W#warnings" + #warning "LockSupport.park/unpark implemented as noops without FUTEX support." + #pragma clang diagnostic pop + UNUSED(is_absolute, time); + UNIMPLEMENTED(WARNING); + sched_yield(); +#endif +} + +void Thread::Unpark() { +#if ART_USE_FUTEXES + // Set permit available; will be consumed either by fetch_add (when the thread + // tries to park) or store (when the parked thread is woken up) + if (tls32_.park_state_.exchange(kPermitAvailable, std::memory_order_relaxed) + == kNoPermitWaiterWaiting) { + int result = futex(tls32_.park_state_.Address(), + FUTEX_WAKE_PRIVATE, + /* number of waiters = */ 1, + nullptr, + nullptr, + 0); + if (result == -1) { + PLOG(FATAL) << "Failed to unpark"; + } + } +#else + UNIMPLEMENTED(WARNING); +#endif +} + void Thread::PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type) { StackedShadowFrameRecord* record = new StackedShadowFrameRecord( sf, type, tlsPtr_.stacked_shadow_frame_record); @@ -489,6 +600,22 @@ void* Thread::CreateCallback(void* arg) { runtime->GetRuntimeCallbacks()->ThreadStart(self); + // Unpark ourselves if the java peer was unparked before it started (see + // b/28845097#comment49 for more information) + + ArtField* unparkedField = jni::DecodeArtField( + WellKnownClasses::java_lang_Thread_unparkedBeforeStart); + bool should_unpark = false; + { + // Hold the lock here, so that if another thread calls unpark before the thread starts + // we don't observe the unparkedBeforeStart field before the unparker writes to it, + // which could cause a lost unpark. + art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); + should_unpark = unparkedField->GetBoolean(self->tlsPtr_.opeer) == JNI_TRUE; + } + if (should_unpark) { + self->Unpark(); + } // Invoke the 'run' method of our java.lang.Thread. ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer; jmethodID mid = WellKnownClasses::java_lang_Thread_run; @@ -2133,6 +2260,9 @@ Thread::Thread(bool daemon) tls32_.state_and_flags.as_struct.flags = 0; tls32_.state_and_flags.as_struct.state = kNative; tls32_.interrupted.store(false, std::memory_order_relaxed); + // Initialize with no permit; if the java Thread was unparked before being + // started, it will unpark itself before calling into java code. + tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed); memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes)); std::fill(tlsPtr_.rosalloc_runs, tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread, @@ -2449,12 +2579,15 @@ bool Thread::IsInterrupted() { } void Thread::Interrupt(Thread* self) { - MutexLock mu(self, *wait_mutex_); - if (tls32_.interrupted.load(std::memory_order_seq_cst)) { - return; + { + MutexLock mu(self, *wait_mutex_); + if (tls32_.interrupted.load(std::memory_order_seq_cst)) { + return; + } + tls32_.interrupted.store(true, std::memory_order_seq_cst); + NotifyLocked(self); } - tls32_.interrupted.store(true, std::memory_order_seq_cst); - NotifyLocked(self); + Unpark(); } void Thread::Notify() { diff --git a/runtime/thread.h b/runtime/thread.h index 941867ce2d..b304cef74d 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -581,6 +581,11 @@ class Thread { return poison_object_cookie_; } + // Parking for 0ns of relative time means an untimed park, negative (though + // should be handled in java code) returns immediately + void Park(bool is_absolute, int64_t time) REQUIRES_SHARED(Locks::mutator_lock_); + void Unpark(); + private: void NotifyLocked(Thread* self) REQUIRES(wait_mutex_); @@ -1543,6 +1548,8 @@ class Thread { // Thread "interrupted" status; stays raised until queried or thrown. Atomic<bool32_t> interrupted; + AtomicInteger park_state_; + // True if the thread is allowed to access a weak ref (Reference::GetReferent() and system // weaks) and to potentially mark an object alive/gray. This is used for concurrent reference // processing of the CC collector only. This is thread local so that we can enable/disable weak diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 206418fbc6..94faa626f6 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -128,6 +128,7 @@ jfieldID WellKnownClasses::java_lang_Thread_lock; jfieldID WellKnownClasses::java_lang_Thread_name; jfieldID WellKnownClasses::java_lang_Thread_priority; jfieldID WellKnownClasses::java_lang_Thread_nativePeer; +jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart; jfieldID WellKnownClasses::java_lang_ThreadGroup_groups; jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups; jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup; @@ -376,6 +377,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;"); java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I"); java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J"); + java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z"); java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;"); java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I"); java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index ce5ab1df84..8c85228dfc 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -137,6 +137,7 @@ struct WellKnownClasses { static jfieldID java_lang_Thread_name; static jfieldID java_lang_Thread_priority; static jfieldID java_lang_Thread_nativePeer; + static jfieldID java_lang_Thread_unparkedBeforeStart; static jfieldID java_lang_ThreadGroup_groups; static jfieldID java_lang_ThreadGroup_ngroups; static jfieldID java_lang_ThreadGroup_mainThreadGroup; diff --git a/test/004-ThreadStress/src-art/Main.java b/test/004-ThreadStress/src-art/Main.java index b8bfafb2d3..e7178523d3 100644 --- a/test/004-ThreadStress/src-art/Main.java +++ b/test/004-ThreadStress/src-art/Main.java @@ -53,6 +53,7 @@ import java.util.concurrent.locks.LockSupport; // -sleep:X .......... frequency of Sleep (double) // -wait:X ........... frequency of Wait (double) // -timedwait:X ...... frequency of TimedWait (double) +// -timedpark:X ...... frequency of TimedPark (double) // -syncandwork:X .... frequency of SyncAndWork (double) // -queuedwait:X ..... frequency of QueuedWait (double) @@ -264,19 +265,6 @@ public class Main implements Runnable { } } - private final static class UnparkAllThreads extends Operation { - public UnparkAllThreads() {} - - @Override - public boolean perform() { - Set<Thread> threads = Thread.getAllStackTraces().keySet(); - for (Thread candidate : threads) { - LockSupport.unpark(candidate); - } - return true; - } - } - private final static class SyncAndWork extends Operation { private final Object lock; @@ -346,9 +334,8 @@ public class Main implements Runnable { frequencyMap.put(new NonMovingAlloc(), 0.025); // 5/200 frequencyMap.put(new StackTrace(), 0.1); // 20/200 frequencyMap.put(new Exit(), 0.225); // 45/200 - frequencyMap.put(new Sleep(), 0.125); // 15/200 - frequencyMap.put(new TimedPark(), 0.025); // 5/200 - frequencyMap.put(new UnparkAllThreads(), 0.025); // 5/200 + frequencyMap.put(new Sleep(), 0.075); // 15/200 + frequencyMap.put(new TimedPark(), 0.05); // 10/200 frequencyMap.put(new TimedWait(lock), 0.05); // 10/200 frequencyMap.put(new Wait(lock), 0.075); // 15/200 frequencyMap.put(new QueuedWait(semaphore), 0.05); // 10/200 @@ -370,10 +357,9 @@ public class Main implements Runnable { Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>(); frequencyMap.put(new Sleep(), 0.2); // 40/200 frequencyMap.put(new TimedWait(lock), 0.1); // 20/200 - frequencyMap.put(new Wait(lock), 0.1); // 20/200 + frequencyMap.put(new Wait(lock), 0.2); // 40/200 frequencyMap.put(new SyncAndWork(lock), 0.4); // 80/200 frequencyMap.put(new TimedPark(), 0.1); // 20/200 - frequencyMap.put(new UnparkAllThreads(), 0.1); // 20/200 return frequencyMap; } @@ -419,6 +405,8 @@ public class Main implements Runnable { op = new Wait(lock); } else if (split[0].equals("-timedwait")) { op = new TimedWait(lock); + } else if (split[0].equals("-timedpark")) { + op = new TimedPark(); } else if (split[0].equals("-syncandwork")) { op = new SyncAndWork(lock); } else if (split[0].equals("-queuedwait")) { @@ -723,7 +711,7 @@ public class Main implements Runnable { } // The notifier thread is a daemon just loops forever to wake - // up threads in operation Wait. + // up threads in operations Wait and Park. if (lock != null) { Thread notifier = new Thread("Notifier") { public void run() { @@ -731,6 +719,11 @@ public class Main implements Runnable { synchronized (lock) { lock.notifyAll(); } + for (Thread runner : runners) { + if (runner != null) { + LockSupport.unpark(runner); + } + } } } }; diff --git a/test/099-vmdebug/check b/test/099-vmdebug/check index d124ce8cfd..6a3fed55ba 100755 --- a/test/099-vmdebug/check +++ b/test/099-vmdebug/check @@ -15,6 +15,6 @@ # limitations under the License. # Strip the process pids and line numbers from exact error messages. -sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp" +sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp" diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null diff --git a/test/143-string-value/check b/test/143-string-value/check index 2a3476c2ab..b5e51cecad 100755 --- a/test/143-string-value/check +++ b/test/143-string-value/check @@ -15,6 +15,6 @@ # limitations under the License. # Strip error log messages. -sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp" +sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp" diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null diff --git a/test/160-read-barrier-stress/src/Main.java b/test/160-read-barrier-stress/src/Main.java index 5865094002..5e49e664be 100644 --- a/test/160-read-barrier-stress/src/Main.java +++ b/test/160-read-barrier-stress/src/Main.java @@ -121,6 +121,23 @@ public class Main { assertSameObject(f4444, la[i4444]); assertDifferentObject(f4999, la[i4998]); assertSameObject(f4999, la[i4999]); + + la = largeArray; + // Group the ArrayGets so they aren't divided by a function call; this will enable + // interm. address sharing for arm64. + Object tmp1 = la[i0]; + Object tmp2 = la[i0 + 1]; + Object tmp3 = la[i0 + 1024]; + Object tmp4 = la[i0 + 4444]; + Object tmp5 = la[i0 + 4998]; + Object tmp6 = la[i0 + 4999]; + + assertSameObject(f0000, tmp1); + assertDifferentObject(f0000, tmp2); + assertSameObject(f1024, tmp3); + assertSameObject(f4444, tmp4); + assertDifferentObject(f4999, tmp5); + assertSameObject(f4999, tmp6); } } diff --git a/test/1958-transform-try-jit/expected.txt b/test/1958-transform-try-jit/expected.txt new file mode 100644 index 0000000000..8cfaea2be0 --- /dev/null +++ b/test/1958-transform-try-jit/expected.txt @@ -0,0 +1,2 @@ +Before redefinition: hello +After redefinition: Goodbye diff --git a/test/1958-transform-try-jit/info.txt b/test/1958-transform-try-jit/info.txt new file mode 100644 index 0000000000..0bf5ee53ee --- /dev/null +++ b/test/1958-transform-try-jit/info.txt @@ -0,0 +1,5 @@ +Tests that JVMTI transformation seems to work even when we try to get a method +inlined by the jit. + +Note this test deliberately avoids any internal libart calls so it can be +included in CTS. diff --git a/test/1958-transform-try-jit/run b/test/1958-transform-try-jit/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/1958-transform-try-jit/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/1958-transform-try-jit/src/Main.java b/test/1958-transform-try-jit/src/Main.java new file mode 100644 index 0000000000..7f45c204fe --- /dev/null +++ b/test/1958-transform-try-jit/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1958.run(); + } +} diff --git a/test/1958-transform-try-jit/src/art/Redefinition.java b/test/1958-transform-try-jit/src/art/Redefinition.java new file mode 100644 index 0000000000..56d2938a01 --- /dev/null +++ b/test/1958-transform-try-jit/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2017 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. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class<?>... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/1958-transform-try-jit/src/art/Test1958.java b/test/1958-transform-try-jit/src/art/Test1958.java new file mode 100644 index 0000000000..100daa10ef --- /dev/null +++ b/test/1958-transform-try-jit/src/art/Test1958.java @@ -0,0 +1,106 @@ +/* + * 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. + */ + +package art; + +import java.lang.reflect.Method; +import java.util.Base64; + +public class Test1958 { + static class Runner { + public static String doSayHi() { + return sayHiHelper(true); + } + + public static String dontSayHi() { + return sayHiHelper(false); + } + + // We are trying to get the definition of Transform.sayHi inlined into this function. + public static String sayHiHelper(boolean sayHi) { + if (sayHi) { + return Transform.sayHi(); + } else { + return "NOPE!"; + } + } + } + + static class Transform { + public static String sayHi() { + // Use lower 'h' to make sure the string will have a different string id + // than the transformation (the transformation code is the same except + // the actual printed String, which was making the test inacurately passing + // in JIT mode when loading the string from the dex cache, as the string ids + // of the two different strings were the same). + // We know the string ids will be different because lexicographically: + // "Goodbye" < "LTransform;" < "hello". + return "hello"; + } + } + + /** + * base64 encoded class/dex file for + * static class Transform { + * public static String sayHi() { + * return "Goodbye"; + * } + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADUAFQoABAANCAAOBwAQBwATAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" + + "ZXJUYWJsZQEABXNheUhpAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQANVGVz" + + "dDE5NTguamF2YQwABQAGAQAHR29vZGJ5ZQcAFAEAFmFydC9UZXN0MTk1OCRUcmFuc2Zvcm0BAAlU" + + "cmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAMYXJ0L1Rlc3QxOTU4" + + "ACAAAwAEAAAAAAACAAAABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAAAQAIAAAABgABAAAABgAJ" + + "AAkACgABAAcAAAAbAAEAAAAAAAMSArAAAAABAAgAAAAGAAEAAAAIAAIACwAAAAIADAASAAAACgAB" + + "AAMADwARAAg="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQCRmEaLPLzpKe+CHcDM8YhIJCPWwcFR5yegAwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAR" + + "AAAAcAAAAAcAAAC0AAAAAgAAANAAAAAAAAAAAAAAAAMAAADoAAAAAQAAAAABAACAAgAAIAEAAFgB" + + "AABgAQAAaQEAAGwBAACGAQAAlgEAALoBAADaAQAA7gEAAAICAAARAgAAHAIAAB8CAAAsAgAAMgIA" + + "ADkCAABAAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACwAAAAIAAAAFAAAAAAAAAAsAAAAGAAAA" + + "AAAAAAAAAQAAAAAAAAAAAA4AAAAEAAEAAAAAAAAAAAAAAAAABAAAAAAAAAAJAAAA5AIAAMYCAAAA" + + "AAAAAQAAAAAAAABUAQAAAwAAABoAAQARAAAAAQABAAEAAABQAQAABAAAAHAQAgAAAA4ABgAOAAgA" + + "DgAGPGluaXQ+AAdHb29kYnllAAFMABhMYXJ0L1Rlc3QxOTU4JFRyYW5zZm9ybTsADkxhcnQvVGVz" + + "dDE5NTg7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" + + "YXRpb24vSW5uZXJDbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" + + "AA1UZXN0MTk1OC5qYXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAARuYW1lAAVzYXlIaQAF" + + "dmFsdWUAdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0x" + + "IjoiNTFjYWNlMWFiZGQwOGIzMjBmMjVmYjgxMTZjMjQzMmIwMmYwOTI5NSIsInZlcnNpb24iOiIx" + + "LjQuNS1kZXYifQACAgEPGAECAwIMBAgNFwoAAAIAAICABLgCAQmgAgAAAAACAAAAtwIAAL0CAADY" + + "AgAAAAAAAAAAAAAAAAAADgAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMA" + + "AAACAAAA0AAAAAUAAAADAAAA6AAAAAYAAAABAAAAAAEAAAEgAAACAAAAIAEAAAMgAAACAAAAUAEA" + + "AAIgAAARAAAAWAEAAAQgAAACAAAAtwIAAAAgAAABAAAAxgIAAAMQAAACAAAA1AIAAAYgAAABAAAA" + + "5AIAAAAQAAABAAAA9AIAAA=="); + + public static void run() throws Exception { + Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE); + Method doSayHi = Runner.class.getDeclaredMethod("doSayHi"); + Method dontSayHi = Runner.class.getDeclaredMethod("dontSayHi"); + // Run the method enough times that the jit thinks it's interesting (default 10000). + for (int i = 0; i < 20000; i++) { + doSayHi.invoke(null); + dontSayHi.invoke(null); + } + // Sleep for 10 seconds to let the jit finish any work it's doing. + Thread.sleep(10 * 1000); + // Check what we get right now. + System.out.println("Before redefinition: " + doSayHi.invoke(null)); + Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); + System.out.println("After redefinition: " + doSayHi.invoke(null)); + } +} diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java index f83c924de9..f39b5e26a0 100644 --- a/test/527-checker-array-access-split/src/Main.java +++ b/test/527-checker-array-access-split/src/Main.java @@ -572,6 +572,75 @@ public class Main { buf1[end] = 'n'; } + // + // Check that IntermediateAddress can be shared for object ArrayGets. + // + /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (before) + /// CHECK: <<Parameter:l\d+>> ParameterValue + /// CHECK: <<Array:l\d+>> NullCheck [<<Parameter>>] + /// CHECK: ArrayGet [<<Array>>,{{i\d+}}] + /// CHECK: ArrayGet [<<Array>>,{{i\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + /// CHECK: ArrayGet [<<Array>>,{{i\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + + /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (after) + /// CHECK: <<Parameter:l\d+>> ParameterValue + /// CHECK: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NullCheck [<<Parameter>>] + /// CHECK: <<IntAddr1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}] + /// CHECK: <<IntAddr2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArrayGet [<<IntAddr2>>,{{i\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + /// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + // + /// CHECK-NOT: IntermediateAddress + + /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) GVN$after_arch (after) + /// CHECK: <<Parameter:l\d+>> ParameterValue + /// CHECK: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NullCheck [<<Parameter>>] + /// CHECK: <<IntAddr1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}] + /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + /// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] + // + /// CHECK-NOT: IntermediateAddress + public final static int checkObjectArrayGet(int index, Integer[] a, Integer[] b) { + Integer five = Integer.valueOf(5); + int tmp1 = a[index]; + tmp1 += a[index + 1]; + a[index + 1] = five; + tmp1 += a[index + 2]; + a[index + 2] = five; + a[index + 3] = five; + return tmp1; + } + + /// CHECK-START-ARM64: int Main.testIntAddressObjDisasm(java.lang.Integer[], int) disassembly (after) + /// CHECK: <<IntAddr:i\d+>> IntermediateAddress + /// CHECK: add w<<AddrReg:\d+>>, {{w\d+}}, #0xc + /// CHECK: ArrayGet [<<IntAddr>>,{{i\d+}}] + /// CHECK: ldr {{w\d+}}, [x<<AddrReg>>, x{{\d+}}, lsl #2] + /// CHECK: ArrayGet [<<IntAddr>>,{{i\d+}}] + /// CHECK: ldr {{w\d+}}, [x<<AddrReg>>, x{{\d+}}, lsl #2] + + /// CHECK-START-ARM64: int Main.testIntAddressObjDisasm(java.lang.Integer[], int) disassembly (after) + /// CHECK: add {{w\d+}}, {{w\d+}}, #0xc + /// CHECK-NOT: add {{w\d+}}, {{w\d+}}, #0xc + private int testIntAddressObjDisasm(Integer[] obj, int x) { + return obj[x] + obj[x + 1]; + } + public final static int ARRAY_SIZE = 128; public static void main(String[] args) { diff --git a/test/580-crc32/expected.txt b/test/580-crc32/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/580-crc32/expected.txt diff --git a/test/580-crc32/info.txt b/test/580-crc32/info.txt new file mode 100644 index 0000000000..24f31e0e1b --- /dev/null +++ b/test/580-crc32/info.txt @@ -0,0 +1 @@ +This test case is used to test java.util.zip.CRC32. diff --git a/test/580-crc32/src/Main.java b/test/580-crc32/src/Main.java new file mode 100644 index 0000000000..7fc1273600 --- /dev/null +++ b/test/580-crc32/src/Main.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 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.util.zip.CRC32; + +/** + * The ART compiler can use intrinsics for the java.util.zip.CRC32 method: + * private native static int update(int crc, int b) + * + * As the method is private it is not possible to check the use of intrinsics + * for it directly. + * The tests check that correct checksums are produced. + */ +public class Main { + private static CRC32 crc32 = new CRC32(); + + public Main() { + } + + public static long TestInt(int value) { + crc32.reset(); + crc32.update(value); + return crc32.getValue(); + } + + public static long TestInt(int... values) { + crc32.reset(); + for (int value : values) { + crc32.update(value); + } + return crc32.getValue(); + } + + public static void assertEqual(long expected, long actual) { + if (expected != actual) { + throw new Error("Expected: " + expected + ", found: " + actual); + } + } + + public static void main(String args[]) { + // public void update(int b) + // + // Tests for checksums of the byte 0x0 + assertEqual(0xD202EF8DL, TestInt(0x0)); + assertEqual(0xD202EF8DL, TestInt(0x0100)); + assertEqual(0xD202EF8DL, TestInt(0x010000)); + assertEqual(0xD202EF8DL, TestInt(0x01000000)); + assertEqual(0xD202EF8DL, TestInt(0xff00)); + assertEqual(0xD202EF8DL, TestInt(0xffff00)); + assertEqual(0xD202EF8DL, TestInt(0xffffff00)); + assertEqual(0xD202EF8DL, TestInt(0x1200)); + assertEqual(0xD202EF8DL, TestInt(0x123400)); + assertEqual(0xD202EF8DL, TestInt(0x12345600)); + assertEqual(0xD202EF8DL, TestInt(Integer.MIN_VALUE)); + + // Tests for checksums of the byte 0x1 + assertEqual(0xA505DF1BL, TestInt(0x1)); + assertEqual(0xA505DF1BL, TestInt(0x0101)); + assertEqual(0xA505DF1BL, TestInt(0x010001)); + assertEqual(0xA505DF1BL, TestInt(0x01000001)); + assertEqual(0xA505DF1BL, TestInt(0xff01)); + assertEqual(0xA505DF1BL, TestInt(0xffff01)); + assertEqual(0xA505DF1BL, TestInt(0xffffff01)); + assertEqual(0xA505DF1BL, TestInt(0x1201)); + assertEqual(0xA505DF1BL, TestInt(0x123401)); + assertEqual(0xA505DF1BL, TestInt(0x12345601)); + + // Tests for checksums of the byte 0x0f + assertEqual(0x42BDF21CL, TestInt(0x0f)); + assertEqual(0x42BDF21CL, TestInt(0x010f)); + assertEqual(0x42BDF21CL, TestInt(0x01000f)); + assertEqual(0x42BDF21CL, TestInt(0x0100000f)); + assertEqual(0x42BDF21CL, TestInt(0xff0f)); + assertEqual(0x42BDF21CL, TestInt(0xffff0f)); + assertEqual(0x42BDF21CL, TestInt(0xffffff0f)); + assertEqual(0x42BDF21CL, TestInt(0x120f)); + assertEqual(0x42BDF21CL, TestInt(0x12340f)); + assertEqual(0x42BDF21CL, TestInt(0x1234560f)); + + // Tests for checksums of the byte 0xff + assertEqual(0xFF000000L, TestInt(0x00ff)); + assertEqual(0xFF000000L, TestInt(0x01ff)); + assertEqual(0xFF000000L, TestInt(0x0100ff)); + assertEqual(0xFF000000L, TestInt(0x010000ff)); + assertEqual(0xFF000000L, TestInt(0x0000ffff)); + assertEqual(0xFF000000L, TestInt(0x00ffffff)); + assertEqual(0xFF000000L, TestInt(0xffffffff)); + assertEqual(0xFF000000L, TestInt(0x12ff)); + assertEqual(0xFF000000L, TestInt(0x1234ff)); + assertEqual(0xFF000000L, TestInt(0x123456ff)); + assertEqual(0xFF000000L, TestInt(Integer.MAX_VALUE)); + + // Tests for sequences + assertEqual(0xFF41D912L, TestInt(0, 0, 0)); + assertEqual(0xFF41D912L, TestInt(0x0100, 0x010000, 0x01000000)); + assertEqual(0xFF41D912L, TestInt(0xff00, 0xffff00, 0xffffff00)); + assertEqual(0xFF41D912L, TestInt(0x1200, 0x123400, 0x12345600)); + + assertEqual(0x909FB2F2L, TestInt(1, 1, 1)); + assertEqual(0x909FB2F2L, TestInt(0x0101, 0x010001, 0x01000001)); + assertEqual(0x909FB2F2L, TestInt(0xff01, 0xffff01, 0xffffff01)); + assertEqual(0x909FB2F2L, TestInt(0x1201, 0x123401, 0x12345601)); + + assertEqual(0xE33A9F71L, TestInt(0x0f, 0x0f, 0x0f)); + assertEqual(0xE33A9F71L, TestInt(0x010f, 0x01000f, 0x0100000f)); + assertEqual(0xE33A9F71L, TestInt(0xff0f, 0xffff0f, 0xffffff0f)); + assertEqual(0xE33A9F71L, TestInt(0x120f, 0x12340f, 0x1234560f)); + + assertEqual(0xFFFFFF00L, TestInt(0x0ff, 0x0ff, 0x0ff)); + assertEqual(0xFFFFFF00L, TestInt(0x01ff, 0x0100ff, 0x010000ff)); + assertEqual(0xFFFFFF00L, TestInt(0x00ffff, 0x00ffffff, 0xffffffff)); + assertEqual(0xFFFFFF00L, TestInt(0x12ff, 0x1234ff, 0x123456ff)); + + assertEqual(0xB6CC4292L, TestInt(0x01, 0x02)); + + assertEqual(0xB2DE047CL, TestInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE)); + } +} diff --git a/test/624-checker-stringops/smali/Smali.smali b/test/624-checker-stringops/smali/Smali.smali index 8600a0a065..f8b92759c7 100644 --- a/test/624-checker-stringops/smali/Smali.smali +++ b/test/624-checker-stringops/smali/Smali.smali @@ -34,7 +34,7 @@ invoke-virtual {v0, v1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer; move-result-object v1 - const-string v2, "x" + const-string v2, "y" invoke-virtual {v1, v2}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer; move-result-object v1 @@ -70,7 +70,7 @@ invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; move-result-object v1 - const-string v2, "x" + const-string v2, "y" invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; move-result-object v1 diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java index 3aa6e563fc..f52d81a053 100644 --- a/test/624-checker-stringops/src/Main.java +++ b/test/624-checker-stringops/src/Main.java @@ -120,7 +120,7 @@ public class Main { /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBufferLength static int bufferLen2() { StringBuffer s = new StringBuffer(); - return s.append("x").append("x").length(); + return s.append("x").append("y").length(); } static int bufferLen2Smali() throws Exception { @@ -150,7 +150,7 @@ public class Main { /// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBuilderLength static int builderLen2() { StringBuilder s = new StringBuilder(); - return s.append("x").append("x").length(); + return s.append("x").append("y").length(); } static int builderLen2Smali() throws Exception { diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index b0a400ab75..8dd49aaa9b 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -21,7 +21,7 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - doTest ()V 33 25 + doTest ()V 31 25 run ()V 0 25 --------- print (Ljava/lang/Thread;II)V 0 38 @@ -41,7 +41,7 @@ From top baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - doTest ()V 37 26 + doTest ()V 35 26 run ()V 0 25 --------- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2 @@ -62,7 +62,7 @@ From bottom baz (IIILart/ControlData;)Ljava/lang/Object; 8 34 bar (IIILart/ControlData;)J 0 26 foo (IIILart/ControlData;)I 0 21 - doTest ()V 60 32 + doTest ()V 58 32 run ()V 0 25 --------- bar (IIILart/ControlData;)J 0 26 @@ -388,7 +388,7 @@ Signal Catcher Test911 getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 printAll (I)V 0 75 - doTest ()V 120 59 + doTest ()V 118 59 run ()V 24 37 --------- @@ -643,7 +643,7 @@ Signal Catcher Test911 getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 printAll (I)V 0 75 - doTest ()V 125 61 + doTest ()V 123 61 run ()V 24 37 --------- @@ -675,7 +675,7 @@ ThreadListTraces Thread 8 Test911 getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 68 - doTest ()V 110 54 + doTest ()V 108 54 run ()V 32 41 --------- @@ -732,7 +732,7 @@ ThreadListTraces Thread 8 Test911 getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 68 - doTest ()V 115 56 + doTest ()V 113 56 run ()V 32 41 --------- @@ -857,7 +857,7 @@ ThreadListTraces Thread 8 4 JVMTI_ERROR_ILLEGAL_ARGUMENT [public static native java.lang.Object[] art.Frames.getFrameLocation(java.lang.Thread,int), ffffffff] -[public static void art.Frames.doTestSameThread(), 40] +[public static void art.Frames.doTestSameThread(), 3e] [public static void art.Frames.doTest() throws java.lang.Exception, 0] [public void art.Test911$1.run(), 28] JVMTI_ERROR_NO_MORE_FRAMES diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt index 065b854a6a..1bd56d1a53 100644 --- a/test/913-heaps/expected.txt +++ b/test/913-heaps/expected.txt @@ -1,9 +1,9 @@ --- true true -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780000, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780004, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780001, length=-1] @@ -44,14 +44,14 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780005, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780009, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780006, length=-1] @@ -90,18 +90,18 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 5@1002 --(field@9)--> 6@1000 [size=16, length=-1] 6@1000 --(class)--> 1000@0 [size=123456780005, length=-1] --- -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 3@1001 --(class)--> 1001@0 [size=123456780011, length=-1] --- -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 3@1001 --(class)--> 1001@0 [size=123456780016, length=-1] --- -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 1001@0 --(superclass)--> 1000@0 [size=123456780020, length=-1] 3@1001 --(class)--> 1001@0 [size=123456780021, length=-1] @@ -110,14 +110,14 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] --- 1001@0 --(superclass)--> 1000@0 [size=123456780025, length=-1] 3@1001 --(class)--> 1001@0 [size=123456780026, length=-1] @@ -198,10 +198,10 @@ root@root --(thread)--> 1@1000 [size=16, length=-1] --- --- ---- untagged objects -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780050, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780054, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780051, length=-1] @@ -242,14 +242,14 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] --- root@root --(jni-global)--> 1@1000 [size=16, length=-1] root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1] -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1] root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] root@root --(thread)--> 1@1000 [size=16, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780055, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780059, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780056, length=-1] @@ -289,9 +289,9 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 6@1000 --(class)--> 1000@0 [size=123456780055, length=-1] --- ---- tagged classes -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780064, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780061, length=-1] @@ -316,9 +316,9 @@ root@root --(thread)--> 3000@0 [size=132, length=-1] 5@1002 --(field@8)--> 500@0 [size=20, length=2] 6@1000 --(class)--> 1000@0 [size=123456780060, length=-1] --- -root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1] -root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1] -root@root --(thread)--> 3000@0 [size=132, length=-1] +root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1] +root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1] +root@root --(thread)--> 3000@0 [size=124, length=-1] 1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1] 1002@0 --(interface)--> 2001@0 [size=123456780069, length=-1] 1002@0 --(superclass)--> 1001@0 [size=123456780066, length=-1] diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 64c1d4f1b8..148aea48ae 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -59,6 +59,7 @@ ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += $(TARGET_OUT_JAVA_LIBRARIES)/conscrypt- ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \ $(ART_HOST_EXECUTABLES) \ $(HOST_OUT_EXECUTABLES)/hprof-conv \ + $(HOST_OUT_EXECUTABLES)/timeout_dumper \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagentd) \ $(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtistress) \ diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 900b1d7759..6488d241e8 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -31,6 +31,8 @@ GDB_ARGS="" GDB_SERVER="gdbserver" HAVE_IMAGE="y" HOST="n" +BIONIC="n" +CREATE_ANDROID_ROOT="n" INTERPRETER="n" JIT="n" INVOKE_WITH="" @@ -209,6 +211,13 @@ while true; do HOST="y" ANDROID_ROOT="$ANDROID_HOST_OUT" shift + elif [ "x$1" = "x--bionic" ]; then + BIONIC="y" + # We need to create an ANDROID_ROOT because currently we cannot create + # the frameworks/libcore with linux_bionic so we need to use the normal + # host ones which are in a different location. + CREATE_ANDROID_ROOT="y" + shift elif [ "x$1" = "x--no-prebuild" ]; then PREBUILD="n" shift @@ -374,6 +383,10 @@ if [ "$USE_JVM" = "n" ]; then done fi +if [ "$CREATE_ANDROID_ROOT" = "y" ]; then + ANDROID_ROOT=$DEX_LOCATION/android-root +fi + if [ "x$1" = "x" ] ; then MAIN="Main" else @@ -644,6 +657,15 @@ if [ "$HOST" = "n" ]; then fi fi +if [ "$BIONIC" = "y" ]; then + # This is the location that soong drops linux_bionic builds. Despite being + # called linux_bionic-x86 the build is actually amd64 (x86_64) only. + if [ ! -e "$OUT_DIR/soong/host/linux_bionic-x86" ]; then + echo "linux_bionic-x86 target doesn't seem to have been built!" >&2 + exit 1 + fi +fi + # Prevent test from silently falling back to interpreter in no-prebuild mode. This happens # when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking # full path to dex, stripping leading '/', appending '@classes.vdex' and changing every @@ -671,6 +693,21 @@ dm_cmdline="true" mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" sync_cmdline="true" +linkroot_cmdline="true" +linkroot_overlay_cmdline="true" + +linkdirs() { + find "$1" -maxdepth 1 -mindepth 1 -type d | xargs -i ln -sf '{}' "$2" +} + +if [ "$CREATE_ANDROID_ROOT" = "y" ]; then + mkdir_locations="${mkdir_locations} ${ANDROID_ROOT}" + linkroot_cmdline="linkdirs ${ANDROID_HOST_OUT} ${ANDROID_ROOT}" + if [ "${BIONIC}" = "y" ]; then + # TODO Make this overlay more generic. + linkroot_overlay_cmdline="linkdirs $OUT_DIR/soong/host/linux_bionic-x86 ${ANDROID_ROOT}" + fi +fi # PROFILE takes precedence over RANDOM_PROFILE, since PROFILE tests require a # specific profile to run properly. @@ -942,14 +979,15 @@ else # Note: We first send SIGRTMIN+2 (usually 36) to ART, which will induce a full thread dump # before abort. However, dumping threads might deadlock, so we also use the "-k" # option to definitely kill the child. - cmdline="timeout -k 120s -s SIGRTMIN+2 ${TIME_OUT_VALUE}s $cmdline" + cmdline="timeout -k 120s -s SIGRTMIN+2 ${TIME_OUT_VALUE}s timeout_dumper $cmdline" fi if [ "$DEV_MODE" = "y" ]; then for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do echo EXPORT $var=${!var} done - echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline" + echo "$(declare -f linkdirs)" + echo "mkdir -p ${mkdir_locations} && $linkroot_cmdline && $linkroot_overlay_cmdline && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline" fi cd $ANDROID_BUILD_TOP @@ -963,6 +1001,8 @@ else export ASAN_OPTIONS=$RUN_TEST_ASAN_OPTIONS mkdir -p ${mkdir_locations} || exit 1 + $linkroot_cmdline || { echo "create symlink android-root failed." >&2 ; exit 2; } + $linkroot_overlay_cmdline || { echo "overlay android-root failed." >&2 ; exit 2; } $profman_cmdline || { echo "Profman failed." >&2 ; exit 2; } $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } $dm_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } diff --git a/test/run-test b/test/run-test index 229e2019dd..23631527e0 100755 --- a/test/run-test +++ b/test/run-test @@ -88,6 +88,8 @@ if [ -z "$ANDROID_HOST_OUT" ]; then export ANDROID_HOST_OUT=${OUT_DIR}/host/linux-x86 fi +host_lib_root=${ANDROID_HOST_OUT} + # Allow changing DESUGAR script to something else, or to disable it with DESUGAR=false. if [ -z "$DESUGAR" ]; then export DESUGAR="$ANDROID_BUILD_TOP/art/tools/desugar.sh" @@ -385,6 +387,14 @@ while true; do run_args="${run_args} --64" suffix64="64" shift + elif [ "x$1" = "x--bionic" ]; then + # soong linux_bionic builds are 64bit only. + run_args="${run_args} --bionic --host --64" + suffix64="64" + target_mode="no" + DEX_LOCATION=$tmp_dir + host_lib_root=$OUT_DIR/soong/host/linux_bionic-x86 + shift elif [ "x$1" = "x--trace" ]; then trace="true" shift @@ -584,7 +594,7 @@ elif [ "$runtime" = "art" ]; then if [ "$target_mode" = "no" ]; then guess_host_arch_name run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}.art" - run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}" + run_args="${run_args} --runtime-option -Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}" else guess_target_arch_name run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}" @@ -711,6 +721,7 @@ if [ "$usage" = "yes" ]; then echo " --output-path [path] Location where to store the build" \ "files." echo " --64 Run the test in 64-bit mode" + echo " --bionic Use the (host, 64-bit only) linux_bionic libc runtime" echo " --trace Run with method tracing" echo " --strace Run with syscall tracing from strace." echo " --stream Run method tracing in streaming mode (requires --trace)" diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh new file mode 100755 index 0000000000..dba2d50e38 --- /dev/null +++ b/tools/build_linux_bionic_tests.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. + + +if [[ -z $ANDROID_BUILD_TOP ]]; then + pushd . +else + pushd $ANDROID_BUILD_TOP +fi + +if [ ! -d art ]; then + echo "Script needs to be run at the root of the android tree" + exit 1 +fi + +source build/envsetup.sh >&/dev/null # for get_build_var + +out_dir=$(get_build_var OUT_DIR) + +# TODO(b/31559095) Figure out a better way to do this. +# +# There is no good way to force soong to generate host-bionic builds currently +# so this is a hacky workaround. + +# First build all the targets still in .mk files (also build normal glibc host +# targets so we know what's needed to run the tests). +build/soong/soong_ui.bash --make-mode "$@" test-art-host-run-test-dependencies build-art-host-tests +if [ $? != 0 ]; then + exit 1 +fi + +tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX) + +echo "Saving soong.variables to " $tmp_soong_var +cat $out_dir/soong/soong.variables > ${tmp_soong_var} +python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables +import json +import sys +x = json.load(open(sys.argv[1])) +x['Allow_missing_dependencies'] = True +x['HostArch'] = 'x86_64' +x['CrossHost'] = 'linux_bionic' +x['CrossHostArch'] = 'x86_64' +if 'CrossHostSecondaryArch' in x: + del x['CrossHostSecondaryArch'] +json.dump(x, open(sys.argv[2], mode='w')) +END +if [ $? != 0 ]; then + mv $tmp_soong_var $out_dir/soong/soong.variables + exit 2 +fi + +soong_out=$out_dir/soong/host/linux_bionic-x86 +declare -a bionic_targets +# These are the binaries actually used in tests. Since some of the files are +# java targets or 32 bit we cannot just do the same find for the bin files. +# +# We look at what the earlier build generated to figure out what to ask soong to +# build since we cannot use the .mk defined phony targets. +bionic_targets=( + $soong_out/bin/dalvikvm + $soong_out/bin/dalvikvm64 + $soong_out/bin/dex2oat + $soong_out/bin/dex2oatd + $soong_out/bin/profman + $soong_out/bin/profmand + $soong_out/bin/hiddenapi + $soong_out/bin/hprof-conv + $(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g") + $(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")) + +echo building ${bionic_targets[*]} + +build/soong/soong_ui.bash --make-mode --skip-make "$@" ${bionic_targets[*]} +ret=$? + +mv $tmp_soong_var $out_dir/soong/soong.variables + +# Having built with host-bionic confuses soong somewhat by making it think the +# linux_bionic targets are needed for art phony targets like +# test-art-host-run-test-dependencies. To work around this blow away all +# ninja files in OUT_DIR. The build system is smart enough to not need to +# rebuild stuff so this should be fine. +rm -f $OUT_DIR/*.ninja $OUT_DIR/soong/*.ninja + +popd + +exit $ret diff --git a/tools/timeout_dumper/Android.bp b/tools/timeout_dumper/Android.bp new file mode 100644 index 0000000000..dfd54421fd --- /dev/null +++ b/tools/timeout_dumper/Android.bp @@ -0,0 +1,39 @@ +// +// Copyright (C) 2018 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_cc_binary { + name: "timeout_dumper", + + host_supported: true, + target: { + darwin: { + enabled: false, + }, + }, + device_supported: false, + + defaults: ["art_defaults"], + + srcs: ["timeout_dumper.cc"], + + shared_libs: [ + "libbacktrace", + "libbase", + ], + sanitize: { + address: true, + }, +} diff --git a/tools/timeout_dumper/timeout_dumper.cc b/tools/timeout_dumper/timeout_dumper.cc new file mode 100644 index 0000000000..30e194ceda --- /dev/null +++ b/tools/timeout_dumper/timeout_dumper.cc @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2018 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 <dirent.h> +#include <poll.h> +#include <sys/prctl.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <csignal> +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <thread> +#include <memory> +#include <set> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <backtrace/Backtrace.h> +#include <backtrace/BacktraceMap.h> + +namespace art { +namespace { + +using android::base::StringPrintf; +using android::base::unique_fd; + +constexpr bool kUseAddr2line = true; + +namespace timeout_signal { + +class SignalSet { + public: + SignalSet() { + if (sigemptyset(&set_) == -1) { + PLOG(FATAL) << "sigemptyset failed"; + } + } + + void Add(int signal) { + if (sigaddset(&set_, signal) == -1) { + PLOG(FATAL) << "sigaddset " << signal << " failed"; + } + } + + void Block() { + if (pthread_sigmask(SIG_BLOCK, &set_, nullptr) != 0) { + PLOG(FATAL) << "pthread_sigmask failed"; + } + } + + int Wait() { + // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures. + int signal_number; + int rc = TEMP_FAILURE_RETRY(sigwait(&set_, &signal_number)); + if (rc != 0) { + PLOG(FATAL) << "sigwait failed"; + } + return signal_number; + } + + private: + sigset_t set_; +}; + +int GetTimeoutSignal() { + return SIGRTMIN + 2; +} + +} // namespace timeout_signal + +namespace addr2line { + +constexpr const char* kAddr2linePath = + "/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/bin/x86_64-linux-addr2line"; + +std::unique_ptr<std::string> FindAddr2line() { + const char* env_value = getenv("ANDROID_BUILD_TOP"); + if (env_value != nullptr) { + std::string path = std::string(env_value) + kAddr2linePath; + if (access(path.c_str(), X_OK) == 0) { + return std::make_unique<std::string>(path); + } + } + + std::string path = std::string(".") + kAddr2linePath; + if (access(path.c_str(), X_OK) == 0) { + return std::make_unique<std::string>(path); + } + + constexpr const char* kHostAddr2line = "/usr/bin/addr2line"; + if (access(kHostAddr2line, F_OK) == 0) { + return std::make_unique<std::string>(kHostAddr2line); + } + + return nullptr; +} + +// The state of an open pipe to addr2line. In "server" mode, addr2line takes input on stdin +// and prints the result to stdout. This struct keeps the state of the open connection. +struct Addr2linePipe { + Addr2linePipe(int in_fd, int out_fd, const std::string& file_name, pid_t pid) + : in(in_fd), out(out_fd), file(file_name), child_pid(pid), odd(true) {} + + ~Addr2linePipe() { + kill(child_pid, SIGKILL); + } + + unique_fd in; // The file descriptor that is connected to the output of addr2line. + unique_fd out; // The file descriptor that is connected to the input of addr2line. + + const std::string file; // The file addr2line is working on, so that we know when to close + // and restart. + const pid_t child_pid; // The pid of the child, which we should kill when we're done. + bool odd; // Print state for indentation of lines. +}; + +std::unique_ptr<Addr2linePipe> Connect(const std::string& name, const char* args[]) { + int caller_to_addr2line[2]; + int addr2line_to_caller[2]; + + if (pipe(caller_to_addr2line) == -1) { + return nullptr; + } + if (pipe(addr2line_to_caller) == -1) { + close(caller_to_addr2line[0]); + close(caller_to_addr2line[1]); + return nullptr; + } + + pid_t pid = fork(); + if (pid == -1) { + close(caller_to_addr2line[0]); + close(caller_to_addr2line[1]); + close(addr2line_to_caller[0]); + close(addr2line_to_caller[1]); + return nullptr; + } + + if (pid == 0) { + dup2(caller_to_addr2line[0], STDIN_FILENO); + dup2(addr2line_to_caller[1], STDOUT_FILENO); + + close(caller_to_addr2line[0]); + close(caller_to_addr2line[1]); + close(addr2line_to_caller[0]); + close(addr2line_to_caller[1]); + + execv(args[0], const_cast<char* const*>(args)); + exit(1); + } else { + close(caller_to_addr2line[0]); + close(addr2line_to_caller[1]); + return std::make_unique<Addr2linePipe>(addr2line_to_caller[0], + caller_to_addr2line[1], + name, + pid); + } +} + +void WritePrefix(std::ostream& os, const char* prefix, bool odd) { + if (prefix != nullptr) { + os << prefix; + } + os << " "; + if (!odd) { + os << " "; + } +} + +void Drain(size_t expected, + const char* prefix, + std::unique_ptr<Addr2linePipe>* pipe /* inout */, + std::ostream& os) { + DCHECK(pipe != nullptr); + DCHECK(pipe->get() != nullptr); + int in = pipe->get()->in.get(); + DCHECK_GE(in, 0); + + bool prefix_written = false; + + for (;;) { + constexpr uint32_t kWaitTimeExpectedMilli = 500; + constexpr uint32_t kWaitTimeUnexpectedMilli = 50; + + int timeout = expected > 0 ? kWaitTimeExpectedMilli : kWaitTimeUnexpectedMilli; + struct pollfd read_fd{in, POLLIN, 0}; + int retval = TEMP_FAILURE_RETRY(poll(&read_fd, 1, timeout)); + if (retval == -1) { + // An error occurred. + pipe->reset(); + return; + } + + if (retval == 0) { + // Timeout. + return; + } + + if (!(read_fd.revents & POLLIN)) { + // addr2line call exited. + pipe->reset(); + return; + } + + constexpr size_t kMaxBuffer = 128; // Relatively small buffer. Should be OK as we're on an + // alt stack, but just to be sure... + char buffer[kMaxBuffer]; + memset(buffer, 0, kMaxBuffer); + int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1)); + if (bytes_read <= 0) { + // This should not really happen... + pipe->reset(); + return; + } + buffer[bytes_read] = '\0'; + + char* tmp = buffer; + while (*tmp != 0) { + if (!prefix_written) { + WritePrefix(os, prefix, (*pipe)->odd); + prefix_written = true; + } + char* new_line = strchr(tmp, '\n'); + if (new_line == nullptr) { + os << tmp; + + break; + } else { + os << std::string(tmp, new_line - tmp + 1); + + tmp = new_line + 1; + prefix_written = false; + (*pipe)->odd = !(*pipe)->odd; + + if (expected > 0) { + expected--; + } + } + } + } +} + +void Addr2line(const std::string& addr2line, + const std::string& map_src, + uintptr_t offset, + std::ostream& os, + const char* prefix, + std::unique_ptr<Addr2linePipe>* pipe /* inout */) { + DCHECK(pipe != nullptr); + + if (map_src == "[vdso]" || android::base::EndsWith(map_src, ".vdex")) { + // addr2line will not work on the vdso. + // vdex files are special frames injected for the interpreter + // so they don't have any line number information available. + return; + } + + if (*pipe == nullptr || (*pipe)->file != map_src) { + if (*pipe != nullptr) { + Drain(0, prefix, pipe, os); + } + pipe->reset(); // Close early. + + const char* args[] = { + addr2line.c_str(), + "--functions", + "--inlines", + "--demangle", + "-e", + map_src.c_str(), + nullptr + }; + *pipe = Connect(map_src, args); + } + + Addr2linePipe* pipe_ptr = pipe->get(); + if (pipe_ptr == nullptr) { + // Failed... + return; + } + + // Send the offset. + const std::string hex_offset = StringPrintf("%zx\n", offset); + + if (!android::base::WriteFully(pipe_ptr->out.get(), hex_offset.data(), hex_offset.length())) { + // Error. :-( + pipe->reset(); + return; + } + + // Now drain (expecting two lines). + Drain(2U, prefix, pipe, os); +} + +} // namespace addr2line + +namespace ptrace { + +std::set<pid_t> PtraceSiblings(pid_t pid) { + std::set<pid_t> ret; + std::string task_path = android::base::StringPrintf("/proc/%d/task", pid); + + std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path.c_str()), closedir); + + // Bail early if the task directory cannot be opened. + if (d == nullptr) { + PLOG(ERROR) << "Failed to scan task folder"; + return ret; + } + + struct dirent* de; + while ((de = readdir(d.get())) != nullptr) { + // Ignore "." and "..". + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { + continue; + } + + char* end; + pid_t tid = strtoul(de->d_name, &end, 10); + if (*end) { + continue; + } + + if (tid == pid) { + continue; + } + + if (::ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) { + PLOG(ERROR) << "Failed to attach to tid " << tid; + continue; + } + + ret.insert(tid); + } + return ret; +} + +} // namespace ptrace + +template <typename T> +bool WaitLoop(uint32_t max_wait_micros, const T& handler) { + constexpr uint32_t kWaitMicros = 10; + const size_t kMaxLoopCount = max_wait_micros / kWaitMicros; + + for (size_t loop_count = 1; loop_count <= kMaxLoopCount; ++loop_count) { + bool ret; + if (handler(&ret)) { + return ret; + } + usleep(kWaitMicros); + } + return false; +} + +bool WaitForMainSigStop(const std::atomic<bool>& saw_wif_stopped_for_main) { + auto handler = [&](bool* res) { + if (saw_wif_stopped_for_main) { + *res = true; + return true; + } + return false; + }; + constexpr uint32_t kMaxWaitMicros = 30 * 1000 * 1000; // 30s wait. + return WaitLoop(kMaxWaitMicros, handler); +} + +bool WaitForSigStopped(pid_t pid, uint32_t max_wait_micros) { + auto handler = [&](bool* res) { + int status; + pid_t rc = TEMP_FAILURE_RETRY(waitpid(pid, &status, WNOHANG)); + if (rc == -1) { + PLOG(ERROR) << "Failed to waitpid for " << pid; + *res = false; + return true; + } + if (rc == pid) { + if (!(WIFSTOPPED(status))) { + LOG(ERROR) << "Did not get expected stopped signal for " << pid; + *res = false; + } else { + *res = true; + } + return true; + } + return false; + }; + return WaitLoop(max_wait_micros, handler); +} + +#ifdef __LP64__ +constexpr bool kIs64Bit = true; +#else +constexpr bool kIs64Bit = false; +#endif + +void DumpThread(pid_t pid, + pid_t tid, + const std::string* addr2line_path, + const char* prefix, + BacktraceMap* map) { + // Use std::cerr to avoid the LOG prefix. + std::cerr << std::endl << "=== pid: " << pid << " tid: " << tid << " ===" << std::endl; + + constexpr uint32_t kMaxWaitMicros = 1000 * 1000; // 1s. + if (pid != tid && !WaitForSigStopped(tid, kMaxWaitMicros)) { + LOG(ERROR) << "Failed to wait for sigstop on " << tid; + } + + std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map)); + if (backtrace == nullptr) { + LOG(ERROR) << prefix << "(failed to create Backtrace for thread " << tid << ")"; + return; + } + backtrace->SetSkipFrames(0); + if (!backtrace->Unwind(0, nullptr)) { + LOG(ERROR) << prefix << "(backtrace::Unwind failed for thread " << tid + << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")"; + return; + } + if (backtrace->NumFrames() == 0) { + LOG(ERROR) << prefix << "(no native stack frames for thread " << tid << ")"; + return; + } + + std::unique_ptr<addr2line::Addr2linePipe> addr2line_state; + + for (Backtrace::const_iterator it = backtrace->begin(); + it != backtrace->end(); ++it) { + std::ostringstream oss; + oss << prefix << StringPrintf("#%02zu pc ", it->num); + bool try_addr2line = false; + if (!BacktraceMap::IsValid(it->map)) { + oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " ???" : "%08" PRIx64 " ???", it->pc); + } else { + oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " " : "%08" PRIx64 " ", it->rel_pc); + if (it->map.name.empty()) { + oss << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start); + } else { + oss << it->map.name; + } + if (it->map.offset != 0) { + oss << StringPrintf(" (offset %" PRIx64 ")", it->map.offset); + } + oss << " ("; + if (!it->func_name.empty()) { + oss << it->func_name; + if (it->func_offset != 0) { + oss << "+" << it->func_offset; + } + // Functions found using the gdb jit interface will be in an empty + // map that cannot be found using addr2line. + if (!it->map.name.empty()) { + try_addr2line = true; + } + } else { + oss << "???"; + } + oss << ")"; + } + std::cerr << oss.str() << std::endl; + if (try_addr2line && addr2line_path != nullptr) { + addr2line::Addr2line(*addr2line_path, + it->map.name, + it->rel_pc, + std::cerr, + prefix, + &addr2line_state); + } + } + + if (addr2line_state != nullptr) { + addr2line::Drain(0, prefix, &addr2line_state, std::cerr); + } +} + +void DumpProcess(pid_t forked_pid, const std::atomic<bool>& saw_wif_stopped_for_main) { + CHECK_EQ(0, ::ptrace(PTRACE_ATTACH, forked_pid, 0, 0)); + std::set<pid_t> tids = ptrace::PtraceSiblings(forked_pid); + tids.insert(forked_pid); + + // Check whether we have and should use addr2line. + std::unique_ptr<std::string> addr2line_path = addr2line::FindAddr2line(); + if (addr2line_path != nullptr) { + LOG(ERROR) << "Found addr2line at " << *addr2line_path; + } else { + LOG(ERROR) << "Did not find usable addr2line"; + } + bool use_addr2line = kUseAddr2line && addr2line_path != nullptr; + LOG(ERROR) << (use_addr2line ? "U" : "Not u") << "sing addr2line"; + + if (!WaitForMainSigStop(saw_wif_stopped_for_main)) { + LOG(ERROR) << "Did not receive SIGSTOP for pid " << forked_pid; + } + + std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(forked_pid)); + if (backtrace_map == nullptr) { + LOG(ERROR) << "Could not create BacktraceMap"; + return; + } + + for (pid_t tid : tids) { + DumpThread(forked_pid, + tid, + use_addr2line ? addr2line_path.get() : nullptr, + " ", + backtrace_map.get()); + } +} + +[[noreturn]] +void WaitMainLoop(pid_t forked_pid, std::atomic<bool>* saw_wif_stopped_for_main) { + for (;;) { + // Consider switching to waitid to not get woken up for WIFSTOPPED. + int status; + pid_t res = TEMP_FAILURE_RETRY(waitpid(forked_pid, &status, 0)); + if (res == -1) { + PLOG(FATAL) << "Failure during waitpid"; + __builtin_unreachable(); + } + + if (WIFEXITED(status)) { + _exit(WEXITSTATUS(status)); + __builtin_unreachable(); + } + if (WIFSIGNALED(status)) { + _exit(1); + __builtin_unreachable(); + } + if (WIFSTOPPED(status)) { + *saw_wif_stopped_for_main = true; + continue; + } + if (WIFCONTINUED(status)) { + continue; + } + + LOG(FATAL) << "Unknown status " << std::hex << status; + } +} + +[[noreturn]] +void SetupAndWait(pid_t forked_pid) { + timeout_signal::SignalSet signals; + signals.Add(timeout_signal::GetTimeoutSignal()); + signals.Block(); + + std::atomic<bool> saw_wif_stopped_for_main(false); + + std::thread signal_catcher([&]() { + signals.Block(); + int sig = signals.Wait(); + CHECK_EQ(sig, timeout_signal::GetTimeoutSignal()); + + DumpProcess(forked_pid, saw_wif_stopped_for_main); + + // Don't clean up. Just kill the child and exit. + kill(forked_pid, SIGKILL); + _exit(1); + }); + + WaitMainLoop(forked_pid, &saw_wif_stopped_for_main); +} + +} // namespace +} // namespace art + +int main(int argc ATTRIBUTE_UNUSED, char** argv) { + pid_t orig_ppid = getpid(); + + pid_t pid = fork(); + if (pid == 0) { + if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) { + _exit(1); + } + + if (getppid() != orig_ppid) { + _exit(2); + } + + execvp(argv[1], &argv[1]); + + _exit(3); + __builtin_unreachable(); + } + + art::SetupAndWait(pid); + __builtin_unreachable(); +} |